X509Certificate.java [plain text]
package gnu.java.security.x509;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Principal;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.CertificateParsingException;
import java.security.interfaces.DSAParams;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.DSAParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.x500.X500Principal;
import gnu.java.security.OID;
import gnu.java.security.der.*;
import gnu.java.security.x509.ext.*;
public class X509Certificate extends java.security.cert.X509Certificate
implements Serializable, GnuPKIExtension
{
private static final boolean DEBUG = false;
private static void debug(String msg)
{
if (DEBUG)
{
System.err.print(">> X509Certificate: ");
System.err.println(msg);
}
}
private static void debug(Throwable t)
{
if (DEBUG)
{
System.err.print(">> X509Certificate: ");
t.printStackTrace();
}
}
protected static final OID ID_DSA = new OID ("1.2.840.10040.4.1");
protected static final OID ID_DSA_WITH_SHA1 = new OID ("1.2.840.10040.4.3");
protected static final OID ID_RSA = new OID ("1.2.840.113549.1.1.1");
protected static final OID ID_RSA_WITH_MD2 = new OID ("1.2.840.113549.1.1.2");
protected static final OID ID_RSA_WITH_MD5 = new OID ("1.2.840.113549.1.1.4");
protected static final OID ID_RSA_WITH_SHA1 = new OID ("1.2.840.113549.1.1.5");
protected static final OID ID_ECDSA_WITH_SHA1 = new OID ("1.2.840.10045.4.1");
protected transient byte[] encoded;
protected transient byte[] tbsCertBytes;
protected transient int version;
protected transient BigInteger serialNo;
protected transient OID algId;
protected transient byte[] algVal;
protected transient X500DistinguishedName issuer;
protected transient Date notBefore;
protected transient Date notAfter;
protected transient X500DistinguishedName subject;
protected transient PublicKey subjectKey;
protected transient BitString issuerUniqueId;
protected transient BitString subjectUniqueId;
protected transient Map extensions;
protected transient OID sigAlgId;
protected transient byte[] sigAlgVal;
protected transient byte[] signature;
public X509Certificate(InputStream encoded)
throws CertificateException, IOException
{
super();
extensions = new HashMap();
try
{
parse(encoded);
}
catch (IOException ioe)
{
debug(ioe);
throw ioe;
}
catch (Exception e)
{
debug(e);
CertificateException ce = new CertificateException(e.getMessage());
ce.initCause (e);
throw ce;
}
}
protected X509Certificate()
{
extensions = new HashMap();
}
public void checkValidity()
throws CertificateExpiredException, CertificateNotYetValidException
{
checkValidity(new Date());
}
public void checkValidity(Date date)
throws CertificateExpiredException, CertificateNotYetValidException
{
if (date.compareTo(notBefore) < 0)
{
throw new CertificateNotYetValidException();
}
if (date.compareTo(notAfter) > 0)
{
throw new CertificateExpiredException();
}
}
public int getVersion()
{
return version;
}
public BigInteger getSerialNumber()
{
return serialNo;
}
public Principal getIssuerDN()
{
return issuer;
}
public X500Principal getIssuerX500Principal()
{
return new X500Principal(issuer.getDer());
}
public Principal getSubjectDN()
{
return subject;
}
public X500Principal getSubjectX500Principal()
{
return new X500Principal(subject.getDer());
}
public Date getNotBefore()
{
return (Date) notBefore.clone();
}
public Date getNotAfter()
{
return (Date) notAfter.clone();
}
public byte[] getTBSCertificate() throws CertificateEncodingException
{
return (byte[]) tbsCertBytes.clone();
}
public byte[] getSignature()
{
return (byte[]) signature.clone();
}
public String getSigAlgName()
{
if (sigAlgId.equals(ID_DSA_WITH_SHA1))
{
return "SHA1withDSA";
}
if (sigAlgId.equals(ID_RSA_WITH_MD2))
{
return "MD2withRSA";
}
if (sigAlgId.equals(ID_RSA_WITH_MD5))
{
return "MD5withRSA";
}
if (sigAlgId.equals(ID_RSA_WITH_SHA1))
{
return "SHA1withRSA";
}
return "unknown";
}
public String getSigAlgOID()
{
return sigAlgId.toString();
}
public byte[] getSigAlgParams()
{
return (byte[]) sigAlgVal.clone();
}
public boolean[] getIssuerUniqueID()
{
if (issuerUniqueId != null)
{
return issuerUniqueId.toBooleanArray();
}
return null;
}
public boolean[] getSubjectUniqueID()
{
if (subjectUniqueId != null)
{
return subjectUniqueId.toBooleanArray();
}
return null;
}
public boolean[] getKeyUsage()
{
Extension e = getExtension(KeyUsage.ID);
if (e != null)
{
KeyUsage ku = (KeyUsage) e.getValue();
boolean[] result = new boolean[9];
boolean[] b = ku.getKeyUsage().toBooleanArray();
System.arraycopy(b, 0, result, 0, b.length);
return result;
}
return null;
}
public List getExtendedKeyUsage() throws CertificateParsingException
{
Extension e = getExtension(ExtendedKeyUsage.ID);
if (e != null)
{
List a = ((ExtendedKeyUsage) e.getValue()).getPurposeIds();
List b = new ArrayList(a.size());
for (Iterator it = a.iterator(); it.hasNext(); )
{
b.add(it.next().toString());
}
return Collections.unmodifiableList(b);
}
return null;
}
public int getBasicConstraints()
{
Extension e = getExtension(BasicConstraints.ID);
if (e != null)
{
return ((BasicConstraints) e.getValue()).getPathLengthConstraint();
}
return -1;
}
public Collection getSubjectAlternativeNames()
throws CertificateParsingException
{
Extension e = getExtension(SubjectAlternativeNames.ID);
if (e != null)
{
return ((SubjectAlternativeNames) e.getValue()).getNames();
}
return null;
}
public Collection getIssuerAlternativeNames()
throws CertificateParsingException
{
Extension e = getExtension(IssuerAlternativeNames.ID);
if (e != null)
{
return ((IssuerAlternativeNames) e.getValue()).getNames();
}
return null;
}
public boolean hasUnsupportedCriticalExtension()
{
for (Iterator it = extensions.values().iterator(); it.hasNext(); )
{
Extension e = (Extension) it.next();
if (e.isCritical() && !e.isSupported())
return true;
}
return false;
}
public Set getCriticalExtensionOIDs()
{
HashSet s = new HashSet();
for (Iterator it = extensions.values().iterator(); it.hasNext(); )
{
Extension e = (Extension) it.next();
if (e.isCritical())
s.add(e.getOid().toString());
}
return Collections.unmodifiableSet(s);
}
public Set getNonCriticalExtensionOIDs()
{
HashSet s = new HashSet();
for (Iterator it = extensions.values().iterator(); it.hasNext(); )
{
Extension e = (Extension) it.next();
if (!e.isCritical())
s.add(e.getOid().toString());
}
return Collections.unmodifiableSet(s);
}
public byte[] getExtensionValue(String oid)
{
Extension e = getExtension(new OID(oid));
if (e != null)
{
return e.getValue().getEncoded();
}
return null;
}
public Extension getExtension(OID oid)
{
return (Extension) extensions.get(oid);
}
public Collection getExtensions()
{
return extensions.values();
}
public byte[] getEncoded() throws CertificateEncodingException
{
return (byte[]) encoded.clone();
}
public void verify(PublicKey key)
throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException
{
Signature sig = Signature.getInstance(sigAlgId.toString());
doVerify(sig, key);
}
public void verify(PublicKey key, String provider)
throws CertificateException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchProviderException, SignatureException
{
Signature sig = Signature.getInstance(sigAlgId.toString(), provider);
doVerify(sig, key);
}
public String toString()
{
StringWriter str = new StringWriter();
PrintWriter out = new PrintWriter(str);
out.println(X509Certificate.class.getName() + " {");
out.println(" TBSCertificate {");
out.println(" version = " + version + ";");
out.println(" serialNo = " + serialNo + ";");
out.println(" signature = {");
out.println(" algorithm = " + getSigAlgName() + ";");
out.print(" parameters =");
if (sigAlgVal != null)
{
out.println();
out.print(Util.hexDump(sigAlgVal, " "));
}
else
{
out.println(" null;");
}
out.println(" }");
out.println(" issuer = " + issuer.getName() + ";");
out.println(" validity = {");
out.println(" notBefore = " + notBefore + ";");
out.println(" notAfter = " + notAfter + ";");
out.println(" }");
out.println(" subject = " + subject.getName() + ";");
out.println(" subjectPublicKeyInfo = {");
out.println(" algorithm = " + subjectKey.getAlgorithm());
out.println(" key =");
out.print(Util.hexDump(subjectKey.getEncoded(), " "));
out.println(" };");
out.println(" issuerUniqueId = " + issuerUniqueId + ";");
out.println(" subjectUniqueId = " + subjectUniqueId + ";");
out.println(" extensions = {");
for (Iterator it = extensions.values().iterator(); it.hasNext(); )
{
out.println(" " + it.next());
}
out.println(" }");
out.println(" }");
out.println(" signatureAlgorithm = " + getSigAlgName() + ";");
out.println(" signatureValue =");
out.print(Util.hexDump(signature, " "));
out.println("}");
return str.toString();
}
public PublicKey getPublicKey()
{
return subjectKey;
}
public boolean equals(Object other)
{
if (!(other instanceof X509Certificate))
return false;
try
{
if (other instanceof X509Certificate)
return Arrays.equals(encoded, ((X509Certificate) other).encoded);
byte[] enc = ((X509Certificate) other).getEncoded();
if (enc == null)
return false;
return Arrays.equals(encoded, enc);
}
catch (CertificateEncodingException cee)
{
return false;
}
}
private void doVerify(Signature sig, PublicKey key)
throws CertificateException, InvalidKeyException, SignatureException
{
debug("verifying sig=" + sig + " key=" + key);
sig.initVerify(key);
sig.update(tbsCertBytes);
if (!sig.verify(signature))
{
throw new CertificateException("signature not validated");
}
}
private void parse(InputStream encoded) throws Exception
{
DERReader der = new DERReader(encoded);
DERValue cert = der.read();
debug("start Certificate len == " + cert.getLength());
this.encoded = cert.getEncoded();
if (!cert.isConstructed())
{
throw new IOException("malformed Certificate");
}
DERValue tbsCert = der.read();
if (tbsCert.getValue() != DER.CONSTRUCTED_VALUE)
{
throw new IOException("malformed TBSCertificate");
}
tbsCertBytes = tbsCert.getEncoded();
debug("start TBSCertificate len == " + tbsCert.getLength());
DERValue val = der.read();
if (val.getTagClass() == DER.CONTEXT && val.getTag() == 0)
{
version = ((BigInteger) der.read().getValue()).intValue() + 1;
val = der.read();
}
else
{
version = 1;
}
debug("read version == " + version);
serialNo = (BigInteger) val.getValue();
debug("read serial number == " + serialNo);
val = der.read();
if (!val.isConstructed())
{
throw new IOException("malformed AlgorithmIdentifier");
}
int certAlgLen = val.getLength();
debug("start AlgorithmIdentifier len == " + certAlgLen);
val = der.read();
algId = (OID) val.getValue();
debug("read algorithm ID == " + algId);
if (certAlgLen > val.getEncodedLength())
{
val = der.read();
if (val == null)
{
algVal = null;
}
else
{
algVal = val.getEncoded();
}
if (val.isConstructed())
{
encoded.skip(val.getLength());
}
debug("read algorithm parameters == " + algVal);
}
val = der.read();
issuer = new X500DistinguishedName(val.getEncoded());
der.skip(val.getLength());
debug("read issuer == " + issuer);
if (!der.read().isConstructed())
{
throw new IOException("malformed Validity");
}
notBefore = (Date) der.read().getValue();
notAfter = (Date) der.read().getValue();
debug("read notBefore == " + notBefore);
debug("read notAfter == " + notAfter);
val = der.read();
subject = new X500DistinguishedName(val.getEncoded());
der.skip(val.getLength());
debug("read subject == " + subject);
DERValue spki = der.read();
if (!spki.isConstructed())
{
throw new IOException("malformed SubjectPublicKeyInfo");
}
KeyFactory spkFac = KeyFactory.getInstance("X.509");
subjectKey = spkFac.generatePublic(new X509EncodedKeySpec(spki.getEncoded()));
der.skip(spki.getLength());
debug("read subjectPublicKey == " + subjectKey);
if (version > 1)
{
val = der.read();
}
if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 1)
{
byte[] b = (byte[]) val.getValue();
issuerUniqueId = new BitString(b, 1, b.length-1, b[0] & 0xFF);
debug("read issuerUniqueId == " + issuerUniqueId);
val = der.read();
}
if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 2)
{
byte[] b = (byte[]) val.getValue();
subjectUniqueId = new BitString(b, 1, b.length-1, b[0] & 0xFF);
debug("read subjectUniqueId == " + subjectUniqueId);
val = der.read();
}
if (version >= 3 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 3)
{
val = der.read();
debug("start Extensions len == " + val.getLength());
int len = 0;
while (len < val.getLength())
{
DERValue ext = der.read();
debug("start extension len == " + ext.getLength());
Extension e = new Extension(ext.getEncoded());
extensions.put(e.getOid(), e);
der.skip(ext.getLength());
len += ext.getEncodedLength();
debug("count == " + len);
}
}
val = der.read();
if (!val.isConstructed())
{
throw new IOException("malformed AlgorithmIdentifier");
}
int sigAlgLen = val.getLength();
debug("start AlgorithmIdentifier len == " + sigAlgLen);
val = der.read();
sigAlgId = (OID) val.getValue();
debug("read algorithm id == " + sigAlgId);
if (sigAlgLen > val.getEncodedLength())
{
val = der.read();
if (val.getValue() == null)
{
if (subjectKey instanceof DSAPublicKey)
{
AlgorithmParameters params =
AlgorithmParameters.getInstance("DSA");
DSAParams dsap = ((DSAPublicKey) subjectKey).getParams();
DSAParameterSpec spec =
new DSAParameterSpec(dsap.getP(), dsap.getQ(), dsap.getG());
params.init(spec);
sigAlgVal = params.getEncoded();
}
}
else
{
sigAlgVal = (byte[]) val.getEncoded();
}
if (val.isConstructed())
{
encoded.skip(val.getLength());
}
debug("read parameters == " + sigAlgVal);
}
signature = ((BitString) der.read().getValue()).toByteArray();
debug("read signature ==\n" + Util.hexDump(signature, ">>>> "));
}
}