/* * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.x509; import java.io.BufferedReader; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.math.BigInteger; import java.security.*; import java.security.cert.*; import java.security.cert.Certificate; import java.util.*; import javax.security.auth.x500.X500Principal; import sun.misc.HexDumpEncoder; import sun.misc.BASE64Decoder; import sun.security.util.*; import sun.security.provider.X509Factory; /** * The X509CertImpl class represents an X.509 certificate. These certificates * are widely used to support authentication and other functionality in * Internet security systems. Common applications include Privacy Enhanced * Mail (PEM), Transport Layer Security (SSL), code signing for trusted * software distribution, and Secure Electronic Transactions (SET). There * is a commercial infrastructure ready to manage large scale deployments * of X.509 identity certificates. * *
These certificates are managed and vouched for by Certificate * Authorities (CAs). CAs are services which create certificates by * placing data in the X.509 standard format and then digitally signing * that data. Such signatures are quite difficult to forge. CAs act as * trusted third parties, making introductions between agents who have no * direct knowledge of each other. CA certificates are either signed by * themselves, or by some other CA such as a "root" CA. * *
RFC 1422 is very informative, though it does not describe much * of the recent work being done with X.509 certificates. That includes * a 1996 version (X.509v3) and a variety of enhancements being made to * facilitate an explosion of personal certificates used as "Internet * Drivers' Licences", or with SET for credit card transactions. * *
More recent work includes the IETF PKIX Working Group efforts,
* especially RFC2459.
*
* @author Dave Brownell
* @author Amit Kapoor
* @author Hemma Prafullchandra
* @see X509CertInfo
*/
public class X509CertImpl extends X509Certificate implements DerEncoder {
private static final long serialVersionUID = -3457612960190864406L;
private static final String DOT = ".";
/**
* Public attribute names.
*/
public static final String NAME = "x509";
public static final String INFO = X509CertInfo.NAME;
public static final String ALG_ID = "algorithm";
public static final String SIGNATURE = "signature";
public static final String SIGNED_CERT = "signed_cert";
/**
* The following are defined for ease-of-use. These
* are the most frequently retrieved attributes.
*/
// x509.info.subject.dname
public static final String SUBJECT_DN = NAME + DOT + INFO + DOT +
X509CertInfo.SUBJECT + DOT +
CertificateSubjectName.DN_NAME;
// x509.info.issuer.dname
public static final String ISSUER_DN = NAME + DOT + INFO + DOT +
X509CertInfo.ISSUER + DOT +
CertificateIssuerName.DN_NAME;
// x509.info.serialNumber.number
public static final String SERIAL_ID = NAME + DOT + INFO + DOT +
X509CertInfo.SERIAL_NUMBER + DOT +
CertificateSerialNumber.NUMBER;
// x509.info.key.value
public static final String PUBLIC_KEY = NAME + DOT + INFO + DOT +
X509CertInfo.KEY + DOT +
CertificateX509Key.KEY;
// x509.info.version.value
public static final String VERSION = NAME + DOT + INFO + DOT +
X509CertInfo.VERSION + DOT +
CertificateVersion.VERSION;
// x509.algorithm
public static final String SIG_ALG = NAME + DOT + ALG_ID;
// x509.signature
public static final String SIG = NAME + DOT + SIGNATURE;
// when we sign and decode we set this to true
// this is our means to make certificates immutable
private boolean readOnly = false;
// Certificate data, and its envelope
private byte[] signedCert = null;
protected X509CertInfo info = null;
protected AlgorithmId algId = null;
protected byte[] signature = null;
// recognized extension OIDS
private static final String KEY_USAGE_OID = "2.5.29.15";
private static final String EXTENDED_KEY_USAGE_OID = "2.5.29.37";
private static final String BASIC_CONSTRAINT_OID = "2.5.29.19";
private static final String SUBJECT_ALT_NAME_OID = "2.5.29.17";
private static final String ISSUER_ALT_NAME_OID = "2.5.29.18";
private static final String AUTH_INFO_ACCESS_OID = "1.3.6.1.5.5.7.1.1";
// number of standard key usage bits.
private static final int NUM_STANDARD_KEY_USAGE = 9;
// SubjectAlterntativeNames cache
private Collection> subjectAlternativeNames;
// IssuerAlternativeNames cache
private Collection
> issuerAlternativeNames;
// ExtendedKeyUsage cache
private List
DerEncoder
interface.
*
* @param out the output stream on which to write the DER encoding.
*
* @exception IOException on encoding error.
*/
public void derEncode(OutputStream out) throws IOException {
if (signedCert == null)
throw new IOException("Null certificate to encode");
out.write(signedCert.clone());
}
/**
* Returns the encoded form of this certificate. It is
* assumed that each certificate type would have only a single
* form of encoding; for example, X.509 certificates would
* be encoded as ASN.1 DER.
*
* @exception CertificateEncodingException if an encoding error occurs.
*/
public byte[] getEncoded() throws CertificateEncodingException {
return getEncodedInternal().clone();
}
/**
* Returned the encoding as an uncloned byte array. Callers must
* guarantee that they neither modify it nor expose it to untrusted
* code.
*/
public byte[] getEncodedInternal() throws CertificateEncodingException {
if (signedCert == null) {
throw new CertificateEncodingException(
"Null certificate to encode");
}
return signedCert;
}
/**
* Throws an exception if the certificate was not signed using the
* verification key provided. Successfully verifying a certificate
* does not indicate that one should trust the entity which
* it represents.
*
* @param key the public key used for verification.
*
* @exception InvalidKeyException on incorrect key.
* @exception NoSuchAlgorithmException on unsupported signature
* algorithms.
* @exception NoSuchProviderException if there's no default provider.
* @exception SignatureException on signature errors.
* @exception CertificateException on encoding errors.
*/
public void verify(PublicKey key)
throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException {
verify(key, "");
}
/**
* Throws an exception if the certificate was not signed using the
* verification key provided. Successfully verifying a certificate
* does not indicate that one should trust the entity which
* it represents.
*
* @param key the public key used for verification.
* @param sigProvider the name of the provider.
*
* @exception NoSuchAlgorithmException on unsupported signature
* algorithms.
* @exception InvalidKeyException on incorrect key.
* @exception NoSuchProviderException on incorrect provider.
* @exception SignatureException on signature errors.
* @exception CertificateException on encoding errors.
*/
public synchronized void verify(PublicKey key, String sigProvider)
throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException {
if (sigProvider == null) {
sigProvider = "";
}
if ((verifiedPublicKey != null) && verifiedPublicKey.equals(key)) {
// this certificate has already been verified using
// this public key. Make sure providers match, too.
if (sigProvider.equals(verifiedProvider)) {
if (verificationResult) {
return;
} else {
throw new SignatureException("Signature does not match.");
}
}
}
if (signedCert == null) {
throw new CertificateEncodingException("Uninitialized certificate");
}
// Verify the signature ...
Signature sigVerf = null;
if (sigProvider.length() == 0) {
sigVerf = Signature.getInstance(algId.getName());
} else {
sigVerf = Signature.getInstance(algId.getName(), sigProvider);
}
sigVerf.initVerify(key);
byte[] rawCert = info.getEncodedInfo();
sigVerf.update(rawCert, 0, rawCert.length);
// verify may throw SignatureException for invalid encodings, etc.
verificationResult = sigVerf.verify(signature);
verifiedPublicKey = key;
verifiedProvider = sigProvider;
if (verificationResult == false) {
throw new SignatureException("Signature does not match.");
}
}
/**
* Creates an X.509 certificate, and signs it using the given key
* (associating a signature algorithm and an X.500 name).
* This operation is used to implement the certificate generation
* functionality of a certificate authority.
*
* @param key the private key used for signing.
* @param algorithm the name of the signature algorithm used.
*
* @exception InvalidKeyException on incorrect key.
* @exception NoSuchAlgorithmException on unsupported signature
* algorithms.
* @exception NoSuchProviderException if there's no default provider.
* @exception SignatureException on signature errors.
* @exception CertificateException on encoding errors.
*/
public void sign(PrivateKey key, String algorithm)
throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException {
sign(key, algorithm, null);
}
/**
* Creates an X.509 certificate, and signs it using the given key
* (associating a signature algorithm and an X.500 name).
* This operation is used to implement the certificate generation
* functionality of a certificate authority.
*
* @param key the private key used for signing.
* @param algorithm the name of the signature algorithm used.
* @param provider the name of the provider.
*
* @exception NoSuchAlgorithmException on unsupported signature
* algorithms.
* @exception InvalidKeyException on incorrect key.
* @exception NoSuchProviderException on incorrect provider.
* @exception SignatureException on signature errors.
* @exception CertificateException on encoding errors.
*/
public void sign(PrivateKey key, String algorithm, String provider)
throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException {
try {
if (readOnly)
throw new CertificateEncodingException(
"cannot over-write existing certificate");
Signature sigEngine = null;
if ((provider == null) || (provider.length() == 0))
sigEngine = Signature.getInstance(algorithm);
else
sigEngine = Signature.getInstance(algorithm, provider);
sigEngine.initSign(key);
// in case the name is reset
algId = AlgorithmId.get(sigEngine.getAlgorithm());
DerOutputStream out = new DerOutputStream();
DerOutputStream tmp = new DerOutputStream();
// encode certificate info
info.encode(tmp);
byte[] rawCert = tmp.toByteArray();
// encode algorithm identifier
algId.encode(tmp);
// Create and encode the signature itself.
sigEngine.update(rawCert, 0, rawCert.length);
signature = sigEngine.sign();
tmp.putBitString(signature);
// Wrap the signed data in a SEQUENCE { data, algorithm, sig }
out.write(DerValue.tag_Sequence, tmp);
signedCert = out.toByteArray();
readOnly = true;
} catch (IOException e) {
throw new CertificateEncodingException(e.toString());
}
}
/**
* Checks that the certificate is currently valid, i.e. the current
* time is within the specified validity period.
*
* @exception CertificateExpiredException if the certificate has expired.
* @exception CertificateNotYetValidException if the certificate is not
* yet valid.
*/
public void checkValidity()
throws CertificateExpiredException, CertificateNotYetValidException {
Date date = new Date();
checkValidity(date);
}
/**
* Checks that the specified date is within the certificate's
* validity period, or basically if the certificate would be
* valid at the specified date/time.
*
* @param date the Date to check against to see if this certificate
* is valid at that date/time.
*
* @exception CertificateExpiredException if the certificate has expired
* with respect to the date
supplied.
* @exception CertificateNotYetValidException if the certificate is not
* yet valid with respect to the date
supplied.
*/
public void checkValidity(Date date)
throws CertificateExpiredException, CertificateNotYetValidException {
CertificateValidity interval = null;
try {
interval = (CertificateValidity)info.get(CertificateValidity.NAME);
} catch (Exception e) {
throw new CertificateNotYetValidException("Incorrect validity period");
}
if (interval == null)
throw new CertificateNotYetValidException("Null validity period");
interval.valid(date);
}
/**
* Return the requested attribute from the certificate.
*
* Note that the X509CertInfo is not cloned for performance reasons.
* Callers must ensure that they do not modify it. All other
* attributes are cloned.
*
* @param name the name of the attribute.
* @exception CertificateParsingException on invalid attribute identifier.
*/
public Object get(String name)
throws CertificateParsingException {
X509AttributeName attr = new X509AttributeName(name);
String id = attr.getPrefix();
if (!(id.equalsIgnoreCase(NAME))) {
throw new CertificateParsingException("Invalid root of "
+ "attribute name, expected [" + NAME +
"], received " + "[" + id + "]");
}
attr = new X509AttributeName(attr.getSuffix());
id = attr.getPrefix();
if (id.equalsIgnoreCase(INFO)) {
if (info == null) {
return null;
}
if (attr.getSuffix() != null) {
try {
return info.get(attr.getSuffix());
} catch (IOException e) {
throw new CertificateParsingException(e.toString());
} catch (CertificateException e) {
throw new CertificateParsingException(e.toString());
}
} else {
return info;
}
} else if (id.equalsIgnoreCase(ALG_ID)) {
return(algId);
} else if (id.equalsIgnoreCase(SIGNATURE)) {
if (signature != null)
return signature.clone();
else
return null;
} else if (id.equalsIgnoreCase(SIGNED_CERT)) {
if (signedCert != null)
return signedCert.clone();
else
return null;
} else {
throw new CertificateParsingException("Attribute name not "
+ "recognized or get() not allowed for the same: " + id);
}
}
/**
* Set the requested attribute in the certificate.
*
* @param name the name of the attribute.
* @param obj the value of the attribute.
* @exception CertificateException on invalid attribute identifier.
* @exception IOException on encoding error of attribute.
*/
public void set(String name, Object obj)
throws CertificateException, IOException {
// check if immutable
if (readOnly)
throw new CertificateException("cannot over-write existing"
+ " certificate");
X509AttributeName attr = new X509AttributeName(name);
String id = attr.getPrefix();
if (!(id.equalsIgnoreCase(NAME))) {
throw new CertificateException("Invalid root of attribute name,"
+ " expected [" + NAME + "], received " + id);
}
attr = new X509AttributeName(attr.getSuffix());
id = attr.getPrefix();
if (id.equalsIgnoreCase(INFO)) {
if (attr.getSuffix() == null) {
if (!(obj instanceof X509CertInfo)) {
throw new CertificateException("Attribute value should"
+ " be of type X509CertInfo.");
}
info = (X509CertInfo)obj;
signedCert = null; //reset this as certificate data has changed
} else {
info.set(attr.getSuffix(), obj);
signedCert = null; //reset this as certificate data has changed
}
} else {
throw new CertificateException("Attribute name not recognized or " +
"set() not allowed for the same: " + id);
}
}
/**
* Delete the requested attribute from the certificate.
*
* @param name the name of the attribute.
* @exception CertificateException on invalid attribute identifier.
* @exception IOException on other errors.
*/
public void delete(String name)
throws CertificateException, IOException {
// check if immutable
if (readOnly)
throw new CertificateException("cannot over-write existing"
+ " certificate");
X509AttributeName attr = new X509AttributeName(name);
String id = attr.getPrefix();
if (!(id.equalsIgnoreCase(NAME))) {
throw new CertificateException("Invalid root of attribute name,"
+ " expected ["
+ NAME + "], received " + id);
}
attr = new X509AttributeName(attr.getSuffix());
id = attr.getPrefix();
if (id.equalsIgnoreCase(INFO)) {
if (attr.getSuffix() != null) {
info = null;
} else {
info.delete(attr.getSuffix());
}
} else if (id.equalsIgnoreCase(ALG_ID)) {
algId = null;
} else if (id.equalsIgnoreCase(SIGNATURE)) {
signature = null;
} else if (id.equalsIgnoreCase(SIGNED_CERT)) {
signedCert = null;
} else {
throw new CertificateException("Attribute name not recognized or " +
"delete() not allowed for the same: " + id);
}
}
/**
* Return an enumeration of names of attributes existing within this
* attribute.
*/
public EnumerationtbsCertificate
from this certificate.
* This can be used to verify the signature independently.
*
* @return the DER encoded certificate information.
* @exception CertificateEncodingException if an encoding error occurs.
*/
public byte[] getTBSCertificate() throws CertificateEncodingException {
if (info != null) {
return info.getEncodedInfo();
} else
throw new CertificateEncodingException("Uninitialized certificate");
}
/**
* Gets the raw Signature bits from the certificate.
*
* @return the signature.
*/
public byte[] getSignature() {
if (signature == null)
return null;
byte[] dup = new byte[signature.length];
System.arraycopy(signature, 0, dup, 0, dup.length);
return dup;
}
/**
* Gets the signature algorithm name for the certificate
* signature algorithm.
* For example, the string "SHA-1/DSA" or "DSS".
*
* @return the signature algorithm name.
*/
public String getSigAlgName() {
if (algId == null)
return null;
return (algId.getName());
}
/**
* Gets the signature algorithm OID string from the certificate.
* For example, the string "1.2.840.10040.4.3"
*
* @return the signature algorithm oid string.
*/
public String getSigAlgOID() {
if (algId == null)
return null;
ObjectIdentifier oid = algId.getOID();
return (oid.toString());
}
/**
* Gets the DER encoded signature algorithm parameters from this
* certificate's signature algorithm.
*
* @return the DER encoded signature algorithm parameters, or
* null if no parameters are present.
*/
public byte[] getSigAlgParams() {
if (algId == null)
return null;
try {
return algId.getEncodedParams();
} catch (IOException e) {
return null;
}
}
/**
* Gets the Issuer Unique Identity from the certificate.
*
* @return the Issuer Unique Identity.
*/
public boolean[] getIssuerUniqueID() {
if (info == null)
return null;
try {
UniqueIdentity id = (UniqueIdentity)info.get(
CertificateIssuerUniqueIdentity.NAME
+ DOT + CertificateIssuerUniqueIdentity.ID);
if (id == null)
return null;
else
return (id.getId());
} catch (Exception e) {
return null;
}
}
/**
* Gets the Subject Unique Identity from the certificate.
*
* @return the Subject Unique Identity.
*/
public boolean[] getSubjectUniqueID() {
if (info == null)
return null;
try {
UniqueIdentity id = (UniqueIdentity)info.get(
CertificateSubjectUniqueIdentity.NAME
+ DOT + CertificateSubjectUniqueIdentity.ID);
if (id == null)
return null;
else
return (id.getId());
} catch (Exception e) {
return null;
}
}
/**
* Get AuthorityKeyIdentifier extension
* @return AuthorityKeyIdentifier object or null (if no such object
* in certificate)
*/
public AuthorityKeyIdentifierExtension getAuthorityKeyIdentifierExtension()
{
return (AuthorityKeyIdentifierExtension)
getExtension(PKIXExtensions.AuthorityKey_Id);
}
/**
* Get BasicConstraints extension
* @return BasicConstraints object or null (if no such object in
* certificate)
*/
public BasicConstraintsExtension getBasicConstraintsExtension() {
return (BasicConstraintsExtension)
getExtension(PKIXExtensions.BasicConstraints_Id);
}
/**
* Get CertificatePoliciesExtension
* @return CertificatePoliciesExtension or null (if no such object in
* certificate)
*/
public CertificatePoliciesExtension getCertificatePoliciesExtension() {
return (CertificatePoliciesExtension)
getExtension(PKIXExtensions.CertificatePolicies_Id);
}
/**
* Get ExtendedKeyUsage extension
* @return ExtendedKeyUsage extension object or null (if no such object
* in certificate)
*/
public ExtendedKeyUsageExtension getExtendedKeyUsageExtension() {
return (ExtendedKeyUsageExtension)
getExtension(PKIXExtensions.ExtendedKeyUsage_Id);
}
/**
* Get IssuerAlternativeName extension
* @return IssuerAlternativeName object or null (if no such object in
* certificate)
*/
public IssuerAlternativeNameExtension getIssuerAlternativeNameExtension() {
return (IssuerAlternativeNameExtension)
getExtension(PKIXExtensions.IssuerAlternativeName_Id);
}
/**
* Get NameConstraints extension
* @return NameConstraints object or null (if no such object in certificate)
*/
public NameConstraintsExtension getNameConstraintsExtension() {
return (NameConstraintsExtension)
getExtension(PKIXExtensions.NameConstraints_Id);
}
/**
* Get PolicyConstraints extension
* @return PolicyConstraints object or null (if no such object in
* certificate)
*/
public PolicyConstraintsExtension getPolicyConstraintsExtension() {
return (PolicyConstraintsExtension)
getExtension(PKIXExtensions.PolicyConstraints_Id);
}
/**
* Get PolicyMappingsExtension extension
* @return PolicyMappingsExtension object or null (if no such object
* in certificate)
*/
public PolicyMappingsExtension getPolicyMappingsExtension() {
return (PolicyMappingsExtension)
getExtension(PKIXExtensions.PolicyMappings_Id);
}
/**
* Get PrivateKeyUsage extension
* @return PrivateKeyUsage object or null (if no such object in certificate)
*/
public PrivateKeyUsageExtension getPrivateKeyUsageExtension() {
return (PrivateKeyUsageExtension)
getExtension(PKIXExtensions.PrivateKeyUsage_Id);
}
/**
* Get SubjectAlternativeName extension
* @return SubjectAlternativeName object or null (if no such object in
* certificate)
*/
public SubjectAlternativeNameExtension getSubjectAlternativeNameExtension()
{
return (SubjectAlternativeNameExtension)
getExtension(PKIXExtensions.SubjectAlternativeName_Id);
}
/**
* Get SubjectKeyIdentifier extension
* @return SubjectKeyIdentifier object or null (if no such object in
* certificate)
*/
public SubjectKeyIdentifierExtension getSubjectKeyIdentifierExtension() {
return (SubjectKeyIdentifierExtension)
getExtension(PKIXExtensions.SubjectKey_Id);
}
/**
* Get CRLDistributionPoints extension
* @return CRLDistributionPoints object or null (if no such object in
* certificate)
*/
public CRLDistributionPointsExtension getCRLDistributionPointsExtension() {
return (CRLDistributionPointsExtension)
getExtension(PKIXExtensions.CRLDistributionPoints_Id);
}
/**
* Return true if a critical extension is found that is
* not supported, otherwise return false.
*/
public boolean hasUnsupportedCriticalExtension() {
if (info == null)
return false;
try {
CertificateExtensions exts = (CertificateExtensions)info.get(
CertificateExtensions.NAME);
if (exts == null)
return false;
return exts.hasUnsupportedCriticalExtension();
} catch (Exception e) {
return false;
}
}
/**
* Gets a Set of the extension(s) marked CRITICAL in the
* certificate. In the returned set, each extension is
* represented by its OID string.
*
* @return a set of the extension oid strings in the
* certificate that are marked critical.
*/
public Set> makeAltNames(GeneralNames names) {
if (names.isEmpty()) {
return Collections.
>emptySet();
}
Set
> newNames = new HashSet
>();
for (GeneralName gname : names.names()) {
GeneralNameInterface name = gname.getName();
List