1 /* X509Certificate.java -- X.509 certificate.
2 Copyright (C) 2003 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.security.x509;
41 import java.io.ByteArrayInputStream;
42 import java.io.InputStream;
43 import java.io.IOException;
44 import java.io.Serializable;
46 import java.math.BigInteger;
48 import java.security.AlgorithmParameters;
49 import java.security.InvalidKeyException;
50 import java.security.KeyFactory;
51 import java.security.NoSuchAlgorithmException;
52 import java.security.NoSuchProviderException;
53 import java.security.Principal;
54 import java.security.PublicKey;
55 import java.security.Signature;
56 import java.security.SignatureException;
58 import java.security.cert.CertificateEncodingException;
59 import java.security.cert.CertificateException;
60 import java.security.cert.CertificateExpiredException;
61 import java.security.cert.CertificateNotYetValidException;
62 import java.security.cert.CertificateParsingException;
64 import java.security.spec.DSAParameterSpec;
65 import java.security.spec.DSAPublicKeySpec;
66 import java.security.spec.RSAPublicKeySpec;
68 import java.util.ArrayList;
69 import java.util.Collection;
70 import java.util.Collections;
71 import java.util.Date;
72 import java.util.HashMap;
73 import java.util.HashSet;
74 import java.util.Iterator;
75 import java.util.LinkedList;
76 import java.util.List;
79 import javax.security.auth.x500.X500Principal;
81 import gnu.java.io.ASN1ParsingException;
82 import gnu.java.security.OID;
83 import gnu.java.security.der.BitString;
84 import gnu.java.security.der.DER;
85 import gnu.java.security.der.DERReader;
86 import gnu.java.security.der.DERValue;
87 import gnu.java.security.der.DERWriter;
90 * An implementation of X.509 certificates.
92 * @author Casey Marshall (rsdio@metastatic.org)
94 public class X509Certificate extends java.security.cert.X509Certificate
95 implements Serializable
98 // Constants and fields.
99 // ------------------------------------------------------------------------
101 private static final OID ID_DSA = new OID("1.2.840.10040.4.1");
102 private static final OID ID_DSA_WITH_SHA1 = new OID("1.2.840.10040.4.3");
103 private static final OID ID_RSA = new OID("1.2.840.113549.1.1.1");
104 private static final OID ID_RSA_WITH_MD2 = new OID("1.2.840.113549.1.1.2");
105 private static final OID ID_RSA_WITH_MD5 = new OID("1.2.840.113549.1.1.4");
106 private static final OID ID_RSA_WITH_SHA1 = new OID("1.2.840.113549.1.1.5");
108 private static final OID ID_EXTENSION = new OID("2.5.29");
109 private static final OID ID_KEY_USAGE = ID_EXTENSION.getChild(15);
110 private static final OID ID_BASIC_CONSTRAINTS = ID_EXTENSION.getChild(19);
111 private static final OID ID_EXT_KEY_USAGE = ID_EXTENSION.getChild(37);
113 private static final int OTHER_NAME = 0;
114 private static final int RFC882_NAME = 1;
115 private static final int DNS_NAME = 2;
116 private static final int X400_ADDRESS = 3;
117 private static final int DIRECTORY_NAME = 4;
118 private static final int EDI_PARTY_NAME = 5;
119 private static final int URI = 6;
120 private static final int IP_ADDRESS = 7;
121 private static final int REGISTERED_ID = 8;
123 // This object SHOULD be serialized with an instance of
124 // java.security.cert.Certificate.CertificateRep, thus all fields are
127 // The encoded certificate.
128 private transient byte[] encoded;
130 // TBSCertificate part.
131 private transient byte[] tbsCertBytes;
132 private transient int version;
133 private transient BigInteger serialNo;
134 private transient OID algId;
135 private transient byte[] algVal;
136 private transient X500Principal issuer;
137 private transient Date notBefore;
138 private transient Date notAfter;
139 private transient X500Principal subject;
140 private transient PublicKey subjectKey;
141 private transient BitString issuerUniqueId;
142 private transient BitString subjectUniqueId;
143 private transient HashMap extensions;
144 private transient HashSet critOids;
145 private transient HashSet nonCritOids;
147 private transient BitString keyUsage;
148 private transient int basicConstraints = -1;
151 private transient OID sigAlgId;
152 private transient byte[] sigAlgVal;
153 private transient byte[] signature;
156 // ------------------------------------------------------------------------
159 * Create a new X.509 certificate from the encoded data. The input
160 * data are expected to be the ASN.1 DER encoding of the certificate.
162 * @param encoded The encoded certificate data.
163 * @throws IOException If the certificate cannot be read, possibly
164 * from a formatting error.
165 * @throws CertificateException If the data read is not an X.509
168 public X509Certificate(InputStream encoded)
169 throws CertificateException, IOException
172 extensions = new HashMap();
173 critOids = new HashSet();
174 nonCritOids = new HashSet();
179 catch (IOException ioe)
185 throw new CertificateException(e.toString());
189 // X509Certificate methods.
190 // ------------------------------------------------------------------------
192 public void checkValidity()
193 throws CertificateExpiredException, CertificateNotYetValidException
195 checkValidity(new Date());
198 public void checkValidity(Date date)
199 throws CertificateExpiredException, CertificateNotYetValidException
201 if (date.compareTo(notBefore) < 0)
202 throw new CertificateNotYetValidException();
203 if (date.compareTo(notAfter) > 0)
204 throw new CertificateExpiredException();
207 public int getVersion()
212 public BigInteger getSerialNumber()
217 public Principal getIssuerDN()
219 return getIssuerX500Principal();
222 public X500Principal getIssuerX500Principal()
227 public Principal getSubjectDN()
229 return getSubjectX500Principal();
232 public X500Principal getSubjectX500Principal()
237 public Date getNotBefore()
239 return (Date) notBefore.clone();
242 public Date getNotAfter()
244 return (Date) notAfter.clone();
247 public byte[] getTBSCertificate() throws CertificateEncodingException
249 return (byte[]) tbsCertBytes.clone();
252 public byte[] getSignature()
254 return (byte[]) signature.clone();
257 public String getSigAlgName()
259 if (sigAlgId.equals(ID_DSA_WITH_SHA1))
260 return "SHA1withDSA";
261 if (sigAlgId.equals(ID_RSA_WITH_MD2 ))
263 if (sigAlgId.equals(ID_RSA_WITH_MD5 ))
265 if (sigAlgId.equals(ID_RSA_WITH_SHA1 ))
266 return "SHA1withRSA";
268 // return sigAlgId.getShortName();
271 public String getSigAlgOID()
273 return sigAlgId.toString();
276 public byte[] getSigAlgParams()
278 return (byte[]) sigAlgVal.clone();
281 public boolean[] getIssuerUniqueID()
283 if (issuerUniqueId != null)
284 return issuerUniqueId.toBooleanArray();
288 public boolean[] getSubjectUniqueID()
290 if (subjectUniqueId != null)
291 return subjectUniqueId.toBooleanArray();
295 public boolean[] getKeyUsage()
297 if (keyUsage != null)
298 return keyUsage.toBooleanArray();
302 public List getExtendedKeyUsage() throws CertificateParsingException
304 byte[] ext = (byte[]) extensions.get("2.5.29.37");
307 LinkedList usages = new LinkedList();
310 DERReader der = new DERReader(new ByteArrayInputStream(ext));
311 DERValue seq = der.read();
312 if (!seq.isConstructed())
313 throw new CertificateParsingException();
315 while (len < seq.getLength())
317 DERValue oid = der.read();
318 if (!(oid.getValue() instanceof OID))
319 throw new CertificateParsingException();
320 usages.add(oid.getValue().toString());
321 len += DERWriter.definiteEncodingSize(oid.getLength())
322 + oid.getLength() + 1;
325 catch (IOException ioe)
327 throw new CertificateParsingException();
332 public int getBasicConstraints()
334 return basicConstraints;
337 public Collection getSubjectAlternativeNames()
338 throws CertificateParsingException
340 byte[] ext = getExtensionValue("2.5.29.17");
343 return getAltNames(ext);
346 public Collection getIssuerAlternativeNames()
347 throws CertificateParsingException
349 byte[] ext = getExtensionValue("2.5.29.18");
352 return getAltNames(ext);
355 \f// X509Extension methods.
356 // ------------------------------------------------------------------------
358 public boolean hasUnsupportedCriticalExtension()
360 for (Iterator it = critOids.iterator(); it.hasNext(); )
362 String oid = (String) it.next();
363 if (!oid.equals("2.5.29.15") && !oid.equals("2.5.29.17") &&
364 !oid.equals("2.5.29.18") && !oid.equals("2.5.29.19") &&
365 !oid.equals("2.5.29.37"))
371 public Set getCriticalExtensionOIDs()
373 return Collections.unmodifiableSet(critOids);
376 public Set getNonCriticalExtensionOIDs()
378 return Collections.unmodifiableSet(nonCritOids);
381 public byte[] getExtensionValue(String oid)
383 byte[] ext = (byte[]) extensions.get(oid);
385 return (byte[]) ext.clone();
389 // Certificate methods.
390 // ------------------------------------------------------------------------
392 public byte[] getEncoded() throws CertificateEncodingException
394 return (byte[]) encoded.clone();
397 public void verify(PublicKey key)
398 throws CertificateException, NoSuchAlgorithmException,
399 InvalidKeyException, NoSuchProviderException, SignatureException
401 Signature sig = Signature.getInstance(sigAlgId.toString());
405 public void verify(PublicKey key, String provider)
406 throws CertificateException, NoSuchAlgorithmException,
407 InvalidKeyException, NoSuchProviderException, SignatureException
409 Signature sig = Signature.getInstance(sigAlgId.toString(), provider);
413 public String toString()
415 // XXX say more than this.
416 return gnu.java.security.x509.X509Certificate.class.getName();
419 public PublicKey getPublicKey()
424 public Object writeReplace() throws java.io.ObjectStreamException
426 return super.writeReplace();
430 // ------------------------------------------------------------------------
433 * Verify this certificate's signature.
435 private void doVerify(Signature sig, PublicKey key)
436 throws CertificateException, InvalidKeyException, SignatureException
439 sig.update(tbsCertBytes);
440 if (!sig.verify(signature))
441 throw new CertificateException("signature not validated");
445 * Read a GeneralNames structure.
447 private List getAltNames(byte[] encoded)
448 throws CertificateParsingException
450 LinkedList names = new LinkedList();
453 ByteArrayInputStream in = new ByteArrayInputStream(encoded);
454 DERReader der = new DERReader(in);
455 DERValue seq = der.read();
456 if (!seq.isConstructed())
457 throw new CertificateParsingException();
459 while (len < seq.getLength())
461 DERValue name = der.read();
462 ArrayList pair = new ArrayList(2);
463 Object nameVal = null;
464 switch (name.getTag())
469 nameVal = new String((byte[]) name.getValue());
472 nameVal = java.net.InetAddress.getByAddress(
473 (byte[]) name.getValue()).getHostAddress();
476 nameVal = new OID((byte[]) name.getValue());
482 nameVal = name.getEncoded();
485 throw new CertificateParsingException();
487 pair.add(new Integer(name.getTag()));
490 if (name.isConstructed())
491 in.skip(name.getLength());
492 len += name.getEncodedLength();
495 catch (IOException ioe)
497 throw new CertificateParsingException(ioe.toString());
499 return Collections.unmodifiableList(names);
503 * Parse a DER stream into an X.509 certificate.
505 * @param encoded The encoded bytes.
507 private void parse(InputStream encoded) throws Exception
509 DERReader der = new DERReader(encoded);
511 // Certificate ::= SEQUENCE {
512 DERValue cert = der.read();
513 this.encoded = cert.getEncoded();
514 if (!cert.isConstructed())
515 throw new ASN1ParsingException("malformed Certificate");
517 // TBSCertificate ::= SEQUENCE {
518 DERValue tbsCert = der.read();
519 if (tbsCert.getValue() != DER.CONSTRUCTED_VALUE)
520 throw new ASN1ParsingException("malformed TBSCertificate");
521 tbsCertBytes = tbsCert.getEncoded();
523 DERValue val = der.read();
524 if (val.getTagClass() == DER.CONTEXT && val.getTag() == 0)
526 // Version ::= INTEGER [0] { v1(0), v2(1), v3(2) }
527 version = ((BigInteger) der.read().getValue()).intValue() + 1;
534 // SerialNumber ::= INTEGER
535 serialNo = (BigInteger) val.getValue();
537 // AlgorithmIdentifier ::= SEQUENCE {
539 if (!val.isConstructed())
540 throw new ASN1ParsingException("malformed AlgorithmIdentifier");
541 int certAlgLen = val.getLength();
543 algId = (OID) val.getValue();
544 if (certAlgLen > val.getEncodedLength())
550 algVal = val.getEncoded();
551 if (val.isConstructed())
552 encoded.skip(val.getLength());
555 issuer = new X500Principal(encoded);
557 if (!der.read().isConstructed())
558 throw new ASN1ParsingException("malformed Validity");
559 notBefore = (Date) der.read().getValue();
560 notAfter = (Date) der.read().getValue();
562 subject = new X500Principal(encoded);
564 if (!der.read().isConstructed())
565 throw new ASN1ParsingException("malformed SubjectPublicKeyInfo");
568 if (!val.isConstructed())
569 throw new ASN1ParsingException("malformed AlgorithmIdentifier");
570 int keyAlgLen = val.getLength();
572 OID keyID = (OID) val.getValue();
573 byte[] keyParams = null;
574 if (keyAlgLen > val.getEncodedLength())
577 keyParams = val.getEncoded();
580 if (val.isConstructed())
581 encoded.skip(val.getLength());
584 byte[] keyVal = ((BitString) val.getValue()).toByteArray();
586 if (keyID.equals(ID_DSA))
588 AlgorithmParameters params = AlgorithmParameters.getInstance("DSA");
589 params.init(keyParams, "ASN.1");
590 KeyFactory keyFac = KeyFactory.getInstance("DSA");
591 DSAParameterSpec spec = (DSAParameterSpec)
592 params.getParameterSpec(DSAParameterSpec.class);
593 subjectKey = keyFac.generatePublic(new DSAPublicKeySpec(
594 (BigInteger) new DERReader(keyVal).read().getValue(),
595 spec.getP(), spec.getQ(), spec.getG()));
597 else if (keyID.equals(ID_RSA))
599 KeyFactory keyFac = KeyFactory.getInstance("RSA");
600 DERReader rsaKey = new DERReader(keyVal);
601 if (!rsaKey.read().isConstructed())
602 throw new ASN1ParsingException("malformed RSAPublicKey");
603 subjectKey = keyFac.generatePublic(new RSAPublicKeySpec(
604 (BigInteger) rsaKey.read().getValue(),
605 (BigInteger) rsaKey.read().getValue()));
608 throw new ASN1ParsingException("unknown key algorithm " + keyID);
612 if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 1)
614 byte[] b = (byte[]) val.getValue();
615 issuerUniqueId = new BitString(b, 1, b.length-1, b[0] & 0xFF);
618 if (version >= 2 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 2)
620 byte[] b = (byte[]) val.getValue();
621 subjectUniqueId = new BitString(b, 1, b.length-1, b[0] & 0xFF);
624 if (version >= 3 && val.getTagClass() != DER.UNIVERSAL && val.getTag() == 3)
628 while (len < val.getLength())
630 DERValue ext = der.read();
631 OID extId = (OID) der.read().getValue();
632 DERValue val2 = der.read();
633 Boolean crit = Boolean.valueOf(false);
634 if (val2.getValue() instanceof Boolean)
636 crit = (Boolean) val2.getValue();
639 byte[] extVal = (byte[]) val2.getValue();
640 extensions.put(extId.toString(), extVal);
641 if (crit.booleanValue())
642 critOids.add(extId.toString());
644 nonCritOids.add(extId.toString());
645 if (extId.equals(ID_KEY_USAGE))
647 keyUsage = (BitString) DERReader.read(extVal).getValue();
649 else if (extId.equals(ID_BASIC_CONSTRAINTS))
651 DERReader bc = new DERReader(extVal);
652 DERValue constraints = bc.read();
653 if (!constraints.isConstructed())
654 throw new ASN1ParsingException("malformed BasicConstraints");
655 if (constraints.getLength() > 0)
660 if (val2.getValue() instanceof Boolean)
662 ca = ((Boolean) val2.getValue()).booleanValue();
663 if (constraints.getLength() > val2.getEncodedLength())
666 if (val2.getValue() instanceof BigInteger)
667 constr = ((BigInteger) val2.getValue()).intValue();
668 basicConstraints = constr;
671 len += ext.getEncodedLength();
676 if (!val.isConstructed())
677 throw new ASN1ParsingException("malformed AlgorithmIdentifier");
678 int sigAlgLen = val.getLength();
680 sigAlgId = (OID) val.getValue();
681 if (sigAlgLen > val.getEncodedLength())
684 if (val.getValue() == null)
685 sigAlgVal = keyParams;
687 sigAlgVal = (byte[]) val.getEncoded();
688 if (val.isConstructed())
689 encoded.skip(val.getLength());
691 signature = ((BitString) der.read().getValue()).toByteArray();