0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026:
0027: package com.sun.midp.pki;
0028:
0029: import java.io.IOException;
0030:
0031: import java.util.*;
0032:
0033: import javax.microedition.pki.*;
0034:
0035: import com.sun.midp.log.Logging;
0036: import com.sun.midp.log.LogChannels;
0037:
0038: import com.sun.midp.crypto.*;
0039:
0040: /**
0041: * This class implements methods for creating X.509 certificates and
0042: * accessing their attributes such as subject/issuer names, public keys
0043: * and validity information. Publicly visible methods methods are
0044: * modeled after those in the X509Certificate classes
0045: * from J2SE (standard edition) but there are some differences and
0046: * these are documented below. <P />
0047: * NOTE: For now, only X.509 certificates containing RSA public keys
0048: * and signed either using md2WithRSA, md5WithRSA, or sha-1WithRSA are
0049: * supported.
0050: * This version of the implementation is unable to parse certificates
0051: * containing DSA keys or signed using DSA. Certificates containing
0052: * RSA keys but signed using an unsupported algorithm
0053: * can be parsed but cannot be verified. Not all version 3 extensions are
0054: * supported (only subjectAltName, basicConstraints, keyUsage and
0055: * extendedKeyUsage are recognized) but if an unrecognized
0056: * extension is marked critical, an error notification is generated.
0057: * <P />
0058: */
0059: public class X509Certificate implements Certificate {
0060:
0061: /** Indicates a no error condition. */
0062: public static final byte NO_ERROR = 0;
0063:
0064: /**
0065: * Indicates that no information is available on
0066: * the pathLengthConstraint associated with this certificate
0067: * (this could happen if the certifiate is a v1 or v2 cert or
0068: * a v3 cert without basicConstraints or a non-CA v3 certificate).
0069: */
0070: public static final int MISSING_PATH_LENGTH_CONSTRAINT = -1;
0071: /** Indicates there is no limit to the server certificate chain length. */
0072: public static final int UNLIMITED_CERT_CHAIN_LENGTH = 65535;
0073:
0074: /** We expect issuer/subject names to fit within these many bytes. */
0075: private static final int MAX_NAME_LENGTH = 300;
0076:
0077: /** ASN ANY_STRING type used in certificate parsing (0x00). */
0078: private static final byte ANY_STRING_TYPE = 0x00; // our own impl
0079: // private static final byte BOOLEAN_TYPE = 0x01 ?????
0080: /** ASN INTEGER type used in certificate parsing (0x02). */
0081: private static final byte INTEGER_TYPE = 0x02;
0082: /** ASN BIT STRING type used in certificate parsing (0x03). */
0083: private static final byte BITSTRING_TYPE = 0x03;
0084: /** ASN OCTET STRING type used in certificate parsing (0x04). */
0085: private static final byte OCTETSTR_TYPE = 0x04;
0086: /** ASN OBJECT ID type used in certificate parsing (0x06). */
0087: private static final byte OID_TYPE = 0x06;
0088: /** ASN UTF8 STRING type used in certificate parsing (0x0c). */
0089: private static final byte UTF8STR_TYPE = 0x0c;
0090: /** ASN UNICODE STRING type used in certificate parsing (0x12). */
0091: private static final byte UNIVSTR_TYPE = 0x12;
0092: /** ASN PRINT STRING type used in certificate parsing (0x13). */
0093: private static final byte PRINTSTR_TYPE = 0x13;
0094: /** ASN TELETEX STRING type used in certificate parsing (0x14). */
0095: private static final byte TELETEXSTR_TYPE = 0x14;
0096: // private static final byte BMPSTR_TYPE = 0x??
0097: /** ASN IA5 STRING type used in certificate parsing (0x16). */
0098: private static final byte IA5STR_TYPE = 0x16; // Used for EmailAddress
0099: /** ASN SEQUENCE type used in certificate parsing (0x30). */
0100: private static final byte SEQUENCE_TYPE = 0x30;
0101: /** ASN SET type used in certificate parsing (0x31). */
0102: private static final byte SET_TYPE = 0x31;
0103:
0104: /** Email address (rfc 822) alternative name type code. */
0105: public static final byte TYPE_EMAIL_ADDRESS = 1;
0106: /** DNS name alternative name type code. */
0107: public static final byte TYPE_DNS_NAME = 2;
0108: /** URI alternative name type code. */
0109: public static final byte TYPE_URI = 6;
0110:
0111: /** Bit mask for digital signature key usage. */
0112: public static final int DIGITAL_SIG_KEY_USAGE = 0x00000001;
0113: /** Bit mask for non repudiation key usage. */
0114: public static final int NON_REPUDIATION_KEY_USAGE = 0x00000002;
0115: /** Bit mask for key encipherment key usage. */
0116: public static final int KEY_ENCIPHER_KEY_USAGE = 0x00000004;
0117: /** Bit mask for data encipherment key usage. */
0118: public static final int DATA_ENCIPHER_KEY_USAGE = 0x00000008;
0119: /** Bit mask for key agreement key usage. */
0120: public static final int KEY_AGREEMENT_KEY_USAGE = 0x00000010;
0121: /** Bit mask for key certificate sign key usage. */
0122: public static final int CERT_SIGN_KEY_USAGE = 0x00000020;
0123: /** Bit mask for CRL sign key usage. */
0124: public static final int CRL_SIGN_KEY_USAGE = 0x00000040;
0125: /** Bit mask for encipher only key usage. */
0126: public static final int ENCIPHER_ONLY_KEY_USAGE = 0x00000080;
0127: /** Bit mask for decipher only key usage. */
0128: public static final int DECIPHER_ONLY_KEY_USAGE = 0x00000100;
0129:
0130: /** Bit mask server auth for extended key usage. */
0131: public static final int SERVER_AUTH_EXT_KEY_USAGE = 0x00000002;
0132: /** Bit mask client auth for extended key usage. */
0133: public static final int CLIENT_AUTH_EXT_KEY_USAGE = 0x00000004;
0134: /** Bit code signing mask for extended key usage. */
0135: public static final int CODE_SIGN_EXT_KEY_USAGE = 0x00000008;
0136: /** Bit email protection mask for extended key usage. */
0137: public static final int EMAIL_EXT_KEY_USAGE = 0x00000010;
0138: /** Bit IPSEC end system mask for extended key usage. */
0139: public static final int IPSEC_END_SYS_EXT_KEY_USAGE = 0x00000020;
0140: /** Bit IPSEC tunnel mask for extended key usage. */
0141: public static final int IPSEC_TUNNEL_EXT_KEY_USAGE = 0x00000040;
0142: /** Bit IPSEC user mask for extended key usage. */
0143: public static final int IPSEC_USER_EXT_KEY_USAGE = 0x00000080;
0144: /** Bit time stamping mask for extended key usage. */
0145: public static final int TIME_STAMP_EXT_KEY_USAGE = 0x00000100;
0146:
0147: /**
0148: * The validity period is contained in thirteen bytes
0149: * yymmddhhmmss followed by 'Z' (for zulu ie GMT), if yy < 50
0150: * assume 20yy else 19yy.
0151: */
0152: private static final int UTC_LENGTH = 13;
0153:
0154: /**
0155: * Maps byte codes that follow id-at (0x55 0x04) to corresponding name
0156: * component tags (e.g. Commom Name, or CN, is 0x55, 0x04, 0x03 and
0157: * Country, or C, is 0x55, 0x04, 0x06). See getName. See X.520 for
0158: * the OIDs and RFC 1779 for the printable labels. Place holders for
0159: * unknown labels have a 0 as the first char.
0160: */
0161: private static final char[][] nameAttr = { { 0 }, { 0 }, { 0 },
0162: { 'C', 'N' }, // Common name: id-at 3
0163: { 'S', 'N' }, // Surname: id-at 4
0164: { 0 }, { 'C' }, // Country: id-at 6
0165: { 'L' }, // Locality: id-at 7
0166: { 'S', 'T' }, // State or province: id-at 8
0167: { 'S', 'T', 'R', 'E', 'E', 'T' }, // Street address: id-at 9
0168: { 'O' }, // Organization: id-at 10
0169: { 'O', 'U' }, // Organization unit: id-at 11
0170: };
0171:
0172: /** Email attribute label in bytes. "EmailAddress" */
0173: private static final char[] EMAIL_ATTR_LABEL = { 'E', 'm', 'a',
0174: 'i', 'l', 'A', 'd', 'd', 'r', 'e', 's', 's' };
0175:
0176: /** Email attribute object identifier. */
0177: private static final byte[] EMAIL_ATTR_OID = { (byte) 0x2a,
0178: (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
0179: (byte) 0x0d, (byte) 0x01, (byte) 0x09, (byte) 0x01 };
0180:
0181: /** Includes DER encoding for OID 1.2.840.113549.1.1. */
0182: private static final byte[] PKCS1Seq = { (byte) 0x30, (byte) 0x0d,
0183: (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
0184: (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
0185: (byte) 0x01, (byte) 0x01, };
0186:
0187: /*
0188: * These signature algorithms are encoded as PKCS1Seq followed by
0189: * a single byte with the corresponding value shown below, e.g.
0190: * md5WithRSAEncryption OBJECT IDENTIFIER ::= {
0191: * iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
0192: * pkcs-1(1) 4
0193: * }
0194: */
0195: /** Uknown algorithm (-1). */
0196: private static final byte NONE = -1;
0197: /** RAS ENCRYPTION (0x01). */
0198: private static final byte RSA_ENCRYPTION = 0x01;
0199: /** MD2_RSA algorithm (0x02). */
0200: private static final byte MD2_RSA = 0x02;
0201: /** MD4_RSA algorithm (0x03). */
0202: private static final byte MD4_RSA = 0x03;
0203: /** MD4_RSA algorithm (0x04). */
0204: private static final byte MD5_RSA = 0x04;
0205: /** SHA1_RSA algorithm (0x05). */
0206: private static final byte SHA1_RSA = 0x05;
0207:
0208: /**
0209: * Expected prefix in decrypted value when MD2 hash is used for signing
0210: * 30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04 10 see verify().
0211: */
0212: private static final byte[] PREFIX_MD2 = { (byte) 0x30,
0213: (byte) 0x20, (byte) 0x30, (byte) 0x0c, (byte) 0x06,
0214: (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
0215: (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x02,
0216: (byte) 0x02, (byte) 0x05, (byte) 0x00, (byte) 0x04,
0217: (byte) 0x10 };
0218:
0219: /**
0220: * Expected prefix in decrypted value when MD5 hash is used for signing
0221: * 30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 see verify().
0222: */
0223: private static final byte[] PREFIX_MD5 = { (byte) 0x30,
0224: (byte) 0x20, (byte) 0x30, (byte) 0x0c, (byte) 0x06,
0225: (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
0226: (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x02,
0227: (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x04,
0228: (byte) 0x10 };
0229: /**
0230: * Expected prefix in decrypted value when SHA-1 hash is used for signing
0231: * 30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14.
0232: */
0233: private static final byte[] PREFIX_SHA1 = { (byte) 0x30,
0234: (byte) 0x21, (byte) 0x30, (byte) 0x09, (byte) 0x06,
0235: (byte) 0x05, (byte) 0x2b, (byte) 0x0e, (byte) 0x03,
0236: (byte) 0x02, (byte) 0x1a, (byte) 0x05, (byte) 0x00,
0237: (byte) 0x04, (byte) 0x14 };
0238:
0239: /** ASN encoding for NULL. */
0240: private static final byte[] NullSeq = { (byte) 0x05, (byte) 0x00 };
0241:
0242: /** This is how the encoding of validity information begins. */
0243: private static final byte[] ValiditySeq = { (byte) 0x30,
0244: (byte) 0x1e };
0245:
0246: /** This is how the encoding of UTCTime begins. */
0247: private static final byte[] UTCSeq = { (byte) 0x17, (byte) 0x0d };
0248:
0249: /** Includes DER encoding for id-kp (key purpose). */
0250: private static final byte[] ID_KP = { (byte) 0x2b, (byte) 0x06,
0251: (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x07,
0252: (byte) 0x03 };
0253:
0254: /** True iff subject matches issuer. */
0255: private boolean selfSigned;
0256: /** X.509 version. For more readable code the version field starts a 1. */
0257: private byte version = 1;
0258: /** MD5 fingerprint of the certificate. */
0259: private byte[] fp = null;
0260: /** Certificate serial number. */
0261: private String serialNumber;
0262: /** Certificate subject. */
0263: private String subject;
0264: /** Certificate issuer. */
0265: private String issuer;
0266: /** Beginning of certificate validity period. */
0267: private long from = 0;
0268: /** End of certificate validity period. */
0269: private long until = 0;
0270: /** Certificate RSA Public key. */
0271: private RSAPublicKey pubKey = null;
0272:
0273: // The following fields are only meaningful in certificates created
0274: // by fully parsing the DER encoding. They are meaningless on
0275: // certificates created using the Certificate constructor below.
0276: /** Index inside encoding. */
0277: private int idx = 0;
0278: /** Contains Certificate DER encoding. */
0279: private byte[] enc = null;
0280: /** Offset where TBSCertificate starts. */
0281: private int TBSStart = 0;
0282: /** Length of TBSCertificate. */
0283: private int TBSLen = 0;
0284: /** Algorithm used to sign the cert. */
0285: private byte sigAlg = NONE;
0286: /** Issuer signature on certificate. */
0287: private byte[] signature = null;
0288: /** Hash of TBSCertificate. */
0289: private byte[] TBSCertHash = null;
0290: /** True iff cert has unrecognized critical extension. */
0291: private boolean badExt = false;
0292: /** Alternate name. */
0293:
0294: /** format of the subject alternative name, 2 means a DNS name */
0295: private byte subAltNameType;
0296: /** subject alternative name */
0297: private Object subAltName;
0298: /** does the cert include BasicConstaints. */
0299: private boolean hasBC = false;
0300: /** CA value in BasicConstraints. */
0301: private boolean isCA = false;
0302: /** Path Length constriant from Basic constraints. */
0303: private int pLenConstr = MISSING_PATH_LENGTH_CONSTRAINT;
0304: /** Collection of keyUsage bits. */
0305: private int keyUsage = -1;
0306: /** Collection of extended keyUsage bits. */
0307: private int extKeyUsage = -1;
0308:
0309: /** Private constructor */
0310: private X509Certificate() {
0311: }
0312:
0313: /**
0314: * Creates an X.509 certificate with the specified attributes.
0315: * This constructor is only used for creating trusted certificates.
0316: * <BR />
0317: * <B>NOTE:</B> All signature related values in these certificates
0318: * (such as the signing algorithm and signature) are set to null and
0319: * invoking methods that access signature information, e.g. verify()
0320: * and getSigAlgName() can produce unexpected errors.
0321: * <P />
0322: * @param ver byte containing X.509 version starting a 0
0323: * @param rawSerialNumber byte array containing the serial number
0324: * @param sub subject name
0325: * @param iss issuer name
0326: * @param notBefore start of validity period expressed in milliseconds
0327: * since midnight Jan 1, 1970 UTC
0328: * @param notAfter end of validity period expressed as above
0329: * @param mod modulus associated with the RSA Public Key
0330: * @param exp exponent associated with the RSA Public Key
0331: * @param chash 16-byte MD5 hash of the certificate's ASN.1
0332: * DER encoding
0333: * @param pLen Is the pathLenConstraint associated with a version 3
0334: * certificate. This parameter is ignored for v1 and
0335: * v2 certificates. If a v3 certificate does not
0336: * have basicConstraints or is not a CA cert, callers
0337: * should pass MISSING_PATH_LENGTH_CONSTRAINT. If the
0338: * v3 certificate has basicConstraints, CA is set but
0339: * pathLenConstraint is missing (indicating no limit
0340: * on the certificate chain), callers should pass
0341: * UNLIMITED_CERT_CHAIN_LENGTH.
0342: * @exception Exception in case of a problem with RSA public key parameters
0343: */
0344: public X509Certificate(byte ver, byte[] rawSerialNumber,
0345: String sub, String iss, long notBefore, long notAfter,
0346: byte[] mod, byte[] exp, byte[] chash, int pLen)
0347: throws Exception {
0348: version = ver;
0349: serialNumber = Utils.hexEncode(rawSerialNumber, 0,
0350: rawSerialNumber.length);
0351:
0352: /*
0353: * We are paranoid so we don't just assign a reference as in
0354: * fp = chash; subject = sub; issuer = iss;
0355: */
0356: if (chash != null) {
0357: fp = new byte[chash.length];
0358: System.arraycopy(chash, 0, fp, 0, chash.length);
0359: }
0360:
0361: subject = new String(sub);
0362: issuer = new String(iss);
0363: from = notBefore;
0364: until = notAfter;
0365: sigAlg = NONE;
0366:
0367: if (subject.compareTo(issuer) == 0) {
0368: selfSigned = true;
0369: }
0370:
0371: pubKey = new RSAPublicKey(mod, exp);
0372:
0373: if ((ver == 3) && (pLen != MISSING_PATH_LENGTH_CONSTRAINT)) {
0374: hasBC = isCA = true;
0375: pLenConstr = pLen;
0376: }
0377: }
0378:
0379: /**
0380: * Matches the contents of buf against this certificates DER
0381: * encoding (enc) starting at the current offset (idx).
0382: * <P />
0383: * @param buf buffer whose contents are to be matched against the
0384: * certificate encoding
0385: * @exception Exception if the match fails
0386: */
0387: private void match(byte[] buf) throws Exception {
0388: if (idx + buf.length < enc.length) {
0389: for (int i = 0; i < buf.length; i++) {
0390: if (enc[idx++] != buf[i])
0391: throw new Exception("match() error 1");
0392: }
0393: } else {
0394: throw new Exception("match() error 2");
0395: }
0396: }
0397:
0398: /**
0399: * Matches the specified ASN type against this certificates DER
0400: * encoding (enc) starting at the current offset (idx) and returns
0401: * its encoded length.
0402: * <P />
0403: * @param type ASN type to be matched
0404: * @return the size in bytes of the sub-encoding associated with
0405: * the given type
0406: * @exception IOException if the length is not formated correctly
0407: */
0408: private int getLen(byte type) throws IOException {
0409:
0410: if ((enc[idx] == type) || ((type == ANY_STRING_TYPE) && // ordered by likelihood of match
0411: ((enc[idx] == PRINTSTR_TYPE)
0412: || (enc[idx] == TELETEXSTR_TYPE)
0413: || (enc[idx] == UTF8STR_TYPE)
0414: || (enc[idx] == IA5STR_TYPE) || (enc[idx] == UNIVSTR_TYPE)))) {
0415: idx++;
0416: int size = (enc[idx++] & 0xff);
0417: if (size >= 128) {
0418: int tmp = size - 128;
0419: // NOTE: for now, all sizes must fit int two bytes
0420: if ((tmp > 2) || (idx + tmp > enc.length)) {
0421: throw new IOException("getLen() err 1");
0422: } else {
0423: size = 0;
0424: while (tmp > 0) {
0425: size = (size << 8) + (enc[idx++] & 0xff);
0426: tmp--;
0427: }
0428: }
0429: }
0430: return size;
0431: }
0432:
0433: throw new IOException("getLen() err 2");
0434: }
0435:
0436: /**
0437: * Expects to see a PKCS1 algorithm identifier in the DER encoding
0438: * (enc) starting at the current offset (idx).
0439: * <P />
0440: * @return a single-byte algorithm identifier, e.g. MD5_RSA, MD2_RSA
0441: * @exception IOException if an error is encountered during parsing
0442: */
0443: private byte getAlg() throws IOException {
0444: byte val;
0445:
0446: try {
0447: match(PKCS1Seq);
0448: val = enc[idx++];
0449: match(NullSeq);
0450: return val;
0451: } catch (Exception e) {
0452: throw new IOException("Algorithm Id parsing failed");
0453: }
0454: }
0455:
0456: /**
0457: * Parses a SubjectName or IssuerName in the DER encoding
0458: * (enc) starting at the current offset (idx) and ending
0459: * at end.
0460: * <P />
0461: * @param end ending offset for the DER-encoded name
0462: * @return a human friendly string representation of the name
0463: * @exception IOException if an error is encountered during parsing
0464: */
0465: private String getName(int end) throws IOException {
0466: byte[] name = new byte[MAX_NAME_LENGTH];
0467: int nameLen = 0;
0468: int len = 0;
0469: int cidx; // index where the most recently seen name component starts
0470: int clen; // Component length
0471: char[] label = null;
0472: int aidx;
0473:
0474: while (idx < end) {
0475: if (nameLen != 0) {
0476: // this is not the first time so insert a separator
0477: name[nameLen++] = (byte) ';';
0478: }
0479:
0480: getLen(SET_TYPE);
0481: getLen(SEQUENCE_TYPE);
0482:
0483: /*
0484: * Save the start of name component, e.g CommonName
0485: * ... and its length
0486: */
0487: clen = getLen(OID_TYPE);
0488: cidx = idx;
0489: idx += clen;
0490:
0491: /*
0492: * At this point we tag the name component, e.g. C= or hex
0493: * if unknown.
0494: */
0495: if ((clen == 3) && (enc[cidx] == 0x55)
0496: && (enc[cidx + 1] == 0x04)) {
0497: // begins with id-at, so try to see if we have a label
0498: aidx = enc[cidx + 2] & 0xFF;
0499: if ((aidx < nameAttr.length)
0500: && (nameAttr[aidx][0] != 0)) {
0501: label = nameAttr[aidx];
0502: } else {
0503: label = Utils.hexEncodeToChars(enc, cidx, clen);
0504: }
0505: } else if (Utils.byteMatch(enc, cidx, EMAIL_ATTR_OID, 0,
0506: EMAIL_ATTR_OID.length)) {
0507: label = EMAIL_ATTR_LABEL;
0508: } else {
0509: label = Utils.hexEncodeToChars(enc, cidx, clen);
0510: }
0511:
0512: for (int i = 0; i < label.length; i++) {
0513: name[nameLen++] = (byte) label[i];
0514: }
0515:
0516: name[nameLen++] = (byte) '=';
0517:
0518: len = getLen(ANY_STRING_TYPE);
0519:
0520: if (len > 0) {
0521: for (int i = 0; i < len; i++) {
0522: name[nameLen++] = enc[idx++];
0523: }
0524: }
0525: }
0526:
0527: return new String(name, 0, nameLen, "UTF-8");
0528: }
0529:
0530: /**
0531: * Gets a string representation of the UTC time whose DER ecnoding
0532: * is contained in the specified buffer.
0533: * <P />
0534: * @param buf buffer containing the DER encoding of UTC Time
0535: * @param off starting offset of the encoding inside buf
0536: * @return a string represntation of the UTC time in the form
0537: * yy/mm/dd hh:mm:ss
0538: * @exception IOException if an error is encountered during parsing
0539: */
0540: private static long getUTCTime(byte[] buf, int off)
0541: throws IOException {
0542: Calendar cal;
0543: int[] period = new int[6]; // year, month, day, hour, minute, second
0544:
0545: if (buf[off + UTC_LENGTH - 1] != (byte) 'Z')
0546: throw new IOException("getUTCTime() err 1");
0547: for (int i = 0; i < 6; i++) {
0548: period[i] = 0;
0549: if ((buf[2 * i + off] < (byte) '0')
0550: || (buf[2 * i + off] > (byte) '9'))
0551: throw new IOException("getUTCTime() err 2");
0552: period[i] = buf[2 * i + off] - (int) '0';
0553: if ((buf[2 * i + off + 1] < (byte) '0')
0554: || (buf[2 * i + off + 1] > (byte) '9'))
0555: throw new IOException("getUTCTime() err 3");
0556: period[i] = (period[i] * 10)
0557: + (buf[2 * i + off + 1] - (int) '0');
0558: }
0559:
0560: if (period[0] < 50) { // from rfc2459
0561: period[0] += 2000;
0562: } else {
0563: period[0] += 1900;
0564: }
0565:
0566: cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
0567: cal.set(Calendar.YEAR, period[0]);
0568: cal.set(Calendar.MONTH, period[1] - 1); // months go 0-11
0569: cal.set(Calendar.DAY_OF_MONTH, period[2]);
0570: cal.set(Calendar.HOUR_OF_DAY, period[3]);
0571: cal.set(Calendar.MINUTE, period[4]);
0572: cal.set(Calendar.SECOND, period[5]);
0573: cal.set(Calendar.MILLISECOND, 0);
0574: return cal.getTime().getTime();
0575: }
0576:
0577: /**
0578: * Parses X.509v3 extensions in the certificate encoding until
0579: * the specified index.
0580: * <p />
0581: * @param end index of the last byte in the certificate encoding
0582: * to be processed
0583: * @exception IOException in case of parsing problems
0584: */
0585: private void parseExtensions(int end) throws IOException {
0586: /*
0587: * NOTE: If one does not wish to support v3 extensions
0588: * at all (to save code), one can simply set badExt to
0589: * true and return -- the code that actually parses extensions
0590: * can be commented out
0591: */
0592: String extId = null;
0593: int extIdIdx = 0;
0594: int extIdLen = 0;
0595: boolean crit;
0596: int extValIdx = 0;
0597: int extValLen = 0;
0598: int tmp;
0599:
0600: getLen((byte) 0xa3); // extensions start with 0xa3
0601: getLen(SEQUENCE_TYPE);
0602: while (idx < end) {
0603: extId = null;
0604: getLen(SEQUENCE_TYPE);
0605: extIdLen = getLen(OID_TYPE);
0606: extIdIdx = idx;
0607: idx += extIdLen;
0608: crit = false;
0609: if ((enc[idx] == 0x01) && (enc[idx + 1] == 0x01)) {
0610: idx += 2;
0611: crit = (enc[idx++] == (byte) 0xff) ? true : false;
0612: }
0613: extValLen = getLen(OCTETSTR_TYPE);
0614: extValIdx = idx;
0615: if ((enc[extIdIdx] == 0x55) && (enc[extIdIdx + 1] == 0x1d)) {
0616: // Do we recognize this? NOTE: id-ce is 0x55, 0x1d
0617: switch (enc[extIdIdx + 2] & 0xff) {
0618: case 0x0f: // keyUsage = id-ce 15
0619: extId = "KU";
0620: if (keyUsage == -1) {
0621: keyUsage = 0;
0622: }
0623:
0624: tmp = getLen(BITSTRING_TYPE) - 1;
0625: int unused = enc[idx++]; // get unused bits in last octet
0626: byte b = 0;
0627:
0628: // process each bit in the bitstring starting with
0629: // the most significant
0630: for (int i = 0; i < ((tmp << 3) - unused); i++) {
0631: if ((i % 8) == 0) {
0632: b = enc[idx++];
0633: }
0634:
0635: if (b < 0) {
0636: keyUsage |= 1 << i;
0637: }
0638:
0639: b = (byte) (b << 1);
0640: }
0641:
0642: break;
0643:
0644: case 0x11: // subAltName = id-ce 17
0645: StringBuffer temp = new StringBuffer();
0646: int start = idx + 4;
0647: int length = extValLen - 4;
0648: extId = "SAN";
0649:
0650: /*
0651: * First byte stores the type e.g. 1=rfc822Name(email),
0652: * 2=dNSName, 6=URI etc
0653: */
0654: subAltNameType = (byte) (enc[idx + 2] - 0x80);
0655:
0656: switch (subAltNameType) {
0657: case TYPE_EMAIL_ADDRESS:
0658: case TYPE_DNS_NAME:
0659: case TYPE_URI:
0660: for (int i = 0; i < length; i++) {
0661: temp.append((char) enc[start + i]);
0662: }
0663:
0664: subAltName = temp.toString();
0665: break;
0666:
0667: default:
0668: subAltName = new byte[length];
0669: for (int i = 0; i < length; i++) {
0670: ((byte[]) subAltName)[i] = enc[start + i];
0671: }
0672: }
0673:
0674: break;
0675:
0676: case 0x13: // basicConstr = id-ce 19
0677: hasBC = true;
0678: extId = "BC";
0679: tmp = getLen(SEQUENCE_TYPE);
0680: if (tmp == 0)
0681: break;
0682: // ca is encoded as an ASN boolean (default is false)
0683: if ((enc[idx] == 0x01) && (enc[idx + 1] == 0x01)
0684: && (enc[idx + 2] == (byte) 0xff)) {
0685: isCA = true;
0686: idx += 3;
0687: }
0688:
0689: /*
0690: * path length constraint is encoded as optional ASN
0691: * integer
0692: */
0693: if ((enc[idx] == 0x02) && (enc[idx + 1] != 0)) {
0694: tmp = getLen(INTEGER_TYPE);
0695: pLenConstr = 0;
0696: for (int i = 0; i < tmp; i++) {
0697: pLenConstr = (pLenConstr << 16)
0698: + enc[idx + i];
0699: }
0700: idx += tmp;
0701: } else {
0702: if (isCA)
0703: pLenConstr = UNLIMITED_CERT_CHAIN_LENGTH;
0704: }
0705: break;
0706:
0707: case 0x25: // extendedKeyUsage = id-ce 37
0708: extId = "EKU";
0709: if (extKeyUsage == -1) {
0710: extKeyUsage = 0;
0711: }
0712:
0713: getLen(SEQUENCE_TYPE);
0714: int kuOidLen;
0715: while (idx < extValIdx + extValLen) {
0716: kuOidLen = getLen(OID_TYPE);
0717: if ((kuOidLen == ID_KP.length + 1)
0718: && Utils.byteMatch(enc, idx, ID_KP, 0,
0719: ID_KP.length)
0720: && (enc[idx + ID_KP.length] > 0)
0721: && (enc[idx + ID_KP.length] < 9)) {
0722: extKeyUsage |= (1 << (enc[idx
0723: + ID_KP.length]));
0724: } else {
0725: if (crit)
0726: badExt = true;
0727: }
0728: idx += kuOidLen;
0729: }
0730:
0731: if (!crit) {
0732: // ignore extended key usage if not critical
0733: extKeyUsage = -1;
0734: }
0735:
0736: break;
0737: /*
0738: * Extensions which we do not currently support include:
0739: * subjectDirectoryAttribute 0x09,
0740: * subjectKeyIdentifier 0x0e, privateKeyUsagePeriod 0x10,
0741: * issuerAltName 0x12, cRLNumber 0x14, reasonCode 0x15,
0742: * instructionCode 0x17, invalidityDate 0x18,
0743: * deltaCRLIndicator 0x1b, issuingDistributionPoint 0x1c,
0744: * certificateIssuer 0x1d, nameConstraints 0x1e,
0745: * cRLDistributionPoints 0x1f, certificatePolicies 0x20,
0746: * policyMappings 0x21, authorityKeyIdentifier 0x23,
0747: * policyConstraints 0x24
0748: */
0749: }
0750: }
0751:
0752: // For debugging only
0753: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0754: Logging.report(Logging.INFORMATION,
0755: LogChannels.LC_SECURITY, "<Id: "
0756: + Utils.hexEncode(enc, extIdIdx,
0757: extIdLen)
0758: + (crit ? ", critical, " : ", ")
0759: + Utils.hexEncode(enc, extValIdx,
0760: extValLen)
0761: + ">"
0762: + ((extId == null) ? " (Unrecognized)"
0763: : ""));
0764: }
0765:
0766: if ((extId == null) && crit)
0767: badExt = true;
0768:
0769: idx = extValIdx + extValLen;
0770: }
0771:
0772: if (idx != end) {
0773: throw new IOException("Extension parsing problem");
0774: }
0775:
0776: } // Done processing extensions
0777:
0778: /**
0779: * Creates a certificate by parsing the ASN.1 DER X.509 certificate
0780: * encoding in the specified buffer.<BR />
0781: * <B>NOTE:</B> In the standard edition, equivalent functionality
0782: * is provided by CertificateFactory.generateCertificate(InputStream).
0783: * <P />
0784: * @param buf byte array to be read
0785: * @param off offset within the byte array
0786: * @param len number of bytes to be read
0787: * @return a certificate object corresponding to the DER encoding
0788: * or null (in case of an encoding problem)
0789: * @exception IOException if there is a parsing error
0790: */
0791: public static X509Certificate generateCertificate(byte[] buf,
0792: int off, int len) throws IOException {
0793: /*
0794: * force bad parameter errors now, so later we can consider any out of
0795: * bounds errors to be parsing errors
0796: */
0797: int test = buf[off] + buf[len - 1] + buf[off + len - 1];
0798:
0799: try {
0800: int start = 0;
0801: int size = 0;
0802: byte[] hash = new byte[16]; // for MD5 fingerprint
0803: X509Certificate res = null;
0804: int publicKeyLen;
0805: int publicKeyPos;
0806: int modulusPos;
0807: int modulusLen;
0808: int exponentPos;
0809: int exponentLen;
0810:
0811: // Compute the MD5 fingerprint
0812: MessageDigest md = MessageDigest.getInstance("MD5");
0813:
0814: md.update(buf, off, len);
0815: md.digest(hash, 0, hash.length);
0816:
0817: /*
0818: * Create a new certificate and fill its attributes by parsing
0819: * the DER encoding
0820: */
0821: res = new X509Certificate();
0822:
0823: // Prepare to parse this certificate
0824: res.idx = 0;
0825: // Set the encoding
0826: res.enc = new byte[len];
0827: System.arraycopy(buf, off, res.enc, 0, len);
0828: // ... and the fingerprint
0829: res.fp = new byte[hash.length];
0830: System.arraycopy(hash, 0, res.fp, 0, hash.length);
0831:
0832: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0833: Logging.report(Logging.INFORMATION,
0834: LogChannels.LC_SECURITY,
0835: "-------- Begin Certificate -------");
0836: }
0837: /*
0838: * A Certificate is a sequence of a TBSCertificate, a signature
0839: * algorithm identifier and the signature
0840: */
0841: res.getLen(SEQUENCE_TYPE);
0842: // Now read the TBS certificate
0843: res.TBSStart = res.idx;
0844: size = res.getLen(SEQUENCE_TYPE);
0845: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0846: Logging.report(Logging.INFORMATION,
0847: LogChannels.LC_SECURITY,
0848: "-------- Begin TBSCertificate -------");
0849: }
0850:
0851: int sigAlgIdx = res.idx + size;
0852: res.TBSLen = sigAlgIdx - res.TBSStart;
0853: // Now parse the version
0854: if ((res.enc[res.idx] & 0xf0) == 0xa0) {
0855: res.idx++;
0856: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0857: Logging.report(Logging.INFORMATION,
0858: LogChannels.LC_SECURITY, "Version info: "
0859: + Utils.hexEncode(res.enc,
0860: (res.idx + 1),
0861: res.enc[res.idx]));
0862: }
0863: size = (res.enc[res.idx++] & 0xff);
0864: if (res.idx + size > res.enc.length) {
0865: throw new IOException("Version info too long");
0866: }
0867:
0868: res.version = (byte) (res.enc[res.idx + (size - 1)]);
0869: res.idx += size;
0870: } else {
0871: res.version = 1; // No explicit version value
0872: }
0873:
0874: // Expect the serial number coded as an integer
0875: size = res.getLen(INTEGER_TYPE);
0876: res.serialNumber = Utils.hexEncode(res.enc, res.idx, size);
0877: res.idx += size;
0878:
0879: // Expect the signature AlgorithmIdentifier
0880: byte id = res.getAlg();
0881: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0882: Logging.report(Logging.INFORMATION,
0883: LogChannels.LC_SECURITY, "Algorithm Id: " + id);
0884: }
0885: // Expect the issuer name
0886: start = res.idx;
0887: size = res.getLen(SEQUENCE_TYPE);
0888: int end = res.idx + size;
0889: try {
0890: res.issuer = res.getName(end);
0891: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0892: Logging.report(Logging.INFORMATION,
0893: LogChannels.LC_SECURITY, "Issuer: "
0894: + res.issuer);
0895:
0896: }
0897: } catch (Exception e) {
0898: throw new IOException("Could not parse issuer name");
0899: }
0900:
0901: // Validity is a sequence of two UTCTime values
0902: try {
0903: res.match(ValiditySeq);
0904: // get start time
0905: res.match(UTCSeq);
0906: res.from = getUTCTime(res.enc, res.idx);
0907: res.idx += UTC_LENGTH;
0908: // get end time
0909: res.match(UTCSeq);
0910: res.until = getUTCTime(res.enc, res.idx);
0911: res.idx += UTC_LENGTH;
0912: } catch (Exception e) {
0913: throw new IOException(
0914: "Could not parse validity information"
0915: + "caught " + e);
0916: }
0917:
0918: // Expect the subject name
0919: start = res.idx;
0920: size = res.getLen(SEQUENCE_TYPE);
0921: end = res.idx + size;
0922: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0923: Logging
0924: .report(Logging.INFORMATION,
0925: LogChannels.LC_SECURITY, "Subject: "
0926: + Utils.hexEncode(res.enc,
0927: start, size));
0928: }
0929: if (size != 0) {
0930: try {
0931: res.subject = res.getName(end);
0932: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0933: Logging.report(Logging.INFORMATION,
0934: LogChannels.LC_SECURITY, "Subject: "
0935: + res.subject);
0936: }
0937: } catch (Exception e) {
0938: throw new IOException(
0939: "Could not parse subject name");
0940: }
0941: } // NOTE: the subject can be null (empty sequence) if
0942: // subjectAltName is present
0943:
0944: // Parse the subject public key information
0945: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0946: Logging.report(Logging.INFORMATION,
0947: LogChannels.LC_SECURITY,
0948: "SubjectPublicKeyInfo follows");
0949: }
0950:
0951: publicKeyLen = res.getLen(SEQUENCE_TYPE);
0952: publicKeyPos = res.idx;
0953:
0954: // Match the algorithm Id
0955: id = res.getAlg();
0956: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0957: Logging.report(Logging.INFORMATION,
0958: LogChannels.LC_SECURITY,
0959: "Public Key Algorithm: " + id);
0960: }
0961:
0962: if (id != RSA_ENCRYPTION) {
0963: // skip the public key
0964: res.idx = publicKeyPos + publicKeyLen;
0965: }
0966:
0967: // Get the bit string
0968: res.getLen(BITSTRING_TYPE);
0969: if (res.enc[res.idx++] != 0x00) {
0970: throw new IOException(
0971: "Bitstring error while parsing public "
0972: + "key information");
0973: }
0974:
0975: res.getLen(SEQUENCE_TYPE);
0976: size = res.getLen(INTEGER_TYPE);
0977: if (res.enc[res.idx] == (byte) 0x00) {
0978: // strip off the sign byte
0979: size--;
0980: res.idx++;
0981: }
0982:
0983: // Build the RSAPublicKey
0984: modulusPos = res.idx;
0985: modulusLen = size;
0986:
0987: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
0988: Logging.report(Logging.INFORMATION,
0989: LogChannels.LC_SECURITY, "Modulus: "
0990: + Utils.hexEncode(res.enc, modulusPos,
0991: modulusLen));
0992: }
0993:
0994: res.idx += size;
0995:
0996: size = res.getLen(INTEGER_TYPE);
0997: if (res.enc[res.idx] == (byte) 0x00) {
0998: // strip off the sign byte
0999: size--;
1000: res.idx++;
1001: }
1002:
1003: exponentPos = res.idx;
1004: exponentLen = size;
1005:
1006: res.pubKey = new RSAPublicKey(res.enc, modulusPos,
1007: modulusLen, res.enc, exponentPos, exponentLen);
1008:
1009: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1010: Logging.report(Logging.INFORMATION,
1011: LogChannels.LC_SECURITY, "Exponent: "
1012: + Utils.hexEncode(res.enc, exponentPos,
1013: exponentLen));
1014: }
1015:
1016: res.idx += size;
1017: if (res.idx != sigAlgIdx) {
1018: if (res.version < 3) {
1019: throw new IOException(
1020: "Unexpected extensions in old version cert");
1021: } else {
1022: res.parseExtensions(sigAlgIdx);
1023: }
1024: }
1025:
1026: // get the signatureAlgorithm
1027: res.sigAlg = res.getAlg();
1028:
1029: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1030: Logging.report(Logging.INFORMATION,
1031: LogChannels.LC_SECURITY,
1032: "Signature Algorithm: " + res.getSigAlgName());
1033: }
1034:
1035: /*
1036: * If this is a supported signature algorithm, compute and save
1037: * the hash of TBSCertificate. A null TBSCertHash indicates
1038: * the use of an unsupported signature algorithm (see verify())
1039: */
1040: md = null;
1041: if (res.sigAlg == MD2_RSA) {
1042: md = MessageDigest.getInstance("MD2");
1043: } else if (res.sigAlg == MD5_RSA) {
1044: md = MessageDigest.getInstance("MD5");
1045: } else if (res.sigAlg == SHA1_RSA) {
1046: md = MessageDigest.getInstance("SHA-1");
1047: }
1048:
1049: if (md != null) {
1050: res.TBSCertHash = new byte[md.getDigestLength()];
1051: md.update(buf, off + res.TBSStart, res.TBSLen);
1052: md.digest(res.TBSCertHash, 0, res.TBSCertHash.length);
1053: }
1054:
1055: // get the signature
1056: size = res.getLen(BITSTRING_TYPE);
1057: if (res.enc[res.idx++] != 0x00) {
1058: throw new IOException(
1059: "Bitstring error in signature parsing");
1060: }
1061:
1062: /*
1063: * We pad the signature to a multiple of 8-bytes before storing
1064: * since we only support RSA modulus lengths that are multiples
1065: * of 8 bytes and the two should match for decryption to succeed.
1066: */
1067: int sigLen = (((size - 1) + 7) >>> 3) << 3;
1068: res.signature = new byte[sigLen];
1069: System.arraycopy(res.enc, res.idx, res.signature,
1070: (sigLen - (size - 1)), (size - 1));
1071:
1072: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
1073: Logging.report(Logging.INFORMATION,
1074: LogChannels.LC_SECURITY, sigLen
1075: + "-byte signature: "
1076: + Utils.hexEncode(res.signature));
1077: }
1078: return res;
1079: } catch (IndexOutOfBoundsException e) {
1080: throw new IOException("Bad length detected in cert DER");
1081: } catch (GeneralSecurityException e) {
1082: throw new IOException(e.toString());
1083: }
1084: }
1085:
1086: /**
1087: * Verify a chain of certificates.
1088: *
1089: * @param certs list of certificates with first being entity certificate
1090: * and the last being the CA issued certificate.
1091: * @param keyUsage -1 to not check the key usage extension, or
1092: * a key usage bit mask to check for if the extension is present
1093: * @param extKeyUsage -1 to not check the extended key usage extension, or
1094: * a extended key usage bit mask to check for if the extension
1095: * is present
1096: * @param certStore store of trusted CA certificates
1097: *
1098: * @return authorization path: an array of names from most trusted to
1099: * least trusted from the certificate chain
1100: *
1101: * @exception CertificateException if there is an error verifying the chain
1102: */
1103: public static String[] verifyChain(Vector certs, int keyUsage,
1104: int extKeyUsage, CertStore certStore)
1105: throws CertificateException {
1106: X509Certificate cert;
1107: X509Certificate prevCert;
1108: PublicKey key;
1109: Vector keys;
1110: X509Certificate[] caCerts; // CA X509Certificates
1111: int maxPathLen = -1; // 0 means a chain of 1 so -1 means no chain
1112: int prevMaxPathLen;
1113: Vector subjectNames = new Vector();
1114: String[] authPath;
1115:
1116: // must be an enitity certificate
1117: cert = (X509Certificate) certs.elementAt(0);
1118: checkKeyUsageAndValidity(cert, keyUsage, extKeyUsage);
1119:
1120: for (int i = 1;; i++) {
1121: // look up the public key of the certificate issurer
1122: caCerts = certStore.getCertificates(cert.getIssuer());
1123: if (caCerts != null) {
1124: // found a known CA no need to go on to the next cert
1125: if (caCerts[0].getSubject() != cert.getSubject()) {
1126: subjectNames.addElement(caCerts[0].getSubject());
1127: }
1128:
1129: break;
1130: }
1131:
1132: if (i >= certs.size()) {
1133: throw new CertificateException(cert,
1134: CertificateException.UNRECOGNIZED_ISSUER);
1135: }
1136:
1137: /* Save the name of subject. */
1138: subjectNames.addElement(cert.getSubject());
1139:
1140: prevCert = cert;
1141: cert = (X509Certificate) certs.elementAt(i);
1142:
1143: /*
1144: * This must be a CA so the key usage always is certficate
1145: * signing.
1146: */
1147: checkKeyUsageAndValidity(cert,
1148: X509Certificate.CERT_SIGN_KEY_USAGE, extKeyUsage);
1149:
1150: /*
1151: * This is a chain, check chain link:
1152: * the subject of this certificate must be the issuer of
1153: * the previous certificate
1154: */
1155: if (prevCert.getIssuer().compareTo(cert.getSubject()) != 0) {
1156: throw new CertificateException(prevCert,
1157: CertificateException.BROKEN_CHAIN);
1158: }
1159:
1160: /*
1161: * Check if basicConstraints are satisfied. Note a zero
1162: * pathLength means we should have only processed one cert
1163: * so far.
1164: */
1165: prevMaxPathLen = maxPathLen;
1166: maxPathLen = cert.getBasicConstraints();
1167:
1168: if (maxPathLen != X509Certificate.UNLIMITED_CERT_CHAIN_LENGTH
1169: && maxPathLen <= prevMaxPathLen) {
1170: if (cert.getSubject().equals(cert.getIssuer())) {
1171: /*
1172: * This cert is a redundent, self signed CA cert
1173: * allowed to be at the end of the chain.
1174: * These certificates may version 1, so will not
1175: * have extensions. So this really should be the
1176: * unrecognized issuer.
1177: */
1178: throw new CertificateException(prevCert,
1179: CertificateException.UNRECOGNIZED_ISSUER);
1180: }
1181:
1182: if (maxPathLen == X509Certificate.MISSING_PATH_LENGTH_CONSTRAINT) {
1183:
1184: throw new CertificateException(
1185: cert,
1186: CertificateException.UNAUTHORIZED_INTERMEDIATE_CA);
1187: }
1188:
1189: /*
1190: * An intermediate CA has over granted
1191: * its given authority to the previous CA in the
1192: * chain.
1193: */
1194: throw new CertificateException(cert,
1195: CertificateException.CERTIFICATE_CHAIN_TOO_LONG);
1196: }
1197:
1198: /* Save the most time intensive check for last. */
1199: prevCert.verify(cert.getPublicKey());
1200: }
1201:
1202: for (int i = 0; i < caCerts.length; i++) {
1203: try {
1204: cert.verify(caCerts[i].getPublicKey());
1205: } catch (CertificateException ce) {
1206: /*
1207: * the exception will be when we get out side of the loop,
1208: * if none of the other keys work
1209: */
1210: continue;
1211: }
1212:
1213: // check the CA key for valid dates
1214: try {
1215: caCerts[i].checkValidity();
1216: } catch (CertificateException ce) {
1217: if (ce.getReason() == CertificateException.EXPIRED) {
1218: /*
1219: * Change the exception reason, so the
1220: * application knows that the problem is with
1221: * the device and not the server.
1222: */
1223: throw new CertificateException(caCerts[i],
1224: CertificateException.ROOT_CA_EXPIRED);
1225: }
1226:
1227: throw ce;
1228: }
1229:
1230: // Success
1231: authPath = new String[subjectNames.size()];
1232: for (int j = subjectNames.size() - 1, k = 0; j >= 0; j--, k++) {
1233: authPath[k] = (String) subjectNames.elementAt(j);
1234: }
1235:
1236: return authPath;
1237: }
1238:
1239: throw new CertificateException(cert,
1240: CertificateException.VERIFICATION_FAILED);
1241: }
1242:
1243: /**
1244: * Check the key usage, extended key usage and validity of a certificate.
1245: *
1246: * @param cert certificate to check
1247: * @param keyUsage -1 to not check the key usage extension, or
1248: * a key usage bit mask to check for if the extension is present
1249: * @param extKeyUsage -1 to not check the extended key usage extension, or
1250: * a extended key usage bit mask to check for if the extension
1251: * is present
1252: *
1253: * @exception CertificateException if there is an error
1254: */
1255: private static void checkKeyUsageAndValidity(X509Certificate cert,
1256: int keyUsage, int extKeyUsage) throws CertificateException {
1257: int certKeyUsage;
1258:
1259: // Check if this certificate has any bad extensions
1260: cert.checkExtensions();
1261:
1262: certKeyUsage = cert.getKeyUsage();
1263: if (keyUsage != -1 && certKeyUsage != -1
1264: && (certKeyUsage & keyUsage) != keyUsage) {
1265: throw new CertificateException(cert,
1266: CertificateException.INAPPROPRIATE_KEY_USAGE);
1267: }
1268:
1269: certKeyUsage = cert.getExtKeyUsage();
1270: if (extKeyUsage != -1 && certKeyUsage != -1
1271: && (certKeyUsage & extKeyUsage) != extKeyUsage) {
1272: throw new CertificateException(cert,
1273: CertificateException.INAPPROPRIATE_KEY_USAGE);
1274: }
1275:
1276: cert.checkValidity();
1277: }
1278:
1279: /**
1280: * Gets the MD5 fingerprint of this certificate.<BR />
1281: * <b>NOTE:</b> this implementation returns a byte array filled
1282: * with zeros if there is no fingerprint associated with this
1283: * certificate. This may happen if a null was passed to the
1284: * X509Certificate constructor.
1285: * <P />
1286: * @return a byte array containing this certificate's MD5 hash
1287: */
1288: public byte[] getFingerprint() {
1289: byte[] res = new byte[16];
1290: if (fp != null)
1291: System.arraycopy(fp, 0, res, 0, res.length);
1292: return res;
1293: }
1294:
1295: /**
1296: * Gets the name of this certificate's issuer. <BR />
1297: * <B>NOTE:</B> The corresponding method in the standard edition
1298: * is getIssuerDN() and returns a Principal.
1299: * <P />
1300: * @return a string containing this certificate's issuer in
1301: * user-friendly form
1302: */
1303: public String getIssuer() {
1304: return issuer;
1305: }
1306:
1307: /**
1308: * Gets the name of this certificate's subject. <BR />
1309: * <B>NOTE:</B> The corresponding method in the standard edition
1310: * is getSubjectDN() and returns a Principal.
1311: * <P />
1312: * @return a string containing this certificate's subject in
1313: * user-friendly form
1314: */
1315: public String getSubject() {
1316: return subject;
1317: }
1318:
1319: /**
1320: * Gets the NotBefore date from the certificate's validity period.
1321: * <P />
1322: * @return a date before which the certificate is not valid
1323: */
1324: public long getNotBefore() {
1325: return from;
1326: }
1327:
1328: /**
1329: * Gets the NotAfter date from the certificate's validity period.
1330: *
1331: * @return a date after which the certificate is not valid (expiration
1332: * date)
1333: */
1334: public long getNotAfter() {
1335: return until;
1336: }
1337:
1338: /**
1339: * Checks if a certificate has any (version 3) extensions that
1340: * were not properly processed and continued use of this certificate
1341: * may be inconsistent with the issuer's intent. This may happen, for
1342: * example, if the certificate has unrecognized critical extensions.
1343: *
1344: * @exception CertificateException with a reason ofr BAD_EXTENSIONS if
1345: * there are any bad extensions
1346: */
1347: public void checkExtensions() throws CertificateException {
1348: if (badExt) {
1349: throw new CertificateException(this ,
1350: CertificateException.BAD_EXTENSIONS);
1351: }
1352: }
1353:
1354: /**
1355: * Checks if the certificate is currently valid. It is if the
1356: * current date and time are within the certificate's validity
1357: * period.
1358: *
1359: * @exception CertificateException with a reason of
1360: * EXPIRED or NOT_YET_VALID
1361: */
1362: public void checkValidity() throws CertificateException {
1363: checkValidity(System.currentTimeMillis());
1364: }
1365:
1366: /**
1367: * Checks if the certificate is valid on the specified time. It is
1368: * if the specified time is within the certificate's validity
1369: * period. <BR />
1370: * <B>NOTE:</B> The standard edition provides a method with this
1371: * name but it throws different types of exceptions rather than
1372: * returning error codes.
1373: * <P />
1374: * @param time the time in milliseconds for which a certificate's
1375: * validity is to be checked
1376: *
1377: * @exception CertificateException with a reason of
1378: * EXPIRED or NOT_YET_VALID
1379: */
1380: public void checkValidity(long time) throws CertificateException {
1381: if (time < from) {
1382: throw new CertificateException(this ,
1383: CertificateException.NOT_YET_VALID);
1384: }
1385:
1386: if (time > until) {
1387: throw new CertificateException(this ,
1388: CertificateException.EXPIRED);
1389: }
1390: }
1391:
1392: /**
1393: * Get the type of the <CODE>Certificate</CODE>.
1394: * @return The type of the <CODE>Certificate</CODE>;
1395: * the value MUST NOT be <CODE>NULL</CODE>.
1396: */
1397: public String getType() {
1398: return "X.509";
1399: }
1400:
1401: /**
1402: * Gets the public key from this certificate.
1403: * <P />
1404: * @return the public key contained in the certificate
1405: *
1406: * @exception CertificateException if public key is not a supported type
1407: * (could not be parsed).
1408: */
1409: public PublicKey getPublicKey() throws CertificateException {
1410: if (pubKey == null) {
1411: throw new CertificateException(this ,
1412: CertificateException.UNSUPPORTED_PUBLIC_KEY_TYPE);
1413: }
1414:
1415: return pubKey;
1416: }
1417:
1418: /**
1419: * Gets the raw X.509 version number of this certificate. Version 1 is 0.
1420: *
1421: * @return the X.509 logic version number (1, 2, 3) of the certificate
1422: */
1423: public String getVersion() {
1424: return Integer.toString(version);
1425: }
1426:
1427: /**
1428: * Gets the certificate constraints path length from the
1429: * <code>BasicConstraints</code> extension. <P />
1430: *
1431: * The <code>BasicConstraints</code> extension identifies whether the
1432: * subject of the certificate is a Certificate Authority (CA) and how
1433: * deep a certification path may exist through the CA. The
1434: * <code>pathLenConstraint</code> field (see below) is meaningful only
1435: * if <code>cA</code> is set to TRUE. In this case, it gives the maximum
1436: * number of CA certificates that may follow this certificate in a
1437: * certification path. A value of zero indicates that only an end-entity
1438: * certificate may follow in the path. <P />
1439: *
1440: * Note that for RFC 2459 this extension is always marked critical
1441: * if <code>cA</code> is TRUE, meaning this certificate belongs to a
1442: * Certificate Authority. <P />
1443: *
1444: * The ASN.1 definition for this is:
1445: * <PRE>
1446: * BasicConstraints ::= SEQUENCE {
1447: * cA BOOLEAN DEFAULT FALSE,
1448: * pathLenConstraint INTEGER (0..MAX) OPTIONAL
1449: * }
1450: * </PRE>
1451: *
1452: * @return MISSING_PATH_LENGTH_CONSTRAINT if the
1453: * <code>BasicConstraints</code> extension is absent or the subject
1454: * of the certificate is not a CA. If the subject of the certificate
1455: * is a CA and <code>pathLenConstraint</code> does not appear,
1456: * <code>UNLIMITED_CERT_CHAIN_LENGTH</code> is returned to indicate that
1457: * there is no limit to the allowed length of the certification path.
1458: * In all other situations, the actual value of the
1459: * <code>pathLenConstraint</code> is returned.
1460: */
1461: public int getBasicConstraints() {
1462: if (isCA) {
1463: return pLenConstr;
1464: } else {
1465: return MISSING_PATH_LENGTH_CONSTRAINT;
1466: }
1467: }
1468:
1469: /**
1470: * Gets a 32-bit bit vector (in the form of an integer) in which
1471: * each position represents a purpose for which the public key in
1472: * the certificate may be used (iff that bit is set). The correspondence
1473: * between bit positions and purposes is as follows: <BR />
1474: * <TABLE>
1475: * <TR><TD>digitalSignature</TD> <TD>0</TD> </TR>
1476: * <TR><TD>nonRepudiation</TD> <TD>1</TD> </TR>
1477: * <TR><TD>keyEncipherment</TD> <TD>2</TD> </TR>
1478: * <TR><TD>dataEncipherment</TD> <TD>3</TD> </TR>
1479: * <TR><TD>keyAgreement</TD> <TD>4</TD> </TR>
1480: * <TR><TD>keyCertSign</TD> <TD>5</TD> </TR>
1481: * <TR><TD>cRLSign</TD> <TD>6</TD> </TR>
1482: * <TR><TD>encipherOnly</TD> <TD>7</TD> </TR>
1483: * <TR><TD>decipherOnly</TD> <TD>8</TD> </TR>
1484: * </TABLE>
1485: * <P />
1486: * @return a bitvector indicating approved key usage of the certificate
1487: * public key, -1 if a KeyUsage extension is not present.
1488: */
1489: public int getKeyUsage() {
1490: return keyUsage;
1491: }
1492:
1493: /**
1494: * Gets a 32-bit bit vector (in the form of an integer) in which
1495: * each position represents a purpose for which the public key in
1496: * the certificate may be used (iff that bit is set). The correspondence
1497: * between bit positions and purposes is as follows: <BR />
1498: * <TABLE>
1499: * <TR><TD>serverAuth</TD> <TD>1</TD> </TR>
1500: * <TR><TD>clientAuth</TD> <TD>2</TD> </TR>
1501: * <TR><TD>codeSigning</TD> <TD>3</TD> </TR>
1502: * <TR><TD>emailProtection</TD> <TD>4</TD> </TR>
1503: * <TR><TD>ipsecEndSystem</TD> <TD>5</TD> </TR>
1504: * <TR><TD>ipsecTunnel</TD> <TD>6</TD> </TR>
1505: * <TR><TD>ipsecUser</TD> <TD>7</TD> </TR>
1506: * <TR><TD>timeStamping</TD> <TD>8</TD> </TR>
1507: * </TABLE>
1508: * <P />
1509: * @return a bitvector indicating extended usage of the certificate
1510: * public key, -1 if a critical extendedKeyUsage extension is not present.
1511: */
1512: public int getExtKeyUsage() {
1513: return extKeyUsage;
1514: }
1515:
1516: /**
1517: * Gets the type of subject alternative name.
1518: *
1519: * @return type of subject alternative name
1520: */
1521: public int getSubjectAltNameType() {
1522: return subAltNameType;
1523: }
1524:
1525: /**
1526: * Gets the subject alternative name or null if it was not in the
1527: * certificate.
1528: *
1529: * @return type of subject alternative name or null
1530: */
1531: public Object getSubjectAltName() {
1532: return subAltName;
1533: }
1534:
1535: /**
1536: * Gets the printable form of the serial number of this
1537: * <CODE>Certificate</CODE>.
1538: * If the serial number within the <CODE>certificate</CODE>
1539: * is binary is should be formatted as a string using
1540: * hexadecimal notation with each byte represented as two
1541: * hex digits separated byte ":" (Unicode x3A).
1542: * For example, 27:56:FA:80.
1543: * @return A string containing the serial number
1544: * in user-friendly form; <CODE>NULL</CODE> is returned
1545: * if there is no serial number.
1546: */
1547: public String getSerialNumber() {
1548: return serialNumber;
1549: }
1550:
1551: /**
1552: * Checks if this certificate was signed using the private key
1553: * corresponding to the specified public key.
1554: *
1555: * @param k public key to be used for verifying certificate signature
1556: *
1557: * @exception CertificateException if there is an error
1558: */
1559: public void verify(PublicKey k) throws CertificateException {
1560: RSAPublicKey pk;
1561:
1562: if (!(k instanceof RSAPublicKey)) {
1563: throw new CertificateException(
1564: "Issuer key not a public RSA", this ,
1565: CertificateException.VERIFICATION_FAILED);
1566: }
1567:
1568: pk = (RSAPublicKey) k;
1569:
1570: /*
1571: * Since selfSigned certificates are stored without
1572: * TBSCertHash and signature fields (to save memory),
1573: * the only way to return anything meaningful is by
1574: * directly comparing the specified public key against
1575: * the certificate public key. This allows us to return
1576: * the right result even on certificates created using
1577: * the Certificate(...) constructor.
1578: *
1579: * NOTE: We can comment this out to save code here and
1580: * in Key (and its subclasses) -- no need to define equals
1581: * The documentation already warns users not to invoke
1582: * verify() on certificates with null signature.
1583: */
1584: if (selfSigned) {
1585: if (pubKey.equals(pk)) {
1586: return;
1587: }
1588:
1589: throw new CertificateException("Bad self signed cert",
1590: this , CertificateException.VERIFICATION_FAILED);
1591: }
1592:
1593: if (signature == null) {
1594: throw new CertificateException(this ,
1595: CertificateException.MISSING_SIGNATURE);
1596: }
1597:
1598: if (TBSCertHash == null) {
1599: throw new CertificateException(this ,
1600: CertificateException.UNSUPPORTED_SIGALG);
1601: }
1602:
1603: int modLen = pk.getModulusLen();
1604: byte[] result = new byte[modLen];
1605:
1606: int val;
1607:
1608: /*
1609: * NOTE: We can not use the Signature class because, at this
1610: * point, we do not have TBSCertificate (just its hash). The
1611: * Signature class needs raw data and computes a hash internally.
1612: */
1613: try {
1614: Cipher rsa = Cipher.getInstance("RSA");
1615:
1616: rsa.init(Cipher.DECRYPT_MODE, pk);
1617: val = rsa
1618: .doFinal(signature, 0, signature.length, result, 0);
1619: } catch (Exception e) {
1620: throw new CertificateException(this ,
1621: CertificateException.VERIFICATION_FAILED);
1622: }
1623:
1624: /*
1625: * NOTE: the decrypted value includes an ASN DER
1626: * encoding of
1627: * DigestInfo ::= SEQUENCE {
1628: * digestAlgorithm DigestAlgorithmIdentifier,
1629: * digest Digest }
1630: * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
1631: * Digest ::= OCTET STRING
1632: *
1633: * For md2WithRSAEncryption, the decrypted value will be
1634: * 3020300c06082a864886f70d020205000410 followed by a 16-byte hash
1635: *
1636: * For md5WithRSAEncryption, the decrypted value will be
1637: * 3020300c06082a864886f70d020505000410 followed by a 16-byte hash
1638: * 30 20 32: SEQUENCE
1639: * 30 0c 12: . SEQUENCE
1640: * 06 08 8: . . OID 1.2.840.113549.2.5 (MD5 OID, rfc2313 pg 14)
1641: * : 2a 86 48 86 f7 0d 02 05
1642: * 05 00 0: . . NULL (null parameters)
1643: * 04 10 16: . OCTET STRING
1644: * : <the hash gos here>
1645: *
1646: * Similarly, for SHA-1, the 20-byte hash will be preceded by
1647: * 3021300906052b0e03021a05000414
1648: * 30 21 33: SEQUENCE
1649: * 30 09 9: . SEQUENCE
1650: * 06 05 5: . . OID 1.3.14.3.2.26 (SHA-1 digest OID)
1651: * 0: 2b 0e 03 02 1a
1652: * 05 00 0: . . NULL (null parameters)
1653: * 04 14 20: . <20-byte hash>
1654: */
1655: if ((sigAlg == MD2_RSA)
1656: && (val == (PREFIX_MD2.length + TBSCertHash.length))
1657: && Utils.byteMatch(result, 0, PREFIX_MD2, 0,
1658: PREFIX_MD2.length)
1659: && Utils.byteMatch(result, PREFIX_MD2.length,
1660: TBSCertHash, 0, TBSCertHash.length)) {
1661: return;
1662: }
1663:
1664: if ((sigAlg == MD5_RSA)
1665: && (val == (PREFIX_MD5.length + TBSCertHash.length))
1666: && Utils.byteMatch(result, 0, PREFIX_MD5, 0,
1667: PREFIX_MD5.length)
1668: && Utils.byteMatch(result, PREFIX_MD5.length,
1669: TBSCertHash, 0, TBSCertHash.length)) {
1670: return;
1671: }
1672:
1673: if ((sigAlg == SHA1_RSA)
1674: && (val == (PREFIX_SHA1.length + TBSCertHash.length))
1675: && Utils.byteMatch(result, 0, PREFIX_SHA1, 0,
1676: PREFIX_SHA1.length)
1677: && Utils.byteMatch(result, PREFIX_SHA1.length,
1678: TBSCertHash, 0, TBSCertHash.length)) {
1679: return;
1680: }
1681:
1682: throw new CertificateException(this ,
1683: CertificateException.VERIFICATION_FAILED);
1684: }
1685:
1686: /**
1687: * Gets the name of the algorithm used to sign the certificate.
1688: * <P />
1689: * @return the name of signature algorithm
1690: */
1691: public String getSigAlgName() {
1692: /*
1693: * These are ordered to maximize the likelihood of an
1694: * early match, md5WithRSA seems the most common
1695: */
1696: if (sigAlg == MD5_RSA)
1697: return ("MD5withRSA");
1698: else if (sigAlg == MD2_RSA)
1699: return ("MD2withRSA");
1700: else if (sigAlg == SHA1_RSA)
1701: return ("SHA1withRSA");
1702: else if (sigAlg == NONE)
1703: return ("None");
1704: else if (sigAlg == MD4_RSA)
1705: return ("MD4withRSA");
1706: else
1707: return ("Unknown (" + sigAlg + ")");
1708: }
1709:
1710: /** Array of purpose strings describing key usage role. */
1711: private static final String[] KEY_USAGE = { "digitalSignature", // 0
1712: "nonRepudiation", // 1
1713: "keyEncipherment", // 2
1714: "dataEncipherment", // 3
1715: "keyAgreement", // 4
1716: "keyCertSign", // 5
1717: "cRLSign", // 6
1718: "encipherOnly", // 7
1719: "decipherOnly", // 8
1720: // below are for the extended key usage extension
1721: "9", "10", "11", "12", "13", "14", "15", "16", // 9-16
1722: "serverAuth", // 17
1723: "clientAuth", // 18
1724: "codeSigning", // 19
1725: "emailProtection", // 20
1726: "ipsecEndSystem", // 21
1727: "ipsecTunnel", // 22
1728: "ipsecUser", // 23
1729: "timeStamping" // 24
1730: };
1731:
1732: /**
1733: * Converts a Date object to a string containing the corresponding
1734: * date.<br />
1735: * <b>NOTE:</b> This is here only because the J2ME date class does not
1736: * implement toString() in any meaningful way.
1737: * <p />
1738: * @param date Date object to be converted
1739: * @return a string representation of the Date object in
1740: * the form "month/day/year hour:min:sec"
1741: */
1742: private static String date2str(Date date) {
1743: Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
1744: c.setTime(date);
1745: String d = (c.get(Calendar.MONTH) + 1) + "/"
1746: + c.get(Calendar.DAY_OF_MONTH) + "/"
1747: + c.get(Calendar.YEAR) + " "
1748: + c.get(Calendar.HOUR_OF_DAY) + ":"
1749: + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND);
1750: return d;
1751: }
1752:
1753: /**
1754: * Returns a string representation of this certificate.
1755: * <p />
1756: * @return a human readable string repesentation of this certificate
1757: */
1758: public String toString() {
1759: StringBuffer tmp = new StringBuffer();
1760:
1761: tmp.append("[Type: ");
1762: tmp.append(getType());
1763: tmp.append("v");
1764: tmp.append(version);
1765:
1766: tmp.append("\n");
1767: tmp.append("Serial number: ");
1768: tmp.append(serialNumber);
1769:
1770: tmp.append("\n");
1771: tmp.append("Subject: ");
1772: tmp.append(subject);
1773:
1774: tmp.append("\n");
1775: tmp.append("Issuer: ");
1776: tmp.append(issuer);
1777:
1778: tmp.append("\n");
1779: tmp.append("Valid from ");
1780: tmp.append(date2str(new Date(getNotBefore())));
1781: tmp.append(" GMT until ");
1782: tmp.append(date2str(new Date(getNotAfter())));
1783: tmp.append(" GMT");
1784:
1785: // tmp.append("\n");
1786: // tmp.append(pubKey.toString());
1787:
1788: // tmp.append("\n");
1789: // tmp.append(TBSCertificate hash: ");
1790: // tmp.append(TBSCertHash == null ?
1791: // "null" : Utils.hexEncode(TBSCertHash));
1792:
1793: tmp.append("\n");
1794: tmp.append("Signature Algorithm: ");
1795: tmp.append(getSigAlgName());
1796:
1797: if (subAltName != null) {
1798: tmp.append("\n");
1799: tmp.append("SubjectAltName: ");
1800: tmp.append(subAltName);
1801: tmp.append("(type ");
1802: tmp.append(subAltNameType);
1803: tmp.append(")");
1804: }
1805:
1806: if (keyUsage != -1) {
1807: tmp.append("\n");
1808: tmp.append("KeyUsage:");
1809: int t = (int) keyUsage;
1810: for (int i = 0; i < KEY_USAGE.length; i++) {
1811: if ((t & 0x01) == 0x01) {
1812: tmp.append(" ");
1813: tmp.append(KEY_USAGE[i]);
1814: }
1815:
1816: t = t >>> 1;
1817: }
1818: }
1819:
1820: if (hasBC) {
1821: tmp.append("\n");
1822: tmp.append("BasicConstraints: ");
1823: tmp.append(isCA ? "is a CA" : "not a CA");
1824: tmp.append(" (pathLengthConstraint ");
1825: if ((pLenConstr == MISSING_PATH_LENGTH_CONSTRAINT)
1826: || (pLenConstr == UNLIMITED_CERT_CHAIN_LENGTH)) {
1827: tmp.append("absent");
1828: } else {
1829: tmp.append(pLenConstr);
1830: }
1831:
1832: tmp.append(")");
1833: }
1834:
1835: // tmp.append("\n");
1836: // tmp.append("MD5 Fingerprint: ");
1837: // tmp.append(Utils.hexEncode(fp));
1838: tmp.append("]");
1839: return tmp.toString();
1840: }
1841: }
|