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