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