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
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
656 }
657 }
658 catch (Exception ex)
659 {
660 buf.append(oid.getId());
661
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 }