1 /* X509Certificate.java -- X.509 certificate.
2 Copyright (C) 2003, 2004 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
.InputStream
;
42 import java
.io
.IOException
;
43 import java
.io
.PrintWriter
;
44 import java
.io
.Serializable
;
45 import java
.io
.StringWriter
;
47 import java
.math
.BigInteger
;
49 import java
.security
.AlgorithmParameters
;
50 import java
.security
.InvalidKeyException
;
51 import java
.security
.KeyFactory
;
52 import java
.security
.NoSuchAlgorithmException
;
53 import java
.security
.NoSuchProviderException
;
54 import java
.security
.Principal
;
55 import java
.security
.PublicKey
;
56 import java
.security
.Signature
;
57 import java
.security
.SignatureException
;
59 import java
.security
.cert
.CertificateEncodingException
;
60 import java
.security
.cert
.CertificateException
;
61 import java
.security
.cert
.CertificateExpiredException
;
62 import java
.security
.cert
.CertificateNotYetValidException
;
63 import java
.security
.cert
.CertificateParsingException
;
65 import java
.security
.interfaces
.DSAParams
;
66 import java
.security
.interfaces
.DSAPublicKey
;
67 import java
.security
.spec
.DSAParameterSpec
;
68 import java
.security
.spec
.X509EncodedKeySpec
;
70 import java
.util
.Arrays
;
71 import java
.util
.ArrayList
;
72 import java
.util
.Collection
;
73 import java
.util
.Collections
;
74 import java
.util
.Date
;
75 import java
.util
.HashMap
;
76 import java
.util
.HashSet
;
77 import java
.util
.Iterator
;
78 import java
.util
.List
;
82 import javax
.security
.auth
.x500
.X500Principal
;
84 import gnu
.java
.security
.OID
;
85 import gnu
.java
.security
.der
.*;
86 import gnu
.java
.security
.x509
.ext
.*;
89 * An implementation of X.509 certificates.
91 * @author Casey Marshall (rsdio@metastatic.org)
93 public class X509Certificate
extends java
.security
.cert
.X509Certificate
94 implements Serializable
, GnuPKIExtension
97 // Constants and fields.
98 // ------------------------------------------------------------------------
100 private static final boolean DEBUG
= false;
101 private static void debug(String msg
)
105 System
.err
.print(">> X509Certificate: ");
106 System
.err
.println(msg
);
109 private static void debug(Throwable t
)
113 System
.err
.print(">> X509Certificate: ");
118 protected static final OID ID_DSA
= new OID ("1.2.840.10040.4.1");
119 protected static final OID ID_DSA_WITH_SHA1
= new OID ("1.2.840.10040.4.3");
120 protected static final OID ID_RSA
= new OID ("1.2.840.113549.1.1.1");
121 protected static final OID ID_RSA_WITH_MD2
= new OID ("1.2.840.113549.1.1.2");
122 protected static final OID ID_RSA_WITH_MD5
= new OID ("1.2.840.113549.1.1.4");
123 protected static final OID ID_RSA_WITH_SHA1
= new OID ("1.2.840.113549.1.1.5");
124 protected static final OID ID_ECDSA_WITH_SHA1
= new OID ("1.2.840.10045.4.1");
126 // This object SHOULD be serialized with an instance of
127 // java.security.cert.Certificate.CertificateRep, thus all fields are
130 // The encoded certificate.
131 protected transient byte[] encoded
;
133 // TBSCertificate part.
134 protected transient byte[] tbsCertBytes
;
135 protected transient int version
;
136 protected transient BigInteger serialNo
;
137 protected transient OID algId
;
138 protected transient byte[] algVal
;
139 protected transient X500DistinguishedName issuer
;
140 protected transient Date notBefore
;
141 protected transient Date notAfter
;
142 protected transient X500DistinguishedName subject
;
143 protected transient PublicKey subjectKey
;
144 protected transient BitString issuerUniqueId
;
145 protected transient BitString subjectUniqueId
;
146 protected transient Map extensions
;
149 protected transient OID sigAlgId
;
150 protected transient byte[] sigAlgVal
;
151 protected transient byte[] signature
;
154 // ------------------------------------------------------------------------
157 * Create a new X.509 certificate from the encoded data. The input
158 * data are expected to be the ASN.1 DER encoding of the certificate.
160 * @param encoded The encoded certificate data.
161 * @throws IOException If the certificate cannot be read, possibly
162 * from a formatting error.
163 * @throws CertificateException If the data read is not an X.509
166 public X509Certificate(InputStream encoded
)
167 throws CertificateException
, IOException
170 extensions
= new HashMap();
175 catch (IOException ioe
)
183 CertificateException ce
= new CertificateException(e
.getMessage());
189 protected X509Certificate()
191 extensions
= new HashMap();
194 // X509Certificate methods.
195 // ------------------------------------------------------------------------
197 public void checkValidity()
198 throws CertificateExpiredException
, CertificateNotYetValidException
200 checkValidity(new Date());
203 public void checkValidity(Date date
)
204 throws CertificateExpiredException
, CertificateNotYetValidException
206 if (date
.compareTo(notBefore
) < 0)
208 throw new CertificateNotYetValidException();
210 if (date
.compareTo(notAfter
) > 0)
212 throw new CertificateExpiredException();
216 public int getVersion()
221 public BigInteger
getSerialNumber()
226 public Principal
getIssuerDN()
231 public X500Principal
getIssuerX500Principal()
233 return new X500Principal(issuer
.getDer());
236 public Principal
getSubjectDN()
241 public X500Principal
getSubjectX500Principal()
243 return new X500Principal(subject
.getDer());
246 public Date
getNotBefore()
248 return (Date
) notBefore
.clone();
251 public Date
getNotAfter()
253 return (Date
) notAfter
.clone();
256 public byte[] getTBSCertificate() throws CertificateEncodingException
258 return (byte[]) tbsCertBytes
.clone();
261 public byte[] getSignature()
263 return (byte[]) signature
.clone();
266 public String
getSigAlgName()
268 if (sigAlgId
.equals(ID_DSA_WITH_SHA1
))
270 return "SHA1withDSA";
272 if (sigAlgId
.equals(ID_RSA_WITH_MD2
))
276 if (sigAlgId
.equals(ID_RSA_WITH_MD5
))
280 if (sigAlgId
.equals(ID_RSA_WITH_SHA1
))
282 return "SHA1withRSA";
287 public String
getSigAlgOID()
289 return sigAlgId
.toString();
292 public byte[] getSigAlgParams()
294 return (byte[]) sigAlgVal
.clone();
297 public boolean[] getIssuerUniqueID()
299 if (issuerUniqueId
!= null)
301 return issuerUniqueId
.toBooleanArray();
306 public boolean[] getSubjectUniqueID()
308 if (subjectUniqueId
!= null)
310 return subjectUniqueId
.toBooleanArray();
315 public boolean[] getKeyUsage()
317 Extension e
= getExtension(KeyUsage
.ID
);
320 KeyUsage ku
= (KeyUsage
) e
.getValue();
321 boolean[] result
= new boolean[9];
322 boolean[] b
= ku
.getKeyUsage().toBooleanArray();
323 System
.arraycopy(b
, 0, result
, 0, b
.length
);
329 public List
getExtendedKeyUsage() throws CertificateParsingException
331 Extension e
= getExtension(ExtendedKeyUsage
.ID
);
334 List a
= ((ExtendedKeyUsage
) e
.getValue()).getPurposeIds();
335 List b
= new ArrayList(a
.size());
336 for (Iterator it
= a
.iterator(); it
.hasNext(); )
338 b
.add(it
.next().toString());
340 return Collections
.unmodifiableList(b
);
345 public int getBasicConstraints()
347 Extension e
= getExtension(BasicConstraints
.ID
);
350 return ((BasicConstraints
) e
.getValue()).getPathLengthConstraint();
355 public Collection
getSubjectAlternativeNames()
356 throws CertificateParsingException
358 Extension e
= getExtension(SubjectAlternativeNames
.ID
);
361 return ((SubjectAlternativeNames
) e
.getValue()).getNames();
366 public Collection
getIssuerAlternativeNames()
367 throws CertificateParsingException
369 Extension e
= getExtension(IssuerAlternativeNames
.ID
);
372 return ((IssuerAlternativeNames
) e
.getValue()).getNames();
377 \f// X509Extension methods.
378 // ------------------------------------------------------------------------
380 public boolean hasUnsupportedCriticalExtension()
382 for (Iterator it
= extensions
.values().iterator(); it
.hasNext(); )
384 Extension e
= (Extension
) it
.next();
385 if (e
.isCritical() && !e
.isSupported())
391 public Set
getCriticalExtensionOIDs()
393 HashSet s
= new HashSet();
394 for (Iterator it
= extensions
.values().iterator(); it
.hasNext(); )
396 Extension e
= (Extension
) it
.next();
398 s
.add(e
.getOid().toString());
400 return Collections
.unmodifiableSet(s
);
403 public Set
getNonCriticalExtensionOIDs()
405 HashSet s
= new HashSet();
406 for (Iterator it
= extensions
.values().iterator(); it
.hasNext(); )
408 Extension e
= (Extension
) it
.next();
410 s
.add(e
.getOid().toString());
412 return Collections
.unmodifiableSet(s
);
415 public byte[] getExtensionValue(String oid
)
417 Extension e
= getExtension(new OID(oid
));
420 return e
.getValue().getEncoded();
425 // GnuPKIExtension method.
426 // -------------------------------------------------------------------------
428 public Extension
getExtension(OID oid
)
430 return (Extension
) extensions
.get(oid
);
433 public Collection
getExtensions()
435 return extensions
.values();
438 // Certificate methods.
439 // -------------------------------------------------------------------------
441 public byte[] getEncoded() throws CertificateEncodingException
443 return (byte[]) encoded
.clone();
446 public void verify(PublicKey key
)
447 throws CertificateException
, NoSuchAlgorithmException
,
448 InvalidKeyException
, NoSuchProviderException
, SignatureException
450 Signature sig
= Signature
.getInstance(sigAlgId
.toString());
454 public void verify(PublicKey key
, String provider
)
455 throws CertificateException
, NoSuchAlgorithmException
,
456 InvalidKeyException
, NoSuchProviderException
, SignatureException
458 Signature sig
= Signature
.getInstance(sigAlgId
.toString(), provider
);
462 public String
toString()
464 StringWriter str
= new StringWriter();
465 PrintWriter out
= new PrintWriter(str
);
466 out
.println(X509Certificate
.class.getName() + " {");
467 out
.println(" TBSCertificate {");
468 out
.println(" version = " + version
+ ";");
469 out
.println(" serialNo = " + serialNo
+ ";");
470 out
.println(" signature = {");
471 out
.println(" algorithm = " + getSigAlgName() + ";");
472 out
.print(" parameters =");
473 if (sigAlgVal
!= null)
476 out
.print(Util
.hexDump(sigAlgVal
, " "));
480 out
.println(" null;");
483 out
.println(" issuer = " + issuer
.getName() + ";");
484 out
.println(" validity = {");
485 out
.println(" notBefore = " + notBefore
+ ";");
486 out
.println(" notAfter = " + notAfter
+ ";");
488 out
.println(" subject = " + subject
.getName() + ";");
489 out
.println(" subjectPublicKeyInfo = {");
490 out
.println(" algorithm = " + subjectKey
.getAlgorithm());
491 out
.println(" key =");
492 out
.print(Util
.hexDump(subjectKey
.getEncoded(), " "));
494 out
.println(" issuerUniqueId = " + issuerUniqueId
+ ";");
495 out
.println(" subjectUniqueId = " + subjectUniqueId
+ ";");
496 out
.println(" extensions = {");
497 for (Iterator it
= extensions
.values().iterator(); it
.hasNext(); )
499 out
.println(" " + it
.next());
503 out
.println(" signatureAlgorithm = " + getSigAlgName() + ";");
504 out
.println(" signatureValue =");
505 out
.print(Util
.hexDump(signature
, " "));
507 return str
.toString();
510 public PublicKey
getPublicKey()
515 public boolean equals(Object other
)
517 if (!(other
instanceof X509Certificate
))
521 if (other
instanceof X509Certificate
)
522 return Arrays
.equals(encoded
, ((X509Certificate
) other
).encoded
);
523 byte[] enc
= ((X509Certificate
) other
).getEncoded();
526 return Arrays
.equals(encoded
, enc
);
528 catch (CertificateEncodingException cee
)
535 // ------------------------------------------------------------------------
538 * Verify this certificate's signature.
540 private void doVerify(Signature sig
, PublicKey key
)
541 throws CertificateException
, InvalidKeyException
, SignatureException
543 debug("verifying sig=" + sig
+ " key=" + key
);
545 sig
.update(tbsCertBytes
);
546 if (!sig
.verify(signature
))
548 throw new CertificateException("signature not validated");
553 * Parse a DER stream into an X.509 certificate.
555 * @param encoded The encoded bytes.
557 private void parse(InputStream encoded
) throws Exception
559 DERReader der
= new DERReader(encoded
);
561 // Certificate ::= SEQUENCE {
562 DERValue cert
= der
.read();
563 debug("start Certificate len == " + cert
.getLength());
565 this.encoded
= cert
.getEncoded();
566 if (!cert
.isConstructed())
568 throw new IOException("malformed Certificate");
571 // TBSCertificate ::= SEQUENCE {
572 DERValue tbsCert
= der
.read();
573 if (tbsCert
.getValue() != DER
.CONSTRUCTED_VALUE
)
575 throw new IOException("malformed TBSCertificate");
577 tbsCertBytes
= tbsCert
.getEncoded();
578 debug("start TBSCertificate len == " + tbsCert
.getLength());
580 // Version ::= INTEGER [0] { v1(0), v2(1), v3(2) }
581 DERValue val
= der
.read();
582 if (val
.getTagClass() == DER
.CONTEXT
&& val
.getTag() == 0)
584 version
= ((BigInteger
) der
.read().getValue()).intValue() + 1;
591 debug("read version == " + version
);
593 // SerialNumber ::= INTEGER
594 serialNo
= (BigInteger
) val
.getValue();
595 debug("read serial number == " + serialNo
);
597 // AlgorithmIdentifier ::= SEQUENCE {
599 if (!val
.isConstructed())
601 throw new IOException("malformed AlgorithmIdentifier");
603 int certAlgLen
= val
.getLength();
604 debug("start AlgorithmIdentifier len == " + certAlgLen
);
607 // algorithm OBJECT IDENTIFIER,
608 algId
= (OID
) val
.getValue();
609 debug("read algorithm ID == " + algId
);
611 // parameters ANY DEFINED BY algorithm OPTIONAL }
612 if (certAlgLen
> val
.getEncodedLength())
621 algVal
= val
.getEncoded();
623 if (val
.isConstructed())
625 encoded
.skip(val
.getLength());
627 debug("read algorithm parameters == " + algVal
);
632 issuer
= new X500DistinguishedName(val
.getEncoded());
633 der
.skip(val
.getLength());
634 debug("read issuer == " + issuer
);
636 // Validity ::= SEQUENCE {
639 if (!der
.read().isConstructed())
641 throw new IOException("malformed Validity");
643 notBefore
= (Date
) der
.read().getValue();
644 notAfter
= (Date
) der
.read().getValue();
645 debug("read notBefore == " + notBefore
);
646 debug("read notAfter == " + notAfter
);
650 subject
= new X500DistinguishedName(val
.getEncoded());
651 der
.skip(val
.getLength());
652 debug("read subject == " + subject
);
654 // SubjectPublicKeyInfo ::= SEQUENCE {
655 // algorithm AlgorithmIdentifier,
656 // subjectPublicKey BIT STRING }
657 DERValue spki
= der
.read();
658 if (!spki
.isConstructed())
660 throw new IOException("malformed SubjectPublicKeyInfo");
662 KeyFactory spkFac
= KeyFactory
.getInstance("X.509");
663 subjectKey
= spkFac
.generatePublic(new X509EncodedKeySpec(spki
.getEncoded()));
664 der
.skip(spki
.getLength());
665 debug("read subjectPublicKey == " + subjectKey
);
671 if (version
>= 2 && val
.getTagClass() != DER
.UNIVERSAL
&& val
.getTag() == 1)
673 byte[] b
= (byte[]) val
.getValue();
674 issuerUniqueId
= new BitString(b
, 1, b
.length
-1, b
[0] & 0xFF);
675 debug("read issuerUniqueId == " + issuerUniqueId
);
678 if (version
>= 2 && val
.getTagClass() != DER
.UNIVERSAL
&& val
.getTag() == 2)
680 byte[] b
= (byte[]) val
.getValue();
681 subjectUniqueId
= new BitString(b
, 1, b
.length
-1, b
[0] & 0xFF);
682 debug("read subjectUniqueId == " + subjectUniqueId
);
685 if (version
>= 3 && val
.getTagClass() != DER
.UNIVERSAL
&& val
.getTag() == 3)
688 debug("start Extensions len == " + val
.getLength());
690 while (len
< val
.getLength())
692 DERValue ext
= der
.read();
693 debug("start extension len == " + ext
.getLength());
694 Extension e
= new Extension(ext
.getEncoded());
695 extensions
.put(e
.getOid(), e
);
696 der
.skip(ext
.getLength());
697 len
+= ext
.getEncodedLength();
698 debug("count == " + len
);
703 if (!val
.isConstructed())
705 throw new IOException("malformed AlgorithmIdentifier");
707 int sigAlgLen
= val
.getLength();
708 debug("start AlgorithmIdentifier len == " + sigAlgLen
);
710 sigAlgId
= (OID
) val
.getValue();
711 debug("read algorithm id == " + sigAlgId
);
712 if (sigAlgLen
> val
.getEncodedLength())
715 if (val
.getValue() == null)
717 if (subjectKey
instanceof DSAPublicKey
)
719 AlgorithmParameters params
=
720 AlgorithmParameters
.getInstance("DSA");
721 DSAParams dsap
= ((DSAPublicKey
) subjectKey
).getParams();
722 DSAParameterSpec spec
=
723 new DSAParameterSpec(dsap
.getP(), dsap
.getQ(), dsap
.getG());
725 sigAlgVal
= params
.getEncoded();
730 sigAlgVal
= (byte[]) val
.getEncoded();
732 if (val
.isConstructed())
734 encoded
.skip(val
.getLength());
736 debug("read parameters == " + sigAlgVal
);
738 signature
= ((BitString
) der
.read().getValue()).toByteArray();
739 debug("read signature ==\n" + Util
.hexDump(signature
, ">>>> "));