X509Certificatepublic class X509Certificate extends Object implements CertificateThis class implements methods for creating X.509 certificates and
accessing their attributes such as subject/issuer names, public keys
and validity information. Publicly visible methods methods are
modeled after those in the X509Certificate classes
from J2SE (standard edition) but there are some differences and
these are documented below.
NOTE: For now, only X.509 certificates containing RSA public keys
and signed either using md2WithRSA, md5WithRSA, or sha-1WithRSA are
supported.
This version of the implementation is unable to parse certificates
containing DSA keys or signed using DSA. Certificates containing
RSA keys but signed using an unsupported algorithm
can be parsed but cannot be verified. Not all version 3 extensions are
supported (only subjectAltName, basicConstraints, keyUsage and
extendedKeyUsage are recognized) but if an unrecognized
extension is marked critical, an error notification is generated.
|
Fields Summary |
---|
public static final byte | NO_ERRORIndicates a no error condition. | public static final int | MISSING_PATH_LENGTH_CONSTRAINTIndicates that no information is available on
the pathLengthConstraint associated with this certificate
(this could happen if the certifiate is a v1 or v2 cert or
a v3 cert without basicConstraints or a non-CA v3 certificate). | public static final int | UNLIMITED_CERT_CHAIN_LENGTHIndicates there is no limit to the server certificate chain length. | private static final int | MAX_NAME_LENGTHWe expect issuer/subject names to fit within these many bytes. | private static final byte | ANY_STRING_TYPEASN ANY_STRING type used in certificate parsing (0x00). | private static final byte | INTEGER_TYPEASN INTEGER type used in certificate parsing (0x02). | private static final byte | BITSTRING_TYPEASN BIT STRING type used in certificate parsing (0x03). | private static final byte | OCTETSTR_TYPEASN OCTET STRING type used in certificate parsing (0x04). | private static final byte | OID_TYPEASN OBJECT ID type used in certificate parsing (0x06). | private static final byte | UTF8STR_TYPEASN UTF8 STRING type used in certificate parsing (0x0c). | private static final byte | UNIVSTR_TYPEASN UNICODE STRING type used in certificate parsing (0x12). | private static final byte | PRINTSTR_TYPEASN PRINT STRING type used in certificate parsing (0x13). | private static final byte | TELETEXSTR_TYPEASN TELETEX STRING type used in certificate parsing (0x14). | private static final byte | IA5STR_TYPEASN IA5 STRING type used in certificate parsing (0x16). | private static final byte | SEQUENCE_TYPEASN SEQUENCE type used in certificate parsing (0x30). | private static final byte | SET_TYPEASN SET type used in certificate parsing (0x31). | public static final byte | TYPE_EMAIL_ADDRESSEmail address (rfc 822) alternative name type code. | public static final byte | TYPE_DNS_NAMEDNS name alternative name type code. | public static final byte | TYPE_URIURI alternative name type code. | public static final int | DIGITAL_SIG_KEY_USAGEBit mask for digital signature key usage. | public static final int | NON_REPUDIATION_KEY_USAGEBit mask for non repudiation key usage. | public static final int | KEY_ENCIPHER_KEY_USAGEBit mask for key encipherment key usage. | public static final int | DATA_ENCIPHER_KEY_USAGEBit mask for data encipherment key usage. | public static final int | KEY_AGREEMENT_KEY_USAGEBit mask for key agreement key usage. | public static final int | CERT_SIGN_KEY_USAGEBit mask for key certificate sign key usage. | public static final int | CRL_SIGN_KEY_USAGEBit mask for CRL sign key usage. | public static final int | ENCIPHER_ONLY_KEY_USAGEBit mask for encipher only key usage. | public static final int | DECIPHER_ONLY_KEY_USAGEBit mask for decipher only key usage. | public static final int | SERVER_AUTH_EXT_KEY_USAGEBit mask server auth for extended key usage. | public static final int | CLIENT_AUTH_EXT_KEY_USAGEBit mask client auth for extended key usage. | public static final int | CODE_SIGN_EXT_KEY_USAGEBit code signing mask for extended key usage. | public static final int | EMAIL_EXT_KEY_USAGEBit email protection mask for extended key usage. | public static final int | IPSEC_END_SYS_EXT_KEY_USAGEBit IPSEC end system mask for extended key usage. | public static final int | IPSEC_TUNNEL_EXT_KEY_USAGEBit IPSEC tunnel mask for extended key usage. | public static final int | IPSEC_USER_EXT_KEY_USAGEBit IPSEC user mask for extended key usage. | public static final int | TIME_STAMP_EXT_KEY_USAGEBit time stamping mask for extended key usage. | private static final int | UTC_LENGTHThe validity period is contained in thirteen bytes
yymmddhhmmss followed by 'Z' (for zulu ie GMT), if yy < 50
assume 20yy else 19yy. | private static final char[] | nameAttrMaps byte codes that follow id-at (0x55 0x04) to corresponding name
component tags (e.g. Commom Name, or CN, is 0x55, 0x04, 0x03 and
Country, or C, is 0x55, 0x04, 0x06). See getName. See X.520 for
the OIDs and RFC 1779 for the printable labels. Place holders for
unknown labels have a 0 as the first char. | private static final char[] | EMAIL_ATTR_LABELEmail attribute label in bytes. "EmailAddress" | private static final byte[] | EMAIL_ATTR_OIDEmail attribute object identifier. | private static final byte[] | PKCS1SeqIncludes DER encoding for OID 1.2.840.113549.1.1. | private static final byte | NONEUknown algorithm (-1). | private static final byte | RSA_ENCRYPTIONRAS ENCRYPTION (0x01). | private static final byte | MD2_RSAMD2_RSA algorithm (0x02). | private static final byte | MD4_RSAMD4_RSA algorithm (0x03). | private static final byte | MD5_RSAMD4_RSA algorithm (0x04). | private static final byte | SHA1_RSASHA1_RSA algorithm (0x05). | private static final byte[] | PREFIX_MD2Expected prefix in decrypted value when MD2 hash is used for signing
30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04 10 see verify(). | private static final byte[] | PREFIX_MD5Expected prefix in decrypted value when MD5 hash is used for signing
30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 see verify(). | private static final byte[] | PREFIX_SHA1Expected prefix in decrypted value when SHA-1 hash is used for signing
30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14. | private static final byte[] | NullSeqASN encoding for NULL. | private static final byte[] | ValiditySeqThis is how the encoding of validity information begins. | private static final byte[] | UTCSeqThis is how the encoding of UTCTime begins. | private static final byte[] | ID_KPIncludes DER encoding for id-kp (key purpose). | private boolean | selfSignedTrue iff subject matches issuer. | private byte | versionX.509 version. For more readable code the version field starts a 1. | private byte[] | fpMD5 fingerprint of the certificate. | private String | serialNumberCertificate serial number. | private String | subjectCertificate subject. | private String | issuerCertificate issuer. | private long | fromBeginning of certificate validity period. | private long | untilEnd of certificate validity period. | private RSAPublicKey | pubKeyCertificate RSA Public key. | private int | idxIndex inside encoding. | private byte[] | encContains Certificate DER encoding. | private int | TBSStartOffset where TBSCertificate starts. | private int | TBSLenLength of TBSCertificate. | private byte | sigAlgAlgorithm used to sign the cert. | private byte[] | signatureIssuer signature on certificate. | private byte[] | TBSCertHashHash of TBSCertificate. | private boolean | badExtTrue iff cert has unrecognized critical extension. | private byte | subAltNameTypeformat of the subject alternative name, 2 means a DNS name | private Object | subAltNamesubject alternative name | private boolean | hasBCdoes the cert include BasicConstaints. | private boolean | isCACA value in BasicConstraints. | private int | pLenConstrPath Length constriant from Basic constraints. | private int | keyUsageCollection of keyUsage bits. | private int | extKeyUsageCollection of extended keyUsage bits. | private static final String[] | KEY_USAGEArray of purpose strings describing key usage role. |
Constructors Summary |
---|
private X509Certificate()Private constructor
| public X509Certificate(byte ver, byte[] rawSerialNumber, String sub, String iss, long notBefore, long notAfter, byte[] mod, byte[] exp, byte[] chash, int pLen)Creates an X.509 certificate with the specified attributes.
This constructor is only used for creating trusted certificates.
NOTE: All signature related values in these certificates
(such as the signing algorithm and signature) are set to null and
invoking methods that access signature information, e.g. verify()
and getSigAlgName() can produce unexpected errors.
version = ver;
serialNumber = Utils.hexEncode(rawSerialNumber, 0,
rawSerialNumber.length);
/*
* We are paranoid so we don't just assign a reference as in
* fp = chash; subject = sub; issuer = iss;
*/
if (chash != null) {
fp = new byte[chash.length];
System.arraycopy(chash, 0, fp, 0, chash.length);
}
subject = new String(sub);
issuer = new String(iss);
from = notBefore;
until = notAfter;
sigAlg = NONE;
if (subject.compareTo(issuer) == 0) {
selfSigned = true;
}
pubKey = new RSAPublicKey(mod, exp);
if ((ver == 3) && (pLen != MISSING_PATH_LENGTH_CONSTRAINT)) {
hasBC = isCA = true;
pLenConstr = pLen;
}
|
Methods Summary |
---|
public void | checkExtensions()Checks if a certificate has any (version 3) extensions that
were not properly processed and continued use of this certificate
may be inconsistent with the issuer's intent. This may happen, for
example, if the certificate has unrecognized critical extensions.
if (badExt) {
throw new CertificateException(this,
CertificateException.BAD_EXTENSIONS);
}
| private static void | checkKeyUsageAndValidity(com.sun.midp.pki.X509Certificate cert, int keyUsage, int extKeyUsage)Check the key usage, extended key usage and validity of a certificate.
int certKeyUsage;
// Check if this certificate has any bad extensions
cert.checkExtensions();
certKeyUsage = cert.getKeyUsage();
if (keyUsage != -1 && certKeyUsage != -1 &&
(certKeyUsage & keyUsage) != keyUsage) {
throw new CertificateException(cert,
CertificateException.INAPPROPRIATE_KEY_USAGE);
}
certKeyUsage = cert.getExtKeyUsage();
if (extKeyUsage != -1 && certKeyUsage != -1 &&
(certKeyUsage & extKeyUsage) != extKeyUsage) {
throw new CertificateException(cert,
CertificateException.INAPPROPRIATE_KEY_USAGE);
}
cert.checkValidity();
| public void | checkValidity()Checks if the certificate is currently valid. It is if the
current date and time are within the certificate's validity
period.
checkValidity(System.currentTimeMillis());
| public void | checkValidity(long time)Checks if the certificate is valid on the specified time. It is
if the specified time is within the certificate's validity
period.
NOTE: The standard edition provides a method with this
name but it throws different types of exceptions rather than
returning error codes.
if (time < from) {
throw new CertificateException(this,
CertificateException.NOT_YET_VALID);
}
if (time > until) {
throw new CertificateException(this,
CertificateException.EXPIRED);
}
| private static java.lang.String | date2str(java.util.Date date)Converts a Date object to a string containing the corresponding
date.
NOTE: This is here only because the J2ME date class does not
implement toString() in any meaningful way.
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
c.setTime(date);
String d = (c.get(Calendar.MONTH) + 1) + "/" +
c.get(Calendar.DAY_OF_MONTH) + "/" +
c.get(Calendar.YEAR) + " " +
c.get(Calendar.HOUR_OF_DAY) + ":" +
c.get(Calendar.MINUTE) + ":" +
c.get(Calendar.SECOND);
return d;
| public static com.sun.midp.pki.X509Certificate | generateCertificate(byte[] buf, int off, int len)Creates a certificate by parsing the ASN.1 DER X.509 certificate
encoding in the specified buffer.
NOTE: In the standard edition, equivalent functionality
is provided by CertificateFactory.generateCertificate(InputStream).
/*
* force bad parameter errors now, so later we can consider any out of
* bounds errors to be parsing errors
*/
int test = buf[off] + buf[len - 1] + buf[off + len - 1];
try {
int start = 0;
int size = 0;
byte[] hash = new byte[16]; // for MD5 fingerprint
X509Certificate res = null;
int publicKeyLen;
int publicKeyPos;
int modulusPos;
int modulusLen;
int exponentPos;
int exponentLen;
// Compute the MD5 fingerprint
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(buf, off, len);
md.digest(hash, 0, hash.length);
/*
* Create a new certificate and fill its attributes by parsing
* the DER encoding
*/
res = new X509Certificate();
// Prepare to parse this certificate
res.idx = 0;
// Set the encoding
res.enc = new byte[len];
System.arraycopy(buf, off, res.enc, 0, len);
// ... and the fingerprint
res.fp = new byte[hash.length];
System.arraycopy(hash, 0, res.fp, 0, hash.length);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"-------- Begin Certificate -------");
}
/*
* A Certificate is a sequence of a TBSCertificate, a signature
* algorithm identifier and the signature
*/
res.getLen(SEQUENCE_TYPE);
// Now read the TBS certificate
res.TBSStart = res.idx;
size = res.getLen(SEQUENCE_TYPE);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"-------- Begin TBSCertificate -------");
}
int sigAlgIdx = res.idx + size;
res.TBSLen = sigAlgIdx - res.TBSStart;
// Now parse the version
if ((res.enc[res.idx] & 0xf0) == 0xa0) {
res.idx++;
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_SECURITY,
"Version info: " +
Utils.hexEncode(res.enc, (res.idx + 1),
res.enc[res.idx]));
}
size = (res.enc[res.idx++] & 0xff);
if (res.idx + size > res.enc.length) {
throw new IOException("Version info too long");
}
res.version = (byte)(res.enc[res.idx + (size - 1)]);
res.idx += size;
} else {
res.version = 1; // No explicit version value
}
// Expect the serial number coded as an integer
size = res.getLen(INTEGER_TYPE);
res.serialNumber = Utils.hexEncode(res.enc, res.idx, size);
res.idx += size;
// Expect the signature AlgorithmIdentifier
byte id = res.getAlg();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"Algorithm Id: " + id);
}
// Expect the issuer name
start = res.idx;
size = res.getLen(SEQUENCE_TYPE);
int end = res.idx + size;
try {
res.issuer = res.getName(end);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"Issuer: " + res.issuer);
}
} catch (Exception e) {
throw new IOException("Could not parse issuer name");
}
// Validity is a sequence of two UTCTime values
try {
res.match(ValiditySeq);
// get start time
res.match(UTCSeq);
res.from = getUTCTime(res.enc, res.idx);
res.idx += UTC_LENGTH;
// get end time
res.match(UTCSeq);
res.until = getUTCTime(res.enc, res.idx);
res.idx += UTC_LENGTH;
} catch (Exception e) {
throw new IOException("Could not parse validity information"
+ "caught " + e);
}
// Expect the subject name
start = res.idx;
size = res.getLen(SEQUENCE_TYPE);
end = res.idx + size;
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"Subject: " +
Utils.hexEncode(res.enc, start, size));
}
if (size != 0) {
try {
res.subject = res.getName(end);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_SECURITY,
"Subject: " + res.subject);
}
} catch (Exception e) {
throw new IOException("Could not parse subject name");
}
} // NOTE: the subject can be null (empty sequence) if
// subjectAltName is present
// Parse the subject public key information
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"SubjectPublicKeyInfo follows");
}
publicKeyLen = res.getLen(SEQUENCE_TYPE);
publicKeyPos = res.idx;
// Match the algorithm Id
id = res.getAlg();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"Public Key Algorithm: " + id);
}
if (id != RSA_ENCRYPTION) {
// skip the public key
res.idx = publicKeyPos + publicKeyLen;
}
// Get the bit string
res.getLen(BITSTRING_TYPE);
if (res.enc[res.idx++] != 0x00) {
throw new IOException("Bitstring error while parsing public " +
"key information");
}
res.getLen(SEQUENCE_TYPE);
size = res.getLen(INTEGER_TYPE);
if (res.enc[res.idx] == (byte) 0x00) {
// strip off the sign byte
size--;
res.idx++;
}
// Build the RSAPublicKey
modulusPos = res.idx;
modulusLen = size;
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"Modulus: " +
Utils.hexEncode(res.enc, modulusPos,
modulusLen));
}
res.idx += size;
size = res.getLen(INTEGER_TYPE);
if (res.enc[res.idx] == (byte) 0x00) {
// strip off the sign byte
size--;
res.idx++;
}
exponentPos = res.idx;
exponentLen = size;
res.pubKey = new RSAPublicKey(res.enc, modulusPos, modulusLen,
res.enc, exponentPos, exponentLen);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"Exponent: " +
Utils.hexEncode(res.enc, exponentPos,
exponentLen));
}
res.idx += size;
if (res.idx != sigAlgIdx) {
if (res.version < 3) {
throw new IOException(
"Unexpected extensions in old version cert");
} else {
res.parseExtensions(sigAlgIdx);
}
}
// get the signatureAlgorithm
res.sigAlg = res.getAlg();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"Signature Algorithm: " +
res.getSigAlgName());
}
/*
* If this is a supported signature algorithm, compute and save
* the hash of TBSCertificate. A null TBSCertHash indicates
* the use of an unsupported signature algorithm (see verify())
*/
md = null;
if (res.sigAlg == MD2_RSA) {
md = MessageDigest.getInstance("MD2");
} else if (res.sigAlg == MD5_RSA) {
md = MessageDigest.getInstance("MD5");
} else if (res.sigAlg == SHA1_RSA) {
md = MessageDigest.getInstance("SHA-1");
}
if (md != null) {
res.TBSCertHash = new byte[md.getDigestLength()];
md.update(buf, off + res.TBSStart, res.TBSLen);
md.digest(res.TBSCertHash, 0, res.TBSCertHash.length);
}
// get the signature
size = res.getLen(BITSTRING_TYPE);
if (res.enc[res.idx++] != 0x00) {
throw new IOException("Bitstring error in signature parsing");
}
/*
* We pad the signature to a multiple of 8-bytes before storing
* since we only support RSA modulus lengths that are multiples
* of 8 bytes and the two should match for decryption to succeed.
*/
int sigLen = (((size - 1) + 7) >>> 3) << 3;
res.signature = new byte[sigLen];
System.arraycopy(res.enc, res.idx, res.signature,
(sigLen - (size - 1)), (size - 1));
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
sigLen + "-byte signature: " +
Utils.hexEncode(res.signature));
}
return res;
} catch (IndexOutOfBoundsException e) {
throw new IOException("Bad length detected in cert DER");
} catch (GeneralSecurityException e) {
throw new IOException(e.toString());
}
| private byte | getAlg()Expects to see a PKCS1 algorithm identifier in the DER encoding
(enc) starting at the current offset (idx).
byte val;
try {
match(PKCS1Seq);
val = enc[idx++];
match(NullSeq);
return val;
} catch (Exception e) {
throw new IOException("Algorithm Id parsing failed");
}
| public int | getBasicConstraints()Gets the certificate constraints path length from the
BasicConstraints extension.
The BasicConstraints extension identifies whether the
subject of the certificate is a Certificate Authority (CA) and how
deep a certification path may exist through the CA. The
pathLenConstraint field (see below) is meaningful only
if cA is set to TRUE. In this case, it gives the maximum
number of CA certificates that may follow this certificate in a
certification path. A value of zero indicates that only an end-entity
certificate may follow in the path.
Note that for RFC 2459 this extension is always marked critical
if cA is TRUE, meaning this certificate belongs to a
Certificate Authority.
The ASN.1 definition for this is:
BasicConstraints ::= SEQUENCE {
cA BOOLEAN DEFAULT FALSE,
pathLenConstraint INTEGER (0..MAX) OPTIONAL
}
if (isCA) {
return pLenConstr;
} else {
return MISSING_PATH_LENGTH_CONSTRAINT;
}
| public int | getExtKeyUsage()Gets a 32-bit bit vector (in the form of an integer) in which
each position represents a purpose for which the public key in
the certificate may be used (iff that bit is set). The correspondence
between bit positions and purposes is as follows:
serverAuth | 1 |
clientAuth | 2 |
codeSigning | 3 |
emailProtection | 4 |
ipsecEndSystem | 5 |
ipsecTunnel | 6 |
ipsecUser | 7 |
timeStamping | 8 |
return extKeyUsage;
| public byte[] | getFingerprint()Gets the MD5 fingerprint of this certificate.
NOTE: this implementation returns a byte array filled
with zeros if there is no fingerprint associated with this
certificate. This may happen if a null was passed to the
X509Certificate constructor.
byte[] res = new byte[16];
if (fp != null) System.arraycopy(fp, 0, res, 0, res.length);
return res;
| public java.lang.String | getIssuer()Gets the name of this certificate's issuer.
NOTE: The corresponding method in the standard edition
is getIssuerDN() and returns a Principal.
return issuer;
| public int | getKeyUsage()Gets a 32-bit bit vector (in the form of an integer) in which
each position represents a purpose for which the public key in
the certificate may be used (iff that bit is set). The correspondence
between bit positions and purposes is as follows:
digitalSignature | 0 |
nonRepudiation | 1 |
keyEncipherment | 2 |
dataEncipherment | 3 |
keyAgreement | 4 |
keyCertSign | 5 |
cRLSign | 6 |
encipherOnly | 7 |
decipherOnly | 8 |
return keyUsage;
| private int | getLen(byte type)Matches the specified ASN type against this certificates DER
encoding (enc) starting at the current offset (idx) and returns
its encoded length.
if ((enc[idx] == type) ||
((type == ANY_STRING_TYPE) && // ordered by likelihood of match
((enc[idx] == PRINTSTR_TYPE) || (enc[idx] == TELETEXSTR_TYPE) ||
(enc[idx] == UTF8STR_TYPE) || (enc[idx] == IA5STR_TYPE) ||
(enc[idx] == UNIVSTR_TYPE)))) {
idx++;
int size = (enc[idx++] & 0xff);
if (size >= 128) {
int tmp = size - 128;
// NOTE: for now, all sizes must fit int two bytes
if ((tmp > 2) || (idx + tmp > enc.length)) {
throw new IOException("getLen() err 1");
} else {
size = 0;
while (tmp > 0) {
size = (size << 8) + (enc[idx++] & 0xff);
tmp--;
}
}
}
return size;
}
throw new IOException("getLen() err 2");
| private java.lang.String | getName(int end)Parses a SubjectName or IssuerName in the DER encoding
(enc) starting at the current offset (idx) and ending
at end.
byte[] name = new byte[MAX_NAME_LENGTH];
int nameLen = 0;
int len = 0;
int cidx; // index where the most recently seen name component starts
int clen; // Component length
char[] label = null;
int aidx;
while (idx < end) {
if (nameLen != 0) {
// this is not the first time so insert a separator
name[nameLen++] = (byte)';";
}
getLen(SET_TYPE);
getLen(SEQUENCE_TYPE);
/*
* Save the start of name component, e.g CommonName
* ... and its length
*/
clen = getLen(OID_TYPE);
cidx = idx;
idx += clen;
/*
* At this point we tag the name component, e.g. C= or hex
* if unknown.
*/
if ((clen == 3) && (enc[cidx] == 0x55) &&
(enc[cidx + 1] == 0x04)) {
// begins with id-at, so try to see if we have a label
aidx = enc[cidx + 2] & 0xFF;
if ((aidx < nameAttr.length) && (nameAttr[aidx][0] != 0)) {
label = nameAttr[aidx];
} else {
label = Utils.hexEncodeToChars(enc, cidx, clen);
}
} else if (Utils.byteMatch(enc, cidx, EMAIL_ATTR_OID, 0,
EMAIL_ATTR_OID.length)) {
label = EMAIL_ATTR_LABEL;
} else {
label = Utils.hexEncodeToChars(enc, cidx, clen);
}
for (int i = 0; i < label.length; i++) {
name[nameLen++] = (byte)label[i];
}
name[nameLen++] = (byte)'=";
len = getLen(ANY_STRING_TYPE);
if (len > 0) {
for (int i = 0; i < len; i++) {
name[nameLen++] = enc[idx++];
}
}
}
return new String(name, 0, nameLen, "UTF-8");
| public long | getNotAfter()Gets the NotAfter date from the certificate's validity period.
return until;
| public long | getNotBefore()Gets the NotBefore date from the certificate's validity period.
return from;
| public PublicKey | getPublicKey()Gets the public key from this certificate.
if (pubKey == null) {
throw new CertificateException(this,
CertificateException.UNSUPPORTED_PUBLIC_KEY_TYPE);
}
return pubKey;
| public java.lang.String | getSerialNumber()Gets the printable form of the serial number of this
Certificate .
If the serial number within the certificate
is binary is should be formatted as a string using
hexadecimal notation with each byte represented as two
hex digits separated byte ":" (Unicode x3A).
For example, 27:56:FA:80.
return serialNumber;
| public java.lang.String | getSigAlgName()Gets the name of the algorithm used to sign the certificate.
/*
* These are ordered to maximize the likelihood of an
* early match, md5WithRSA seems the most common
*/
if (sigAlg == MD5_RSA)
return ("MD5withRSA");
else if (sigAlg == MD2_RSA)
return ("MD2withRSA");
else if (sigAlg == SHA1_RSA)
return ("SHA1withRSA");
else if (sigAlg == NONE)
return ("None");
else if (sigAlg == MD4_RSA)
return ("MD4withRSA");
else
return ("Unknown (" + sigAlg + ")");
| public java.lang.String | getSubject()Gets the name of this certificate's subject.
NOTE: The corresponding method in the standard edition
is getSubjectDN() and returns a Principal.
return subject;
| public java.lang.Object | getSubjectAltName()Gets the subject alternative name or null if it was not in the
certificate.
return subAltName;
| public int | getSubjectAltNameType()Gets the type of subject alternative name.
return subAltNameType;
| public java.lang.String | getType()Get the type of the Certificate .
return "X.509";
| private static long | getUTCTime(byte[] buf, int off)Gets a string representation of the UTC time whose DER ecnoding
is contained in the specified buffer.
Calendar cal;
int[] period = new int[6]; // year, month, day, hour, minute, second
if (buf[off + UTC_LENGTH - 1] != (byte) 'Z")
throw new IOException("getUTCTime() err 1");
for (int i = 0; i < 6; i++) {
period[i] = 0;
if ((buf[2*i + off] < (byte) '0") ||
(buf[2*i + off] > (byte) '9"))
throw new IOException("getUTCTime() err 2");
period[i] = buf[2*i + off] - (int) '0";
if ((buf[2*i + off + 1] < (byte) '0") ||
(buf[2*i + off + 1] > (byte) '9"))
throw new IOException("getUTCTime() err 3");
period[i] = (period[i] * 10) + (buf[2*i + off + 1] - (int) '0");
}
if (period[0] < 50) { // from rfc2459
period[0] += 2000;
} else {
period[0] += 1900;
}
cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.set(Calendar.YEAR, period[0]);
cal.set(Calendar.MONTH, period[1] - 1); // months go 0-11
cal.set(Calendar.DAY_OF_MONTH, period[2]);
cal.set(Calendar.HOUR_OF_DAY, period[3]);
cal.set(Calendar.MINUTE, period[4]);
cal.set(Calendar.SECOND, period[5]);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime().getTime();
| public java.lang.String | getVersion()Gets the raw X.509 version number of this certificate. Version 1 is 0.
return Integer.toString(version);
| private void | match(byte[] buf)Matches the contents of buf against this certificates DER
encoding (enc) starting at the current offset (idx).
if (idx + buf.length < enc.length) {
for (int i = 0; i < buf.length; i++) {
if (enc[idx++] != buf[i])
throw new Exception("match() error 1");
}
} else {
throw new Exception("match() error 2");
}
| private void | parseExtensions(int end)Parses X.509v3 extensions in the certificate encoding until
the specified index.
/*
* NOTE: If one does not wish to support v3 extensions
* at all (to save code), one can simply set badExt to
* true and return -- the code that actually parses extensions
* can be commented out
*/
String extId = null;
int extIdIdx = 0;
int extIdLen = 0;
boolean crit;
int extValIdx = 0;
int extValLen = 0;
int tmp;
getLen((byte) 0xa3); // extensions start with 0xa3
getLen(SEQUENCE_TYPE);
while (idx < end) {
extId = null;
getLen(SEQUENCE_TYPE);
extIdLen = getLen(OID_TYPE);
extIdIdx = idx;
idx += extIdLen;
crit = false;
if ((enc[idx] == 0x01) && (enc[idx + 1] == 0x01)) {
idx += 2;
crit = (enc[idx++] == (byte) 0xff) ? true : false;
}
extValLen = getLen(OCTETSTR_TYPE);
extValIdx = idx;
if ((enc[extIdIdx] == 0x55) && (enc[extIdIdx + 1] == 0x1d)) {
// Do we recognize this? NOTE: id-ce is 0x55, 0x1d
switch (enc[extIdIdx + 2] & 0xff) {
case 0x0f: // keyUsage = id-ce 15
extId = "KU";
if (keyUsage == -1) {
keyUsage = 0;
}
tmp = getLen(BITSTRING_TYPE) - 1;
int unused = enc[idx++]; // get unused bits in last octet
byte b = 0;
// process each bit in the bitstring starting with
// the most significant
for (int i = 0; i < ((tmp << 3) - unused); i++) {
if ((i % 8) == 0) {
b = enc[idx++];
}
if (b < 0) {
keyUsage |= 1 << i;
}
b = (byte) (b << 1);
}
break;
case 0x11: // subAltName = id-ce 17
StringBuffer temp = new StringBuffer();
int start = idx + 4;
int length = extValLen - 4;
extId = "SAN";
/*
* First byte stores the type e.g. 1=rfc822Name(email),
* 2=dNSName, 6=URI etc
*/
subAltNameType = (byte) (enc[idx + 2] - 0x80);
switch (subAltNameType) {
case TYPE_EMAIL_ADDRESS:
case TYPE_DNS_NAME:
case TYPE_URI:
for (int i = 0; i < length; i++) {
temp.append((char)enc[start + i]);
}
subAltName = temp.toString();
break;
default:
subAltName = new byte[length];
for (int i = 0; i < length; i++) {
((byte[])subAltName)[i] = enc[start + i];
}
}
break;
case 0x13: // basicConstr = id-ce 19
hasBC = true;
extId = "BC";
tmp = getLen(SEQUENCE_TYPE);
if (tmp == 0) break;
// ca is encoded as an ASN boolean (default is false)
if ((enc[idx] == 0x01) && (enc[idx + 1] == 0x01) &&
(enc[idx + 2] == (byte) 0xff)) {
isCA = true;
idx += 3;
}
/*
* path length constraint is encoded as optional ASN
* integer
*/
if ((enc[idx] == 0x02) && (enc[idx + 1] != 0)) {
tmp = getLen(INTEGER_TYPE);
pLenConstr = 0;
for (int i = 0; i < tmp; i++) {
pLenConstr = (pLenConstr << 16) + enc[idx + i];
}
idx += tmp;
} else {
if (isCA) pLenConstr = UNLIMITED_CERT_CHAIN_LENGTH;
}
break;
case 0x25: // extendedKeyUsage = id-ce 37
extId = "EKU";
if (extKeyUsage == -1) {
extKeyUsage = 0;
}
getLen(SEQUENCE_TYPE);
int kuOidLen;
while (idx < extValIdx + extValLen) {
kuOidLen = getLen(OID_TYPE);
if ((kuOidLen == ID_KP.length + 1) &&
Utils.byteMatch(enc, idx,
ID_KP, 0, ID_KP.length) &&
(enc[idx + ID_KP.length] > 0) &&
(enc[idx + ID_KP.length] < 9)) {
extKeyUsage |=
(1 << (enc[idx + ID_KP.length]));
} else {
if (crit) badExt = true;
}
idx += kuOidLen;
}
if (!crit) {
// ignore extended key usage if not critical
extKeyUsage = -1;
}
break;
/*
* Extensions which we do not currently support include:
* subjectDirectoryAttribute 0x09,
* subjectKeyIdentifier 0x0e, privateKeyUsagePeriod 0x10,
* issuerAltName 0x12, cRLNumber 0x14, reasonCode 0x15,
* instructionCode 0x17, invalidityDate 0x18,
* deltaCRLIndicator 0x1b, issuingDistributionPoint 0x1c,
* certificateIssuer 0x1d, nameConstraints 0x1e,
* cRLDistributionPoints 0x1f, certificatePolicies 0x20,
* policyMappings 0x21, authorityKeyIdentifier 0x23,
* policyConstraints 0x24
*/
}
}
// For debugging only
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_SECURITY,
"<Id: " +
Utils.hexEncode(enc, extIdIdx, extIdLen) +
(crit ? ", critical, " : ", ") +
Utils.hexEncode(enc, extValIdx, extValLen) +
">" +
((extId == null) ? " (Unrecognized)" : ""));
}
if ((extId == null) && crit) badExt = true;
idx = extValIdx + extValLen;
}
if (idx != end) {
throw new IOException("Extension parsing problem");
}
| public java.lang.String | toString()Returns a string representation of this certificate.
StringBuffer tmp = new StringBuffer();
tmp.append("[Type: ");
tmp.append(getType());
tmp.append("v");
tmp.append(version);
tmp.append("\n");
tmp.append("Serial number: ");
tmp.append(serialNumber);
tmp.append("\n");
tmp.append("Subject: ");
tmp.append(subject);
tmp.append("\n");
tmp.append("Issuer: ");
tmp.append(issuer);
tmp.append("\n");
tmp.append("Valid from ");
tmp.append(date2str(new Date(getNotBefore())));
tmp.append(" GMT until ");
tmp.append(date2str(new Date(getNotAfter())));
tmp.append(" GMT");
// tmp.append("\n");
// tmp.append(pubKey.toString());
// tmp.append("\n");
// tmp.append(TBSCertificate hash: ");
// tmp.append(TBSCertHash == null ?
// "null" : Utils.hexEncode(TBSCertHash));
tmp.append("\n");
tmp.append("Signature Algorithm: ");
tmp.append(getSigAlgName());
if (subAltName != null) {
tmp.append("\n");
tmp.append("SubjectAltName: ");
tmp.append(subAltName);
tmp.append("(type ");
tmp.append(subAltNameType);
tmp.append(")");
}
if (keyUsage != -1) {
tmp.append("\n");
tmp.append("KeyUsage:");
int t = (int) keyUsage;
for (int i = 0; i < KEY_USAGE.length; i++) {
if ((t & 0x01) == 0x01) {
tmp.append(" ");
tmp.append(KEY_USAGE[i]);
}
t = t >>> 1;
}
}
if (hasBC) {
tmp.append("\n");
tmp.append("BasicConstraints: ");
tmp.append(isCA ? "is a CA" : "not a CA");
tmp.append(" (pathLengthConstraint ");
if ((pLenConstr == MISSING_PATH_LENGTH_CONSTRAINT) ||
(pLenConstr == UNLIMITED_CERT_CHAIN_LENGTH)) {
tmp.append("absent");
} else {
tmp.append(pLenConstr);
}
tmp.append(")");
}
// tmp.append("\n");
// tmp.append("MD5 Fingerprint: ");
// tmp.append(Utils.hexEncode(fp));
tmp.append("]");
return tmp.toString();
| public void | verify(PublicKey k)Checks if this certificate was signed using the private key
corresponding to the specified public key.
RSAPublicKey pk;
if (!(k instanceof RSAPublicKey)) {
throw new CertificateException("Issuer key not a public RSA",
this, CertificateException.VERIFICATION_FAILED);
}
pk = (RSAPublicKey)k;
/*
* Since selfSigned certificates are stored without
* TBSCertHash and signature fields (to save memory),
* the only way to return anything meaningful is by
* directly comparing the specified public key against
* the certificate public key. This allows us to return
* the right result even on certificates created using
* the Certificate(...) constructor.
*
* NOTE: We can comment this out to save code here and
* in Key (and its subclasses) -- no need to define equals
* The documentation already warns users not to invoke
* verify() on certificates with null signature.
*/
if (selfSigned) {
if (pubKey.equals(pk)) {
return;
}
throw new CertificateException("Bad self signed cert",
this, CertificateException.VERIFICATION_FAILED);
}
if (signature == null) {
throw new CertificateException(this,
CertificateException.MISSING_SIGNATURE);
}
if (TBSCertHash == null) {
throw new CertificateException(this,
CertificateException.UNSUPPORTED_SIGALG);
}
int modLen = pk.getModulusLen();
byte[] result = new byte[modLen];
int val;
/*
* NOTE: We can not use the Signature class because, at this
* point, we do not have TBSCertificate (just its hash). The
* Signature class needs raw data and computes a hash internally.
*/
try {
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.DECRYPT_MODE, pk);
val = rsa.doFinal(signature, 0, signature.length, result, 0);
} catch (Exception e) {
throw new CertificateException(this,
CertificateException.VERIFICATION_FAILED);
}
/*
* NOTE: the decrypted value includes an ASN DER
* encoding of
* DigestInfo ::= SEQUENCE {
* digestAlgorithm DigestAlgorithmIdentifier,
* digest Digest }
* DigestAlgorithmIdentifier ::= AlgorithmIdentifier
* Digest ::= OCTET STRING
*
* For md2WithRSAEncryption, the decrypted value will be
* 3020300c06082a864886f70d020205000410 followed by a 16-byte hash
*
* For md5WithRSAEncryption, the decrypted value will be
* 3020300c06082a864886f70d020505000410 followed by a 16-byte hash
* 30 20 32: SEQUENCE
* 30 0c 12: . SEQUENCE
* 06 08 8: . . OID 1.2.840.113549.2.5 (MD5 OID, rfc2313 pg 14)
* : 2a 86 48 86 f7 0d 02 05
* 05 00 0: . . NULL (null parameters)
* 04 10 16: . OCTET STRING
* : <the hash gos here>
*
* Similarly, for SHA-1, the 20-byte hash will be preceded by
* 3021300906052b0e03021a05000414
* 30 21 33: SEQUENCE
* 30 09 9: . SEQUENCE
* 06 05 5: . . OID 1.3.14.3.2.26 (SHA-1 digest OID)
* 0: 2b 0e 03 02 1a
* 05 00 0: . . NULL (null parameters)
* 04 14 20: . <20-byte hash>
*/
if ((sigAlg == MD2_RSA) &&
(val == (PREFIX_MD2.length + TBSCertHash.length)) &&
Utils.byteMatch(result, 0,
PREFIX_MD2, 0, PREFIX_MD2.length) &&
Utils.byteMatch(result, PREFIX_MD2.length,
TBSCertHash, 0, TBSCertHash.length)) {
return;
}
if ((sigAlg == MD5_RSA) &&
(val == (PREFIX_MD5.length + TBSCertHash.length)) &&
Utils.byteMatch(result, 0,
PREFIX_MD5, 0, PREFIX_MD5.length) &&
Utils.byteMatch(result, PREFIX_MD5.length,
TBSCertHash, 0, TBSCertHash.length)) {
return;
}
if ((sigAlg == SHA1_RSA) &&
(val == (PREFIX_SHA1.length + TBSCertHash.length)) &&
Utils.byteMatch(result, 0,
PREFIX_SHA1, 0, PREFIX_SHA1.length) &&
Utils.byteMatch(result, PREFIX_SHA1.length,
TBSCertHash, 0, TBSCertHash.length)) {
return;
}
throw new CertificateException(this,
CertificateException.VERIFICATION_FAILED);
| public static java.lang.String[] | verifyChain(java.util.Vector certs, int keyUsage, int extKeyUsage, CertStore certStore)Verify a chain of certificates.
X509Certificate cert;
X509Certificate prevCert;
PublicKey key;
Vector keys;
X509Certificate[] caCerts; // CA X509Certificates
int maxPathLen = -1; // 0 means a chain of 1 so -1 means no chain
int prevMaxPathLen;
Vector subjectNames = new Vector();
String[] authPath;
// must be an enitity certificate
cert = (X509Certificate)certs.elementAt(0);
checkKeyUsageAndValidity(cert, keyUsage, extKeyUsage);
for (int i = 1; ; i++) {
// look up the public key of the certificate issurer
caCerts = certStore.getCertificates(cert.getIssuer());
if (caCerts != null) {
// found a known CA no need to go on to the next cert
if (caCerts[0].getSubject() != cert.getSubject()) {
subjectNames.addElement(caCerts[0].getSubject());
}
break;
}
if (i >= certs.size()) {
throw new CertificateException(cert,
CertificateException.UNRECOGNIZED_ISSUER);
}
/* Save the name of subject. */
subjectNames.addElement(cert.getSubject());
prevCert = cert;
cert = (X509Certificate)certs.elementAt(i);
/*
* This must be a CA so the key usage always is certficate
* signing.
*/
checkKeyUsageAndValidity(cert,
X509Certificate.CERT_SIGN_KEY_USAGE, extKeyUsage);
/*
* This is a chain, check chain link:
* the subject of this certificate must be the issuer of
* the previous certificate
*/
if (prevCert.getIssuer().compareTo(cert.getSubject()) != 0) {
throw new CertificateException(prevCert,
CertificateException.BROKEN_CHAIN);
}
/*
* Check if basicConstraints are satisfied. Note a zero
* pathLength means we should have only processed one cert
* so far.
*/
prevMaxPathLen = maxPathLen;
maxPathLen = cert.getBasicConstraints();
if (maxPathLen != X509Certificate.UNLIMITED_CERT_CHAIN_LENGTH &&
maxPathLen <= prevMaxPathLen) {
if (cert.getSubject().equals(cert.getIssuer())) {
/*
* This cert is a redundent, self signed CA cert
* allowed to be at the end of the chain.
* These certificates may version 1, so will not
* have extensions. So this really should be the
* unrecognized issuer.
*/
throw new CertificateException(prevCert,
CertificateException.UNRECOGNIZED_ISSUER);
}
if (maxPathLen ==
X509Certificate.MISSING_PATH_LENGTH_CONSTRAINT) {
throw new CertificateException(cert,
CertificateException.UNAUTHORIZED_INTERMEDIATE_CA);
}
/*
* An intermediate CA has over granted
* its given authority to the previous CA in the
* chain.
*/
throw new CertificateException(cert,
CertificateException.CERTIFICATE_CHAIN_TOO_LONG);
}
/* Save the most time intensive check for last. */
prevCert.verify(cert.getPublicKey());
}
for (int i = 0; i < caCerts.length; i++) {
try {
cert.verify(caCerts[i].getPublicKey());
} catch (CertificateException ce) {
/*
* the exception will be when we get out side of the loop,
* if none of the other keys work
*/
continue;
}
// check the CA key for valid dates
try {
caCerts[i].checkValidity();
} catch (CertificateException ce) {
if (ce.getReason() == CertificateException.EXPIRED) {
/*
* Change the exception reason, so the
* application knows that the problem is with
* the device and not the server.
*/
throw new CertificateException(caCerts[i],
CertificateException.ROOT_CA_EXPIRED);
}
throw ce;
}
// Success
authPath = new String[subjectNames.size()];
for (int j = subjectNames.size() - 1, k = 0; j >= 0;
j--, k++) {
authPath[k] = (String)subjectNames.elementAt(j);
}
return authPath;
}
throw new CertificateException(cert,
CertificateException.VERIFICATION_FAILED);
|
|