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