1package org.bouncycastle.jce.provider;
2
3import java.io.ByteArrayOutputStream;
4import java.io.IOException;
5import java.math.BigInteger;
6import java.security.InvalidKeyException;
7import java.security.NoSuchAlgorithmException;
8import java.security.NoSuchProviderException;
9import java.security.Principal;
10import java.security.Provider;
11import java.security.PublicKey;
12import java.security.Security;
13import java.security.Signature;
14import java.security.SignatureException;
15import java.security.cert.Certificate;
16import java.security.cert.CertificateEncodingException;
17import java.security.cert.CertificateException;
18import java.security.cert.CertificateExpiredException;
19import java.security.cert.CertificateNotYetValidException;
20import java.security.cert.CertificateParsingException;
21import java.security.cert.X509Certificate;
22import java.util.ArrayList;
23import java.util.Collections;
24import java.util.Date;
25import java.util.Enumeration;
26import java.util.HashSet;
27import java.util.List;
28import java.util.Set;
29
30import javax.security.auth.x500.X500Principal;
31
32import org.bouncycastle.asn1.ASN1Encodable;
33import org.bouncycastle.asn1.ASN1InputStream;
34import org.bouncycastle.asn1.ASN1Object;
35import org.bouncycastle.asn1.ASN1OutputStream;
36import org.bouncycastle.asn1.ASN1Sequence;
37import org.bouncycastle.asn1.DERBitString;
38import org.bouncycastle.asn1.DEREncodable;
39import org.bouncycastle.asn1.DERIA5String;
40import org.bouncycastle.asn1.DERNull;
41import org.bouncycastle.asn1.DERObjectIdentifier;
42import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
43import org.bouncycastle.asn1.misc.NetscapeCertType;
44import org.bouncycastle.asn1.misc.NetscapeRevocationURL;
45import org.bouncycastle.asn1.misc.VerisignCzagExtension;
46import org.bouncycastle.asn1.util.ASN1Dump;
47import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
48import org.bouncycastle.asn1.x509.BasicConstraints;
49import org.bouncycastle.asn1.x509.KeyUsage;
50import org.bouncycastle.asn1.x509.X509CertificateStructure;
51import org.bouncycastle.asn1.x509.X509Extension;
52import org.bouncycastle.asn1.x509.X509Extensions;
53import org.bouncycastle.jce.X509Principal;
54import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
55import org.bouncycastle.util.Arrays;
56import org.bouncycastle.util.encoders.Hex;
57
58public class X509CertificateObject
59    extends X509Certificate
60    implements PKCS12BagAttributeCarrier
61{
62    private X509CertificateStructure    c;
63    private BasicConstraints            basicConstraints;
64    private boolean[]                   keyUsage;
65    private boolean                     hashValueSet;
66    private int                         hashValue;
67
68    private PKCS12BagAttributeCarrier   attrCarrier = new PKCS12BagAttributeCarrierImpl();
69
70    public X509CertificateObject(
71        X509CertificateStructure    c)
72        throws CertificateParsingException
73    {
74        this.c = c;
75
76        try
77        {
78            byte[]  bytes = this.getExtensionBytes("2.5.29.19");
79
80            if (bytes != null)
81            {
82                basicConstraints = BasicConstraints.getInstance(ASN1Object.fromByteArray(bytes));
83            }
84        }
85        catch (Exception e)
86        {
87            throw new CertificateParsingException("cannot construct BasicConstraints: " + e);
88        }
89
90        try
91        {
92            byte[] bytes = this.getExtensionBytes("2.5.29.15");
93            if (bytes != null)
94            {
95                DERBitString    bits = DERBitString.getInstance(ASN1Object.fromByteArray(bytes));
96
97                bytes = bits.getBytes();
98                int length = (bytes.length * 8) - bits.getPadBits();
99
100                keyUsage = new boolean[(length < 9) ? 9 : length];
101
102                for (int i = 0; i != length; i++)
103                {
104                    keyUsage[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
105                }
106            }
107            else
108            {
109                keyUsage = null;
110            }
111        }
112        catch (Exception e)
113        {
114            throw new CertificateParsingException("cannot construct KeyUsage: " + e);
115        }
116    }
117
118    public void checkValidity()
119        throws CertificateExpiredException, CertificateNotYetValidException
120    {
121        this.checkValidity(new Date());
122    }
123
124    public void checkValidity(
125        Date    date)
126        throws CertificateExpiredException, CertificateNotYetValidException
127    {
128        if (date.getTime() > this.getNotAfter().getTime())  // for other VM compatibility
129        {
130            throw new CertificateExpiredException("certificate expired on " + c.getEndDate().getTime());
131        }
132
133        if (date.getTime() < this.getNotBefore().getTime())
134        {
135            throw new CertificateNotYetValidException("certificate not valid till " + c.getStartDate().getTime());
136        }
137    }
138
139    public int getVersion()
140    {
141        return c.getVersion();
142    }
143
144    public BigInteger getSerialNumber()
145    {
146        return c.getSerialNumber().getValue();
147    }
148
149    public Principal getIssuerDN()
150    {
151        return new X509Principal(c.getIssuer());
152    }
153
154    public X500Principal getIssuerX500Principal()
155    {
156        try
157        {
158            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
159            ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
160
161            aOut.writeObject(c.getIssuer());
162
163            return new X500Principal(bOut.toByteArray());
164        }
165        catch (IOException e)
166        {
167            throw new IllegalStateException("can't encode issuer DN");
168        }
169    }
170
171    public Principal getSubjectDN()
172    {
173        return new X509Principal(c.getSubject());
174    }
175
176    public X500Principal getSubjectX500Principal()
177    {
178        try
179        {
180            ByteArrayOutputStream   bOut = new ByteArrayOutputStream();
181            ASN1OutputStream        aOut = new ASN1OutputStream(bOut);
182
183            aOut.writeObject(c.getSubject());
184
185            return new X500Principal(bOut.toByteArray());
186        }
187        catch (IOException e)
188        {
189            throw new IllegalStateException("can't encode issuer DN");
190        }
191    }
192
193    public Date getNotBefore()
194    {
195        return c.getStartDate().getDate();
196    }
197
198    public Date getNotAfter()
199    {
200        return c.getEndDate().getDate();
201    }
202
203    public byte[] getTBSCertificate()
204        throws CertificateEncodingException
205    {
206        try
207        {
208            return c.getTBSCertificate().getEncoded(ASN1Encodable.DER);
209        }
210        catch (IOException e)
211        {
212            throw new CertificateEncodingException(e.toString());
213        }
214    }
215
216    public byte[] getSignature()
217    {
218        return c.getSignature().getBytes();
219    }
220
221    /**
222     * return a more "meaningful" representation for the signature algorithm used in
223     * the certficate.
224     */
225    public String getSigAlgName()
226    {
227        Provider    prov = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
228
229        if (prov != null)
230        {
231            String      algName = prov.getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
232
233            if (algName != null)
234            {
235                return algName;
236            }
237        }
238
239        Provider[] provs = Security.getProviders();
240
241        //
242        // search every provider looking for a real algorithm
243        //
244        for (int i = 0; i != provs.length; i++)
245        {
246            String algName = provs[i].getProperty("Alg.Alias.Signature." + this.getSigAlgOID());
247            if (algName != null)
248            {
249                return algName;
250            }
251        }
252
253        return this.getSigAlgOID();
254    }
255
256    /**
257     * return the object identifier for the signature.
258     */
259    public String getSigAlgOID()
260    {
261        return c.getSignatureAlgorithm().getObjectId().getId();
262    }
263
264    /**
265     * return the signature parameters, or null if there aren't any.
266     */
267    public byte[] getSigAlgParams()
268    {
269        if (c.getSignatureAlgorithm().getParameters() != null)
270        {
271            return c.getSignatureAlgorithm().getParameters().getDERObject().getDEREncoded();
272        }
273        else
274        {
275            return null;
276        }
277    }
278
279    public boolean[] getIssuerUniqueID()
280    {
281        DERBitString    id = c.getTBSCertificate().getIssuerUniqueId();
282
283        if (id != null)
284        {
285            byte[]          bytes = id.getBytes();
286            boolean[]       boolId = new boolean[bytes.length * 8 - id.getPadBits()];
287
288            for (int i = 0; i != boolId.length; i++)
289            {
290                boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
291            }
292
293            return boolId;
294        }
295
296        return null;
297    }
298
299    public boolean[] getSubjectUniqueID()
300    {
301        DERBitString    id = c.getTBSCertificate().getSubjectUniqueId();
302
303        if (id != null)
304        {
305            byte[]          bytes = id.getBytes();
306            boolean[]       boolId = new boolean[bytes.length * 8 - id.getPadBits()];
307
308            for (int i = 0; i != boolId.length; i++)
309            {
310                boolId[i] = (bytes[i / 8] & (0x80 >>> (i % 8))) != 0;
311            }
312
313            return boolId;
314        }
315
316        return null;
317    }
318
319    public boolean[] getKeyUsage()
320    {
321        return keyUsage;
322    }
323
324    public List getExtendedKeyUsage()
325        throws CertificateParsingException
326    {
327        byte[]  bytes = this.getExtensionBytes("2.5.29.37");
328
329        if (bytes != null)
330        {
331            try
332            {
333                ASN1InputStream dIn = new ASN1InputStream(bytes);
334                ASN1Sequence    seq = (ASN1Sequence)dIn.readObject();
335                List            list = new ArrayList();
336
337                for (int i = 0; i != seq.size(); i++)
338                {
339                    list.add(((DERObjectIdentifier)seq.getObjectAt(i)).getId());
340                }
341
342                return Collections.unmodifiableList(list);
343            }
344            catch (Exception e)
345            {
346                throw new CertificateParsingException("error processing extended key usage extension");
347            }
348        }
349
350        return null;
351    }
352
353    public int getBasicConstraints()
354    {
355        if (basicConstraints != null)
356        {
357            if (basicConstraints.isCA())
358            {
359                if (basicConstraints.getPathLenConstraint() == null)
360                {
361                    return Integer.MAX_VALUE;
362                }
363                else
364                {
365                    return basicConstraints.getPathLenConstraint().intValue();
366                }
367            }
368            else
369            {
370                return -1;
371            }
372        }
373
374        return -1;
375    }
376
377    public Set getCriticalExtensionOIDs()
378    {
379        if (this.getVersion() == 3)
380        {
381            Set             set = new HashSet();
382            X509Extensions  extensions = c.getTBSCertificate().getExtensions();
383
384            if (extensions != null)
385            {
386                Enumeration     e = extensions.oids();
387
388                while (e.hasMoreElements())
389                {
390                    DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
391                    X509Extension       ext = extensions.getExtension(oid);
392
393                    if (ext.isCritical())
394                    {
395                        set.add(oid.getId());
396                    }
397                }
398
399                return set;
400            }
401        }
402
403        return null;
404    }
405
406    private byte[] getExtensionBytes(String oid)
407    {
408        X509Extensions exts = c.getTBSCertificate().getExtensions();
409
410        if (exts != null)
411        {
412            X509Extension   ext = exts.getExtension(new DERObjectIdentifier(oid));
413            if (ext != null)
414            {
415                return ext.getValue().getOctets();
416            }
417        }
418
419        return null;
420    }
421
422    public byte[] getExtensionValue(String oid)
423    {
424        X509Extensions exts = c.getTBSCertificate().getExtensions();
425
426        if (exts != null)
427        {
428            X509Extension   ext = exts.getExtension(new DERObjectIdentifier(oid));
429
430            if (ext != null)
431            {
432                try
433                {
434                    return ext.getValue().getEncoded();
435                }
436                catch (Exception e)
437                {
438                    throw new IllegalStateException("error parsing " + e.toString());
439                }
440            }
441        }
442
443        return null;
444    }
445
446    public Set getNonCriticalExtensionOIDs()
447    {
448        if (this.getVersion() == 3)
449        {
450            Set             set = new HashSet();
451            X509Extensions  extensions = c.getTBSCertificate().getExtensions();
452
453            if (extensions != null)
454            {
455                Enumeration     e = extensions.oids();
456
457                while (e.hasMoreElements())
458                {
459                    DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
460                    X509Extension       ext = extensions.getExtension(oid);
461
462                    if (!ext.isCritical())
463                    {
464                        set.add(oid.getId());
465                    }
466                }
467
468                return set;
469            }
470        }
471
472        return null;
473    }
474
475    public boolean hasUnsupportedCriticalExtension()
476    {
477        if (this.getVersion() == 3)
478        {
479            X509Extensions  extensions = c.getTBSCertificate().getExtensions();
480
481            if (extensions != null)
482            {
483                Enumeration     e = extensions.oids();
484
485                while (e.hasMoreElements())
486                {
487                    DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();
488                    String              oidId = oid.getId();
489
490                    if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE)
491                     || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES)
492                     || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS)
493                     || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY)
494                     || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS)
495                     || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT)
496                     || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR)
497                     || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS)
498                     || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS)
499                     || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME)
500                     || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS))
501                    {
502                        continue;
503                    }
504
505                    X509Extension       ext = extensions.getExtension(oid);
506
507                    if (ext.isCritical())
508                    {
509                        return true;
510                    }
511                }
512            }
513        }
514
515        return false;
516    }
517
518    public PublicKey getPublicKey()
519    {
520        return JDKKeyFactory.createPublicKeyFromPublicKeyInfo(c.getSubjectPublicKeyInfo());
521    }
522
523    // BEGIN android-changed
524    private byte[] encoded;
525    // END android-changed
526    public byte[] getEncoded()
527        throws CertificateEncodingException
528    {
529        try
530        {
531            // BEGIN android-changed
532            if (encoded == null) {
533                encoded = c.getEncoded(ASN1Encodable.DER);
534            }
535            return encoded;
536            // END android-changed
537        }
538        catch (IOException e)
539        {
540            throw new CertificateEncodingException(e.toString());
541        }
542    }
543
544    public boolean equals(
545        Object o)
546    {
547        if (o == this)
548        {
549            return true;
550        }
551
552        if (!(o instanceof Certificate))
553        {
554            return false;
555        }
556
557        Certificate other = (Certificate)o;
558
559        try
560        {
561            byte[] b1 = this.getEncoded();
562            byte[] b2 = other.getEncoded();
563
564            return Arrays.areEqual(b1, b2);
565        }
566        catch (CertificateEncodingException e)
567        {
568            return false;
569        }
570    }
571
572    public synchronized int hashCode()
573    {
574        if (!hashValueSet)
575        {
576            hashValue = calculateHashCode();
577            hashValueSet = true;
578        }
579
580        return hashValue;
581    }
582
583    private int calculateHashCode()
584    {
585        try
586        {
587            int hashCode = 0;
588            byte[] certData = this.getEncoded();
589            for (int i = 1; i < certData.length; i++)
590            {
591                 hashCode += certData[i] * i;
592            }
593            return hashCode;
594        }
595        catch (CertificateEncodingException e)
596        {
597            return 0;
598        }
599    }
600
601    public void setBagAttribute(
602        DERObjectIdentifier oid,
603        DEREncodable        attribute)
604    {
605        attrCarrier.setBagAttribute(oid, attribute);
606    }
607
608    public DEREncodable getBagAttribute(
609        DERObjectIdentifier oid)
610    {
611        return attrCarrier.getBagAttribute(oid);
612    }
613
614    public Enumeration getBagAttributeKeys()
615    {
616        return attrCarrier.getBagAttributeKeys();
617    }
618
619    public String toString()
620    {
621        StringBuffer    buf = new StringBuffer();
622        String          nl = System.getProperty("line.separator");
623
624        buf.append("  [0]         Version: ").append(this.getVersion()).append(nl);
625        buf.append("         SerialNumber: ").append(this.getSerialNumber()).append(nl);
626        buf.append("             IssuerDN: ").append(this.getIssuerDN()).append(nl);
627        buf.append("           Start Date: ").append(this.getNotBefore()).append(nl);
628        buf.append("           Final Date: ").append(this.getNotAfter()).append(nl);
629        buf.append("            SubjectDN: ").append(this.getSubjectDN()).append(nl);
630        buf.append("           Public Key: ").append(this.getPublicKey()).append(nl);
631        buf.append("  Signature Algorithm: ").append(this.getSigAlgName()).append(nl);
632
633        byte[]  sig = this.getSignature();
634
635        buf.append("            Signature: ").append(new String(Hex.encode(sig, 0, 20))).append(nl);
636        for (int i = 20; i < sig.length; i += 20)
637        {
638            if (i < sig.length - 20)
639            {
640                buf.append("                       ").append(new String(Hex.encode(sig, i, 20))).append(nl);
641            }
642            else
643            {
644                buf.append("                       ").append(new String(Hex.encode(sig, i, sig.length - i))).append(nl);
645            }
646        }
647
648        X509Extensions  extensions = c.getTBSCertificate().getExtensions();
649
650        if (extensions != null)
651        {
652            Enumeration     e = extensions.oids();
653
654            if (e.hasMoreElements())
655            {
656                buf.append("       Extensions: \n");
657            }
658
659            while (e.hasMoreElements())
660            {
661                DERObjectIdentifier     oid = (DERObjectIdentifier)e.nextElement();
662                X509Extension           ext = extensions.getExtension(oid);
663
664                if (ext.getValue() != null)
665                {
666                    byte[]                  octs = ext.getValue().getOctets();
667                    ASN1InputStream         dIn = new ASN1InputStream(octs);
668                    buf.append("                       critical(").append(ext.isCritical()).append(") ");
669                    try
670                    {
671                        if (oid.equals(X509Extensions.BasicConstraints))
672                        {
673                            buf.append(new BasicConstraints((ASN1Sequence)dIn.readObject())).append(nl);
674                        }
675                        else if (oid.equals(X509Extensions.KeyUsage))
676                        {
677                            buf.append(new KeyUsage((DERBitString)dIn.readObject())).append(nl);
678                        }
679                        else if (oid.equals(MiscObjectIdentifiers.netscapeCertType))
680                        {
681                            buf.append(new NetscapeCertType((DERBitString)dIn.readObject())).append(nl);
682                        }
683                        else if (oid.equals(MiscObjectIdentifiers.netscapeRevocationURL))
684                        {
685                            buf.append(new NetscapeRevocationURL((DERIA5String)dIn.readObject())).append(nl);
686                        }
687                        else if (oid.equals(MiscObjectIdentifiers.verisignCzagExtension))
688                        {
689                            buf.append(new VerisignCzagExtension((DERIA5String)dIn.readObject())).append(nl);
690                        }
691                        else
692                        {
693                            buf.append(oid.getId());
694                            buf.append(" value = ").append(ASN1Dump.dumpAsString(dIn.readObject())).append(nl);
695                            //buf.append(" value = ").append("*****").append(nl);
696                        }
697                    }
698                    catch (Exception ex)
699                    {
700                        buf.append(oid.getId());
701                   //     buf.append(" value = ").append(new String(Hex.encode(ext.getValue().getOctets()))).append(nl);
702                        buf.append(" value = ").append("*****").append(nl);
703                    }
704                }
705                else
706                {
707                    buf.append(nl);
708                }
709            }
710        }
711
712        return buf.toString();
713    }
714
715    public final void verify(
716        PublicKey   key)
717        throws CertificateException, NoSuchAlgorithmException,
718        InvalidKeyException, NoSuchProviderException, SignatureException
719    {
720        Signature   signature;
721        String      sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
722
723        try
724        {
725            signature = Signature.getInstance(sigName, BouncyCastleProvider.PROVIDER_NAME);
726        }
727        catch (Exception e)
728        {
729            signature = Signature.getInstance(sigName);
730        }
731
732        checkSignature(key, signature);
733    }
734
735    public final void verify(
736        PublicKey   key,
737        String      sigProvider)
738        throws CertificateException, NoSuchAlgorithmException,
739        InvalidKeyException, NoSuchProviderException, SignatureException
740    {
741        String    sigName = X509SignatureUtil.getSignatureName(c.getSignatureAlgorithm());
742        Signature signature = Signature.getInstance(sigName, sigProvider);
743
744        checkSignature(key, signature);
745    }
746
747    private void checkSignature(
748        PublicKey key,
749        Signature signature)
750        throws CertificateException, NoSuchAlgorithmException,
751            SignatureException, InvalidKeyException
752    {
753        if (!isAlgIdEqual(c.getSignatureAlgorithm(), c.getTBSCertificate().getSignature()))
754        {
755            throw new CertificateException("signature algorithm in TBS cert not same as outer cert");
756        }
757
758        DEREncodable params = c.getSignatureAlgorithm().getParameters();
759
760        // TODO This should go after the initVerify?
761        X509SignatureUtil.setSignatureParameters(signature, params);
762
763        signature.initVerify(key);
764
765        signature.update(this.getTBSCertificate());
766
767        if (!signature.verify(this.getSignature()))
768        {
769            throw new InvalidKeyException("Public key presented not for certificate signature");
770        }
771    }
772
773    private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2)
774    {
775        if (!id1.getObjectId().equals(id2.getObjectId()))
776        {
777            return false;
778        }
779
780        if (id1.getParameters() == null)
781        {
782            if (id2.getParameters() != null && !id2.getParameters().equals(DERNull.INSTANCE))
783            {
784                return false;
785            }
786
787            return true;
788        }
789
790        if (id2.getParameters() == null)
791        {
792            if (id1.getParameters() != null && !id1.getParameters().equals(DERNull.INSTANCE))
793            {
794                return false;
795            }
796
797            return true;
798        }
799
800        return id1.getParameters().equals(id2.getParameters());
801    }
802}
803