View Javadoc

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