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