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