KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ejbca > util > CertTools


1 /*************************************************************************
2  * *
3  * EJBCA: The OpenSource Certificate Authority *
4  * *
5  * This software is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU Lesser General Public *
7  * License as published by the Free Software Foundation; either *
8  * version 2.1 of the License, or any later version. *
9  * *
10  * See terms of license at gnu.org. *
11  * *
12  *************************************************************************/

13  
14 package org.ejbca.util;
15
16 import java.io.BufferedReader JavaDoc;
17 import java.io.ByteArrayInputStream JavaDoc;
18 import java.io.ByteArrayOutputStream JavaDoc;
19 import java.io.FileInputStream JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.InputStreamReader JavaDoc;
23 import java.io.PrintStream JavaDoc;
24 import java.math.BigInteger JavaDoc;
25 import java.net.URL JavaDoc;
26 import java.security.InvalidKeyException JavaDoc;
27 import java.security.MessageDigest JavaDoc;
28 import java.security.NoSuchAlgorithmException JavaDoc;
29 import java.security.NoSuchProviderException JavaDoc;
30 import java.security.PrivateKey JavaDoc;
31 import java.security.PublicKey JavaDoc;
32 import java.security.SecureRandom JavaDoc;
33 import java.security.Security JavaDoc;
34 import java.security.SignatureException JavaDoc;
35 import java.security.cert.CRLException JavaDoc;
36 import java.security.cert.Certificate JavaDoc;
37 import java.security.cert.CertificateEncodingException JavaDoc;
38 import java.security.cert.CertificateException JavaDoc;
39 import java.security.cert.CertificateFactory JavaDoc;
40 import java.security.cert.CertificateParsingException JavaDoc;
41 import java.security.cert.X509CRL JavaDoc;
42 import java.security.cert.X509Certificate JavaDoc;
43 import java.util.ArrayList JavaDoc;
44 import java.util.Collection JavaDoc;
45 import java.util.Date JavaDoc;
46 import java.util.Hashtable JavaDoc;
47 import java.util.Iterator JavaDoc;
48 import java.util.List JavaDoc;
49 import java.util.Vector JavaDoc;
50
51 import org.apache.commons.lang.BooleanUtils;
52 import org.apache.commons.lang.StringUtils;
53 import org.apache.log4j.Logger;
54 import org.bouncycastle.asn1.ASN1EncodableVector;
55 import org.bouncycastle.asn1.ASN1InputStream;
56 import org.bouncycastle.asn1.ASN1OctetString;
57 import org.bouncycastle.asn1.ASN1Sequence;
58 import org.bouncycastle.asn1.ASN1TaggedObject;
59 import org.bouncycastle.asn1.DERBitString;
60 import org.bouncycastle.asn1.DEREncodable;
61 import org.bouncycastle.asn1.DERIA5String;
62 import org.bouncycastle.asn1.DERObject;
63 import org.bouncycastle.asn1.DERObjectIdentifier;
64 import org.bouncycastle.asn1.DEROctetString;
65 import org.bouncycastle.asn1.DERSequence;
66 import org.bouncycastle.asn1.DERTaggedObject;
67 import org.bouncycastle.asn1.DERUTF8String;
68 import org.bouncycastle.asn1.x509.AccessDescription;
69 import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
70 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
71 import org.bouncycastle.asn1.x509.BasicConstraints;
72 import org.bouncycastle.asn1.x509.GeneralName;
73 import org.bouncycastle.asn1.x509.GeneralNames;
74 import org.bouncycastle.asn1.x509.PolicyInformation;
75 import org.bouncycastle.asn1.x509.ReasonFlags;
76 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
77 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
78 import org.bouncycastle.asn1.x509.X509DefaultEntryConverter;
79 import org.bouncycastle.asn1.x509.X509Extensions;
80 import org.bouncycastle.asn1.x509.X509Name;
81 import org.bouncycastle.asn1.x509.X509NameEntryConverter;
82 import org.bouncycastle.asn1.x509.X509NameTokenizer;
83 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
84 import org.bouncycastle.jce.X509KeyUsage;
85 import org.bouncycastle.jce.interfaces.ConfigurableProvider;
86 import org.bouncycastle.jce.provider.BouncyCastleProvider;
87 import org.bouncycastle.math.ec.ECCurve;
88 import org.bouncycastle.util.encoders.Hex;
89 import org.bouncycastle.x509.X509V3CertificateGenerator;
90 import org.ejbca.core.model.ca.crl.RevokedCertInfo;
91 import org.ejbca.util.dn.DNFieldExtractor;
92 import org.ejbca.util.dn.DnComponents;
93
94
95 /**
96  * Tools to handle common certificate operations.
97  *
98  * @version $Id: CertTools.java,v 1.35.2.4 2007/08/09 09:03:40 anatom Exp $
99  */

100 public class CertTools {
101     private static Logger log = Logger.getLogger(CertTools.class);
102     
103     // Initialize dnComponents
104
static {
105         DnComponents.getDnObjects();
106     }
107     public static final String JavaDoc EMAIL = "rfc822name";
108     public static final String JavaDoc EMAIL1 = "email";
109     public static final String JavaDoc EMAIL2 = "EmailAddress";
110     public static final String JavaDoc EMAIL3 = "E";
111     public static final String JavaDoc DNS = "dNSName";
112     public static final String JavaDoc URI = "uniformResourceIdentifier";
113     public static final String JavaDoc URI1 = "uri";
114     public static final String JavaDoc URI2 = "uniformResourceId";
115     public static final String JavaDoc IPADDR = "iPAddress";
116     public static final String JavaDoc DIRECTORYNAME = "directoryName";
117
118     /** Microsoft altName for windows smart card logon */
119     public static final String JavaDoc UPN = "upn";
120     /** ObjectID for upn altName for windows smart card logon */
121     public static final String JavaDoc UPN_OBJECTID = "1.3.6.1.4.1.311.20.2.3";
122     /** Microsoft altName for windows domain controller guid */
123     public static final String JavaDoc GUID = "guid";
124     /** ObjectID for upn altName for windows domain controller guid */
125     public static final String JavaDoc GUID_OBJECTID = "1.3.6.1.4.1.311.25.1";
126     /** Object id id-pkix */
127     public static final String JavaDoc id_pkix = "1.3.6.1.5.5.7";
128     /** Object id id-pda */
129     public static final String JavaDoc id_pda = id_pkix + ".9";
130     /** Object id id-pda-dateOfBirth
131      * DateOfBirth ::= GeneralizedTime
132      */

133     public static final String JavaDoc id_pda_dateOfBirth = id_pda + ".1";
134     /** Object id id-pda-placeOfBirth
135      * PlaceOfBirth ::= DirectoryString
136      */

137     public static final String JavaDoc id_pda_placeOfBirth = id_pda + ".2";
138     /** Object id id-pda-gender
139      * Gender ::= PrintableString (SIZE(1))
140      * -- "M", "F", "m" or "f"
141      */

142     public static final String JavaDoc id_pda_gender = id_pda + ".3";
143     /** Object id id-pda-countryOfCitizenship
144      * CountryOfCitizenship ::= PrintableString (SIZE (2))
145      * -- ISO 3166 Country Code
146      */

147     public static final String JavaDoc id_pda_countryOfCitizenship = id_pda + ".4";
148     /** Object id id-pda-countryOfResidence
149      * CountryOfResidence ::= PrintableString (SIZE (2))
150      * -- ISO 3166 Country Code
151      */

152     public static final String JavaDoc id_pda_countryOfResidence = id_pda + ".5";
153     /** Object id for qcStatements Extension */
154     public static final String JavaDoc QCSTATEMENTS_OBJECTID = "1.3.6.1.5.5.7.1.3";
155     /** OID used for creating MS Templates */
156     public static final String JavaDoc OID_MSTEMPLATE = "1.3.6.1.4.1.311.20.2";
157           
158     
159     private static final String JavaDoc[] EMAILIDS = { EMAIL, EMAIL1, EMAIL2, EMAIL3 };
160     /** ObjectID for unstructuredName DN attribute */
161     //public static final DERObjectIdentifier unstructuredName = new DERObjectIdentifier("1.2.840.113549.1.9.2");
162
/** ObjectID for unstructuredAddress DN attribute */
163     //public static final DERObjectIdentifier unstructuredAddress = new DERObjectIdentifier("1.2.840.113549.1.9.8");
164

165     /** Parameters used when generating or verifying ECDSA keys/certs using the "implicitlyCA" key encoding.
166      * The curve parameters is then defined outside of the key and configured in the BC provider.
167      */

168     private static String JavaDoc IMPLICITLYCA_Q = "@ecdsa.implicitlyca.q@";
169     private static String JavaDoc IMPLICITLYCA_A = "@ecdsa.implicitlyca.a@";
170     private static String JavaDoc IMPLICITLYCA_B = "@ecdsa.implicitlyca.b@";
171     private static String JavaDoc IMPLICITLYCA_G = "@ecdsa.implicitlyca.g@";
172     private static String JavaDoc IMPLICITLYCA_N = "@ecdsa.implicitlyca.n@";
173
174     /** Flag indicating if the BC provider should be removed before installing it again. When developing and re-deploying alot
175      * this is needed so you don't have to restart JBoss all the time.
176      * In production it may cause failures because the BC provider may get removed just when another thread wants to use it.
177      * Therefore the default value is false.
178      */

179     private static final boolean developmentProviderInstallation = BooleanUtils.toBoolean("@development.provider.installation@");
180     
181     /**
182      * inhibits creation of new CertTools
183      */

184     protected CertTools() {
185     }
186
187     /** See stringToBcX509Name(String, X509NameEntryConverter), this method uses the default BC converter (X509DefaultEntryConverter)
188      * @see #stringToBcX509Name(String, X509NameEntryConverter)
189      * @param dn
190      * @param dn
191      * String containing DN that will be transformed into X509Name, The
192      * DN string has the format "CN=zz,OU=yy,O=foo,C=SE". Unknown OIDs in
193      * the string will be added to the end positions of OID array.
194      *
195      * @return X509Name or null if input is null
196      */

197     public static X509Name stringToBcX509Name(String JavaDoc dn) {
198         X509NameEntryConverter converter = new X509DefaultEntryConverter();
199         return stringToBcX509Name(dn, converter);
200         
201         
202     }
203     /**
204      * Creates a (Bouncycastle) X509Name object from a string with a DN. Known OID
205      * (with order) are:
206      * <code> EmailAddress, UID, CN, SN (SerialNumber), GivenName, Initials, SurName, T, OU,
207      * O, L, ST, DC, C </code>
208      * To change order edit 'dnObjects' in this source file. Important NOT to mess
209      * with the ordering within this class, since cert vierification on some
210      * clients (IE :-() might depend on order.
211      *
212      * @param dn
213      * String containing DN that will be transformed into X509Name, The
214      * DN string has the format "CN=zz,OU=yy,O=foo,C=SE". Unknown OIDs in
215      * the string will be added to the end positions of OID array.
216      * @param converter BC converter for DirectoryStrings, that determines which encoding is chosen
217      * @return X509Name or null if input is null
218      */

219     public static X509Name stringToBcX509Name(String JavaDoc dn, X509NameEntryConverter converter) {
220       //log.debug(">stringToBcX509Name: " + dn);
221
if (dn == null)
222         return null;
223
224       Vector JavaDoc defaultOrdering = new Vector JavaDoc();
225       Vector JavaDoc values = new Vector JavaDoc();
226       X509NameTokenizer xt = new X509NameTokenizer(dn);
227
228       while (xt.hasMoreTokens()) {
229         // This is a pair (CN=xx)
230
String JavaDoc pair = xt.nextToken();
231         int ix = pair.indexOf("=");
232
233         if (ix != -1) {
234           String JavaDoc key = pair.substring(0, ix).toLowerCase();
235           String JavaDoc val = pair.substring(ix + 1);
236
237           // -- First search the OID by name in declared OID's
238
DERObjectIdentifier oid = DnComponents.getOid(key);
239
240           try {
241               // -- If isn't declared, we try to create it
242
if (oid == null) {
243                 oid = new DERObjectIdentifier(key);
244               }
245               defaultOrdering.add(oid);
246               values.add(val);
247           } catch (IllegalArgumentException JavaDoc e) {
248               // If it is not an OID we will ignore it
249
log.warn("Unknown DN component ignored and silently dropped: " + key);
250           }
251
252         } else {
253             log.warn("Huh, what's this? DN: " + dn+" PAIR: "+pair);
254         }
255       }
256
257       X509Name x509Name = new X509Name(defaultOrdering, values, converter);
258
259       //-- Reorder fields
260
X509Name orderedX509Name = getOrderedX509Name(x509Name, getDefaultX509FieldOrder(), converter);
261
262       //log.debug("<stringToBcX509Name");
263
return orderedX509Name;
264     } // stringToBcX509Name
265

266
267
268     /**
269      * Every DN-string should look the same. Creates a name string ordered and looking like we want
270      * it...
271      *
272      * @param dn String containing DN
273      *
274      * @return String containing DN, or null if input is null
275      */

276     public static String JavaDoc stringToBCDNString(String JavaDoc dn) {
277         //log.debug(">stringToBcDNString: "+dn);
278
if (isDNReversed(dn)) {
279             dn = reverseDN(dn);
280         }
281         String JavaDoc ret = null;
282         X509Name name = stringToBcX509Name(dn);
283         if (name != null) {
284             ret = name.toString();
285         }
286         //log.debug("<stringToBcDNString: "+ret);
287
return ret;
288     }
289
290     /**
291      * Convenience method for getting an email addresses from a DN. Uses {@link
292      * getPartsFromDN(String,String)} internally, and searches for {@link EMAIL}, {@link EMAIL1},
293      * {@link EMAIL2}, {@link EMAIL3} and returns the first one found.
294      *
295      * @param dn the DN
296      *
297      * @return ArrayList containing email or empty list if email is not present
298      * @return the found email address, or <code>null</code> if none is found
299      */

300     public static ArrayList JavaDoc getEmailFromDN(String JavaDoc dn) {
301         log.debug(">getEmailFromDN(" + dn + ")");
302         ArrayList JavaDoc ret = new ArrayList JavaDoc();
303         for (int i = 0; i < EMAILIDS.length ; i++) {
304             ArrayList JavaDoc emails = getPartsFromDN(dn, EMAILIDS[i]);
305             if (emails.size() > 0) {
306                 ret.addAll(emails);
307             }
308             
309         }
310         log.debug("<getEmailFromDN(" + dn + "): " + ret.size());
311         return ret;
312     }
313     
314     /**
315      * Search for e-mail address, first in SubjectAltName (as in PKIX
316      * recomandation) then in subject DN.
317      * Original author: Marco Ferrante, (c) 2005 CSITA - University of Genoa (Italy)
318      *
319      * @param certificate
320      * @return subject email or null if not present in certificate
321      */

322     public static String JavaDoc getEMailAddress(X509Certificate JavaDoc certificate) {
323         log.debug("Searching for EMail Address in SubjectAltName");
324         if (certificate == null) {
325             return null;
326         }
327         try {
328             if (certificate.getSubjectAlternativeNames() != null) {
329                 java.util.Collection JavaDoc altNames = certificate.getSubjectAlternativeNames();
330                 Iterator JavaDoc iter = altNames.iterator();
331                 while (iter.hasNext()) {
332                     java.util.List JavaDoc item = (java.util.List JavaDoc)iter.next();
333                     Integer JavaDoc type = (Integer JavaDoc)item.get(0);
334                     if (type.intValue() == 1) {
335                         return (String JavaDoc)item.get(1);
336                     }
337                 }
338             }
339         } catch (CertificateParsingException JavaDoc e) {
340             log.error("Error parsing certificate: ", e);
341         }
342         log.debug("Searching for EMail Address in Subject DN");
343         ArrayList JavaDoc emails = CertTools.getEmailFromDN(certificate.getSubjectDN().getName());
344         if (emails.size() > 0) {
345             return (String JavaDoc)emails.get(0);
346         }
347         return null;
348     }
349     
350     /**
351      * Takes a DN and reverses it completely so the first attribute ends up last.
352      * C=SE,O=Foo,CN=Bar becomes CN=Bar,O=Foo,C=SE.
353      *
354      * @param dn String containing DN to be reversed, The DN string has the format "C=SE, O=xx, OU=yy, CN=zz".
355      *
356      * @return String containing reversed DN
357      */

358     public static String JavaDoc reverseDN(String JavaDoc dn) {
359         log.debug(">reverseDN: dn: " + dn);
360         String JavaDoc ret = null;
361         if (dn != null) {
362             String JavaDoc o;
363             BasicX509NameTokenizer xt = new BasicX509NameTokenizer(dn);
364             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
365             boolean first = true;
366             while (xt.hasMoreTokens()) {
367                 o = xt.nextToken();
368                 //log.debug("token: "+o);
369
if (!first) {
370                     buf.insert(0,",");
371                 } else {
372                     first = false;
373                 }
374                 buf.insert(0,o);
375             }
376             if (buf.length() > 0) {
377                 ret = buf.toString();
378             }
379         }
380         
381         log.debug("<reverseDN: resulting dn: " + ret);
382         return ret;
383     } //reverseDN
384

385     /**
386      * Tries to determine if a DN is in reversed form. It does this by taking the last attribute
387      * and the first attribute. If the last attribute comes before the first in the dNObjects array
388      * the DN is assumed to be in reversed order.
389      * The check if a DN is revered is relative to the default ordering, so if the default ordering is:
390      * "C=SE, O=PrimeKey, CN=Tomas" (dNObjectsReverse ordering in EJBCA) a dn or form "CN=Tomas, O=PrimeKey, C=SE" is reversed.
391      *
392      * if the default ordering is:
393      * "CN=Tomas, O=PrimeKey, C=SE" (dNObjectsForward ordering in EJBCA) a dn or form "C=SE, O=PrimeKey, CN=Tomas" is reversed.
394      *
395      *
396      * @param dn String containing DN to be checked, The DN string has the format "C=SE, O=xx, OU=yy, CN=zz".
397      *
398      * @return true if the DN is believed to be in reversed order, false otherwise
399      */

400     protected static boolean isDNReversed(String JavaDoc dn) {
401         //log.debug(">isDNReversed: dn: " + dn);
402
boolean ret = false;
403         if (dn != null) {
404             String JavaDoc first = null;
405             String JavaDoc last = null;
406             X509NameTokenizer xt = new X509NameTokenizer(dn);
407             if (xt.hasMoreTokens()) {
408                 first = xt.nextToken();
409             }
410             while (xt.hasMoreTokens()) {
411                 last = xt.nextToken();
412             }
413             String JavaDoc[] dNObjects = DnComponents.getDnObjects();
414             if ( (first != null) && (last != null) ) {
415                 first = first.substring(0,first.indexOf('='));
416                 last = last.substring(0,last.indexOf('='));
417                 int firsti = 0, lasti = 0;
418                 for (int i = 0; i < dNObjects.length; i++) {
419                     if (first.toLowerCase().equals(dNObjects[i])) {
420                         firsti = i;
421                     }
422                     if (last.toLowerCase().equals(dNObjects[i])) {
423                         lasti = i;
424                     }
425                 }
426                 if (lasti < firsti) {
427                     ret = true;
428                 }
429                 
430             }
431         }
432         //log.debug("<isDNReversed: " + ret);
433
return ret;
434     } //isDNReversed
435

436     /**
437      * Gets a specified part of a DN. Specifically the first occurrence it the DN contains several
438      * instances of a part (i.e. cn=x, cn=y returns x).
439      *
440      * @param dn String containing DN, The DN string has the format "C=SE, O=xx, OU=yy, CN=zz".
441      * @param dnpart String specifying which part of the DN to get, should be "CN" or "OU" etc.
442      *
443      * @return String containing dnpart or null if dnpart is not present
444      */

445     public static String JavaDoc getPartFromDN(String JavaDoc dn, String JavaDoc dnpart) {
446         log.debug(">getPartFromDN: dn:'" + dn + "', dnpart=" + dnpart);
447         String JavaDoc part = null;
448         if ((dn != null) && (dnpart != null)) {
449             String JavaDoc o;
450             dnpart += "="; // we search for 'CN=' etc.
451
X509NameTokenizer xt = new X509NameTokenizer(dn);
452             while (xt.hasMoreTokens()) {
453                 o = xt.nextToken();
454                 //log.debug("checking: "+o.substring(0,dnpart.length()));
455
if ((o.length() > dnpart.length()) &&
456                         o.substring(0, dnpart.length()).equalsIgnoreCase(dnpart)) {
457                     part = o.substring(dnpart.length());
458
459                     break;
460                 }
461             }
462         }
463         log.debug("<getpartFromDN: resulting DN part=" + part);
464         return part;
465     } //getPartFromDN
466

467     /**
468      * Gets a specified parts of a DN. Returns all occurences as an ArrayList, also works if DN contains several
469      * instances of a part (i.e. cn=x, cn=y returns {x, y, null}).
470      *
471      * @param dn String containing DN, The DN string has the format "C=SE, O=xx, OU=yy, CN=zz".
472      * @param dnpart String specifying which part of the DN to get, should be "CN" or "OU" etc.
473      *
474      * @return ArrayList containing dnparts or empty list if dnpart is not present
475      */

476     public static ArrayList JavaDoc getPartsFromDN(String JavaDoc dn, String JavaDoc dnpart) {
477         log.debug(">getPartsFromDN: dn:'" + dn + "', dnpart=" + dnpart);
478         ArrayList JavaDoc parts = new ArrayList JavaDoc();
479         if ((dn != null) && (dnpart != null)) {
480             String JavaDoc o;
481             dnpart += "="; // we search for 'CN=' etc.
482
X509NameTokenizer xt = new X509NameTokenizer(dn);
483             while (xt.hasMoreTokens()) {
484                 o = xt.nextToken();
485                 if ((o.length() > dnpart.length()) &&
486                         o.substring(0, dnpart.length()).equalsIgnoreCase(dnpart)) {
487                     parts.add(o.substring(dnpart.length()));
488                 }
489             }
490         }
491         log.debug("<getpartsFromDN: resulting DN part=" + parts.toString());
492         return parts;
493     } //getPartFromDN
494

495     /**
496      * Gets a list of all custom OIDs defined in the string. A custom OID is defined as an OID, simply as that. Otherwise, if it is not a custom oid, the DNpart is defined by a name such as CN och rfc822Name.
497      *
498      * @param dn String containing DN, The DN string has the format "C=SE, O=xx, OU=yy, CN=zz", or "rfc822Name=foo@bar.com", etc.
499      * @param dnpart String specifying which part of the DN to get, should be "CN" or "OU" etc.
500      *
501      * @return ArrayList containing oids or empty list if no custom OIDs are present
502      */

503     public static ArrayList JavaDoc getCustomOids(String JavaDoc dn) {
504         log.debug(">getCustomOids: dn:'" + dn);
505         ArrayList JavaDoc parts = new ArrayList JavaDoc();
506         if (dn != null) {
507             String JavaDoc o;
508             X509NameTokenizer xt = new X509NameTokenizer(dn);
509             while (xt.hasMoreTokens()) {
510                 o = xt.nextToken();
511                 // Try to see if it is a valid OID
512
try {
513                     int i = o.indexOf('=');
514                     // An oid is never shorter than 3 chars and must start with 1.
515
if ( (i > 2) && (o.charAt(1) == '.') ) {
516                         String JavaDoc oid = o.substring(0, i);
517                         new DERObjectIdentifier(oid);
518                         parts.add(oid);
519                     }
520                 } catch (IllegalArgumentException JavaDoc e) {
521                     // Not a valid oid
522
}
523             }
524         }
525         log.debug("<getpartsFromDN: resulting DN part=" + parts.toString());
526         return parts;
527     } //getPartFromDN
528

529     /**
530      * Gets subject DN in the format we are sure about (BouncyCastle),supporting UTF8.
531      *
532      * @param cert X509Certificate
533      *
534      * @return String containing the subjects DN.
535      */

536     public static String JavaDoc getSubjectDN(X509Certificate JavaDoc cert) {
537         return getDN(cert, 1);
538     }
539
540     /**
541      * Gets issuer DN in the format we are sure about (BouncyCastle),supporting UTF8.
542      *
543      * @param cert X509Certificate
544      *
545      * @return String containing the issuers DN.
546      */

547     public static String JavaDoc getIssuerDN(X509Certificate JavaDoc cert) {
548         return getDN(cert, 2);
549     }
550
551     /**
552      * Gets subject or issuer DN in the format we are sure about (BouncyCastle),supporting UTF8.
553      *
554      * @param cert X509Certificate
555      * @param which 1 = subjectDN, anything else = issuerDN
556      *
557      * @return String containing the DN.
558      */

559     private static String JavaDoc getDN(X509Certificate JavaDoc cert, int which) {
560         //log.debug(">getDN("+which+")");
561
String JavaDoc dn = null;
562         if (cert == null) {
563             return dn;
564         }
565         try {
566             CertificateFactory JavaDoc cf = CertTools.getCertificateFactory();
567             X509Certificate JavaDoc x509cert = (X509Certificate JavaDoc) cf.generateCertificate(new ByteArrayInputStream JavaDoc(
568                         cert.getEncoded()));
569             //log.debug("Created certificate of class: " + x509cert.getClass().getName());
570

571             if (which == 1) {
572                 dn = x509cert.getSubjectDN().toString();
573             } else {
574                 dn = x509cert.getIssuerDN().toString();
575             }
576         } catch (CertificateException JavaDoc ce) {
577             log.error("CertificateException: ", ce);
578             return null;
579         }
580         //log.debug("<getDN("+which+"):"+dn);
581
return stringToBCDNString(dn);
582     } // getDN
583

584     /**
585      * Gets issuer DN for CRL in the format we are sure about (BouncyCastle),supporting UTF8.
586      *
587      * @param crl X509RL
588      *
589      * @return String containing the DN.
590      */

591     public static String JavaDoc getIssuerDN(X509CRL JavaDoc crl) {
592         //log.debug(">getIssuerDN(crl)");
593
String JavaDoc dn = null;
594         try {
595             CertificateFactory JavaDoc cf = CertTools.getCertificateFactory();
596             X509CRL JavaDoc x509crl = (X509CRL JavaDoc) cf.generateCRL(new ByteArrayInputStream JavaDoc(crl.getEncoded()));
597             //log.debug("Created certificate of class: " + x509crl.getClass().getName());
598
dn = x509crl.getIssuerDN().toString();
599         } catch (CRLException JavaDoc ce) {
600             log.error("CRLException: ", ce);
601             return null;
602         }
603         //log.debug("<getIssuerDN(crl):"+dn);
604
return stringToBCDNString(dn);
605     } // getIssuerDN
606

607     public static CertificateFactory JavaDoc getCertificateFactory() {
608         try {
609             return CertificateFactory.getInstance("X.509", "BC");
610         } catch (NoSuchProviderException JavaDoc nspe) {
611             log.error("NoSuchProvider: ", nspe);
612         } catch (CertificateException JavaDoc ce) {
613             log.error("CertificateException: ", ce);
614         }
615         return null;
616     }
617
618     public static synchronized void removeBCProvider() {
619         Security.removeProvider("BC");
620     }
621     public static synchronized void installBCProvider() {
622         // A flag that ensures that we intall the parameters for implcitlyCA only when we have installed a new provider
623
boolean installImplicitlyCA = false;
624         if (Security.addProvider(new BouncyCastleProvider()) < 0) {
625             // If already installed, remove so we can handle redeploy
626
// Nope, we ignore re-deploy on this level, because it can happen
627
// that the BC-provider is uninstalled, in just the second another
628
// thread tries to use the provider, and then that request will fail.
629
if (developmentProviderInstallation) {
630                 removeBCProvider();
631                 if (Security.addProvider(new BouncyCastleProvider()) < 0) {
632                     log.error("Cannot even install BC provider again!");
633                 } else {
634                     installImplicitlyCA = true;
635                 }
636             }
637         } else {
638             installImplicitlyCA = true;
639         }
640         if (installImplicitlyCA) {
641             // Install EC parameters for implicitlyCA encoding of EC keys, we have default curve parameters if no new ones have been given.
642
// The parameters are only used if implicitlyCA is used for generating keys, or verifying certs
643
checkImplicitParams();
644             ECCurve curve = new ECCurve.Fp(
645                     new BigInteger JavaDoc(IMPLICITLYCA_Q), // q
646
new BigInteger JavaDoc(IMPLICITLYCA_A, 16), // a
647
new BigInteger JavaDoc(IMPLICITLYCA_B, 16)); // b
648
org.bouncycastle.jce.spec.ECParameterSpec implicitSpec = new org.bouncycastle.jce.spec.ECParameterSpec(
649                     curve,
650                     curve.decodePoint(Hex.decode(IMPLICITLYCA_G)), // G
651
new BigInteger JavaDoc(IMPLICITLYCA_N)); // n
652
ConfigurableProvider config = (ConfigurableProvider)Security.getProvider("BC");
653             if (config != null) {
654                 config.setParameter(ConfigurableProvider.EC_IMPLICITLY_CA, implicitSpec);
655             } else {
656                 log.error("Can not get ConfigurableProvider, implicitlyCA EC parameters NOT set!");
657             }
658         }
659         
660         // 2007-05-25
661
// Finally we must configure SERIALNUMBER behaviour in BC >=1.36 to be the same
662
// as the behaviour in BC 1.35, it changed from SN to SERIALNUMBER in BC 1.36
663
// We must be backwards compatible
664
X509Name.DefaultSymbols.put(X509Name.SN, "SN");
665     }
666
667     /** Check if parameters have been set correctly during pre-process, otherwise log an error and
668      * set default values. Mostly used to be able to do JUnit testing
669      */

670     private static void checkImplicitParams() {
671         if (StringUtils.contains(IMPLICITLYCA_Q, "ecdsa.implicitlyca.q")) {
672             log.error("IMPLICITLYCA_Q not set!");
673             IMPLICITLYCA_Q = "883423532389192164791648750360308885314476597252960362792450860609699839";
674         }
675         if (StringUtils.contains(IMPLICITLYCA_A, "ecdsa.implicitlyca.a")) {
676             log.error("IMPLICITLYCA_A not set!");
677             IMPLICITLYCA_A = "7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc";
678         }
679         if (StringUtils.contains(IMPLICITLYCA_B, "ecdsa.implicitlyca.b")) {
680             log.error("IMPLICITLYCA_B not set!");
681             IMPLICITLYCA_B = "6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a";
682         }
683         if (StringUtils.contains(IMPLICITLYCA_G, "ecdsa.implicitlyca.g")) {
684             log.error("IMPLICITLYCA_G not set!");
685             IMPLICITLYCA_G = "020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf";
686         }
687         if (StringUtils.contains(IMPLICITLYCA_N, "ecdsa.implicitlyca.n")) {
688             log.error("IMPLICITLYCA_N not set!");
689             IMPLICITLYCA_N = "883423532389192164791648750360308884807550341691627752275345424702807307";
690         }
691     }
692     
693     /**
694      * Reads a certificate in PEM-format from a file. The file may contain other things,
695      * the first certificate in the file is read.
696      *
697      * @param certFile the file containing the certificate in PEM-format
698      * @return Ordered Collection of X509Certificate, first certificate first, or empty Collection
699      * @exception IOException if the filen cannot be read.
700      * @exception CertificateException if the filen does not contain a correct certificate.
701      */

702     public static Collection JavaDoc getCertsFromPEM(String JavaDoc certFile) throws IOException JavaDoc, CertificateException JavaDoc {
703         log.debug(">getCertfromPEM: certFile=" + certFile);
704         InputStream JavaDoc inStrm = null;
705         Collection JavaDoc certs;
706         try {
707             inStrm = new FileInputStream JavaDoc(certFile);
708             certs = getCertsFromPEM(inStrm);
709         } finally {
710             if (inStrm != null) inStrm.close();
711         }
712         log.debug("<getCertfromPEM: certFile=" + certFile);
713         return certs;
714     }
715
716     /**
717      * Reads a certificate in PEM-format from an InputStream. The stream may contain other things,
718      * the first certificate in the stream is read.
719      *
720      * @param certFile the input stream containing the certificate in PEM-format
721      * @return Ordered Collection of X509Certificate, first certificate first, or empty Collection
722      * @exception IOException if the stream cannot be read.
723      * @exception CertificateException if the stream does not contain a correct certificate.
724      */

725     public static Collection JavaDoc getCertsFromPEM(InputStream JavaDoc certstream)
726     throws IOException JavaDoc, CertificateException JavaDoc {
727         log.debug(">getCertfromPEM:");
728         ArrayList JavaDoc ret = new ArrayList JavaDoc();
729         String JavaDoc beginKey = "-----BEGIN CERTIFICATE-----";
730         String JavaDoc endKey = "-----END CERTIFICATE-----";
731         BufferedReader JavaDoc bufRdr = null;
732         ByteArrayOutputStream JavaDoc ostr = null;
733         PrintStream JavaDoc opstr = null;
734         try {
735             bufRdr = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(certstream));
736             while (bufRdr.ready()) {
737                 ostr = new ByteArrayOutputStream JavaDoc();
738                 opstr = new PrintStream JavaDoc(ostr);
739                 String JavaDoc temp;
740                 while ((temp = bufRdr.readLine()) != null
741                         && !temp.equals(beginKey))
742                     continue;
743                 if (temp == null)
744                     throw new IOException JavaDoc("Error in " + certstream.toString()
745                             + ", missing " + beginKey + " boundary");
746                 while ((temp = bufRdr.readLine()) != null
747                         && !temp.equals(endKey))
748                     opstr.print(temp);
749                 if (temp == null)
750                     throw new IOException JavaDoc("Error in " + certstream.toString()
751                             + ", missing " + endKey + " boundary");
752                 opstr.close();
753
754                 byte[] certbuf = Base64.decode(ostr.toByteArray());
755                 ostr.close();
756                 // Phweeew, were done, now decode the cert from file back to X509Certificate object
757
CertificateFactory JavaDoc cf = CertTools.getCertificateFactory();
758                 X509Certificate JavaDoc x509cert = (X509Certificate JavaDoc)cf.generateCertificate(new ByteArrayInputStream JavaDoc(certbuf));
759                 ret.add(x509cert);
760             }
761         } finally {
762             if (bufRdr != null) bufRdr.close();
763             if (opstr != null) opstr.close();
764             if (ostr != null) ostr.close();
765         }
766         log.debug("<getcertfromPEM:" + ret.size());
767         return ret;
768     } // getCertsFromPEM
769

770    /** Converts a regular array of certificates into an ArrayList, using the provided provided.
771     *
772     * @param certs Certificate[] of certificates to convert
773     * @param provider provider for example "SUN" or "BC", use null for the default provider (BC)
774     * @return An ArrayList of certificates in the same order as the passed in array
775     * @throws NoSuchProviderException
776     * @throws CertificateException
777     */

778     public static ArrayList JavaDoc getCertCollectionFromArray(Certificate JavaDoc[] certs, String JavaDoc provider) throws CertificateException JavaDoc, NoSuchProviderException JavaDoc {
779         log.debug(">getCertCollectionFromArray: "+provider);
780         ArrayList JavaDoc ret = new ArrayList JavaDoc();
781         String JavaDoc prov = provider;
782         if (prov == null) {
783             prov = "BC";
784         }
785         for (int i=0; i < certs.length; i++) {
786             CertificateFactory JavaDoc cf = CertificateFactory.getInstance("X.509", prov);
787             Certificate JavaDoc cert = certs[i];
788             X509Certificate JavaDoc x509cert = (X509Certificate JavaDoc)cf.generateCertificate(new ByteArrayInputStream JavaDoc(cert.getEncoded()));
789             ret.add(x509cert);
790         }
791         log.debug("<getCertCollectionFromArray: "+ret.size());
792         return ret;
793     }
794     
795     /**
796      * Returns a certificate in PEM-format.
797      *
798      * @param cert the certificate to convert to PEM
799      * @return byte array containing PEM certificate
800      * @exception IOException if the stream cannot be read.
801      * @exception CertificateException if the stream does not contain a correct certificate.
802      */

803     public static byte[] getPEMFromCerts(Collection JavaDoc certs)
804     throws CertificateException JavaDoc {
805         String JavaDoc beginKey = "-----BEGIN CERTIFICATE-----";
806         String JavaDoc endKey = "-----END CERTIFICATE-----";
807         ByteArrayOutputStream JavaDoc ostr = new ByteArrayOutputStream JavaDoc();
808         PrintStream JavaDoc opstr = new PrintStream JavaDoc(ostr);
809         Iterator JavaDoc iter = certs.iterator();
810         while (iter.hasNext()) {
811             X509Certificate JavaDoc cert = (X509Certificate JavaDoc)iter.next();
812             byte[] certbuf = Base64.encode(cert.getEncoded());
813             opstr.println("Subject: "+cert.getSubjectDN());
814             opstr.println("Issuer: "+cert.getIssuerDN());
815             opstr.println(beginKey);
816             opstr.println(new String JavaDoc(certbuf));
817             opstr.println(endKey);
818         }
819         opstr.close();
820         byte[] ret = ostr.toByteArray();
821         return ret;
822     }
823
824     /**
825      * Creates X509Certificate from byte[].
826      *
827      * @param cert byte array containing certificate in DER-format
828      *
829      * @return X509Certificate
830      *
831      * @throws CertificateException if the byte array does not contain a proper certificate.
832      * @throws IOException if the byte array cannot be read.
833      */

834     public static X509Certificate JavaDoc getCertfromByteArray(byte[] cert)
835         throws CertificateException JavaDoc {
836         log.debug(">getCertfromByteArray:");
837         CertificateFactory JavaDoc cf = CertTools.getCertificateFactory();
838         X509Certificate JavaDoc x509cert = (X509Certificate JavaDoc) cf.generateCertificate(new ByteArrayInputStream JavaDoc(cert));
839         log.debug("<getCertfromByteArray:");
840         return x509cert;
841     } // getCertfromByteArray
842

843     /**
844      * Creates X509CRL from byte[].
845      *
846      * @param crl byte array containing CRL in DER-format
847      *
848      * @return X509CRL
849      *
850      * @throws IOException if the byte array can not be read.
851      * @throws CertificateException if the byte arrayen does not contani a correct CRL.
852      * @throws CRLException if the byte arrayen does not contani a correct CRL.
853      */

854     public static X509CRL JavaDoc getCRLfromByteArray(byte[] crl)
855         throws IOException JavaDoc, CRLException JavaDoc {
856         log.debug(">getCRLfromByteArray:");
857
858         if (crl == null) {
859             throw new IOException JavaDoc("Cannot read byte[] that is 'null'!");
860         }
861
862         CertificateFactory JavaDoc cf = CertTools.getCertificateFactory();
863         X509CRL JavaDoc x509crl = (X509CRL JavaDoc) cf.generateCRL(new ByteArrayInputStream JavaDoc(crl));
864         log.debug("<getCRLfromByteArray:");
865
866         return x509crl;
867     } // getCRLfromByteArray
868

869     /**
870      * Checks if a certificate is self signed by verifying if subject and issuer are the same.
871      *
872      * @param cert the certificate that skall be checked.
873      *
874      * @return boolean true if the certificate has the same issuer and subject, false otherwise.
875      */

876     public static boolean isSelfSigned(X509Certificate JavaDoc cert) {
877         log.debug(">isSelfSigned: cert: " + CertTools.getIssuerDN(cert) + "\n" +
878             CertTools.getSubjectDN(cert));
879
880         boolean ret = CertTools.getSubjectDN(cert).equals(CertTools.getIssuerDN(cert));
881         log.debug("<isSelfSigned:" + ret);
882
883         return ret;
884     } // isSelfSigned
885

886     /**
887      * Generate a selfsigned certiicate.
888      *
889      * @param dn subject and issuer DN
890      * @param validity in days
891      * @param policyId policy string ('2.5.29.32.0') or null
892      * @param privKey private key
893      * @param pubKey public key
894      * @param sigAlg signature algorithm, you can use one of the contants CATokenInfo.SIGALG_XXX
895      * @param isCA boolean true or false
896      *
897      * @return X509Certificate, self signed
898      *
899      * @throws NoSuchAlgorithmException DOCUMENT ME!
900      * @throws SignatureException DOCUMENT ME!
901      * @throws InvalidKeyException DOCUMENT ME!
902      * @throws IllegalStateException
903      * @throws CertificateEncodingException
904      */

905     public static X509Certificate JavaDoc genSelfCert(String JavaDoc dn, long validity, String JavaDoc policyId,
906         PrivateKey JavaDoc privKey, PublicKey JavaDoc pubKey, String JavaDoc sigAlg, boolean isCA)
907         throws NoSuchAlgorithmException JavaDoc, SignatureException JavaDoc, InvalidKeyException JavaDoc, CertificateEncodingException JavaDoc, IllegalStateException JavaDoc {
908         
909         int keyusage = X509KeyUsage.keyCertSign + X509KeyUsage.cRLSign;
910         return genSelfCertForPurpose(dn, validity, policyId, privKey, pubKey, sigAlg, isCA, keyusage);
911         
912     } //genselfCert
913

914     /**
915      * Generate a selfsigned certiicate with possibility to specify key usage.
916      *
917      * @param dn subject and issuer DN
918      * @param validity in days
919      * @param policyId policy string ('2.5.29.32.0') or null
920      * @param privKey private key
921      * @param pubKey public key
922      * @param sigAlg signature algorithm, you can use one of the contants CATokenInfo.SIGALG_XXX
923      * @param isCA boolean true or false
924      * @param keyusage as defined by constants in X509KeyUsage
925      *
926      * @return X509Certificate, self signed
927      *
928      * @throws NoSuchAlgorithmException DOCUMENT ME!
929      * @throws SignatureException DOCUMENT ME!
930      * @throws InvalidKeyException DOCUMENT ME!
931      * @throws IllegalStateException
932      * @throws CertificateEncodingException
933      */

934     public static X509Certificate JavaDoc genSelfCertForPurpose(String JavaDoc dn, long validity, String JavaDoc policyId,
935         PrivateKey JavaDoc privKey, PublicKey JavaDoc pubKey, String JavaDoc sigAlg, boolean isCA, int keyusage)
936         throws NoSuchAlgorithmException JavaDoc, SignatureException JavaDoc, InvalidKeyException JavaDoc, CertificateEncodingException JavaDoc, IllegalStateException JavaDoc {
937         // Create self signed certificate
938
Date JavaDoc firstDate = new Date JavaDoc();
939
940         // Set back startdate ten minutes to avoid some problems with wrongly set clocks.
941
firstDate.setTime(firstDate.getTime() - (10 * 60 * 1000));
942
943         Date JavaDoc lastDate = new Date JavaDoc();
944
945         // validity in days = validity*24*60*60*1000 milliseconds
946
lastDate.setTime(lastDate.getTime() + (validity * (24 * 60 * 60 * 1000)));
947
948         X509V3CertificateGenerator certgen = new X509V3CertificateGenerator();
949         
950         // Serialnumber is random bits, where random generator is initialized with Date.getTime() when this
951
// bean is created.
952
byte[] serno = new byte[8];
953         SecureRandom JavaDoc random = SecureRandom.getInstance("SHA1PRNG");
954         random.setSeed((new Date JavaDoc().getTime()));
955         random.nextBytes(serno);
956         certgen.setSerialNumber((new java.math.BigInteger JavaDoc(serno)).abs());
957         certgen.setNotBefore(firstDate);
958         certgen.setNotAfter(lastDate);
959         certgen.setSignatureAlgorithm(sigAlg);
960         certgen.setSubjectDN(CertTools.stringToBcX509Name(dn));
961         certgen.setIssuerDN(CertTools.stringToBcX509Name(dn));
962         certgen.setPublicKey(pubKey);
963
964         // Basic constranits is always critical and MUST be present at-least in CA-certificates.
965
BasicConstraints bc = new BasicConstraints(isCA);
966         certgen.addExtension(X509Extensions.BasicConstraints.getId(), true, bc);
967
968         // Put critical KeyUsage in CA-certificates
969
if (isCA == true) {
970             X509KeyUsage ku = new X509KeyUsage(keyusage);
971             certgen.addExtension(X509Extensions.KeyUsage.getId(), true, ku);
972         }
973
974         // Subject and Authority key identifier is always non-critical and MUST be present for certificates to verify in Mozilla.
975
try {
976             if (isCA == true) {
977                 SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(
978                             new ByteArrayInputStream JavaDoc(pubKey.getEncoded())).readObject());
979                 SubjectKeyIdentifier ski = new SubjectKeyIdentifier(spki);
980
981                 SubjectPublicKeyInfo apki = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(
982                             new ByteArrayInputStream JavaDoc(pubKey.getEncoded())).readObject());
983                 AuthorityKeyIdentifier aki = new AuthorityKeyIdentifier(apki);
984
985                 certgen.addExtension(X509Extensions.SubjectKeyIdentifier.getId(), false, ski);
986                 certgen.addExtension(X509Extensions.AuthorityKeyIdentifier.getId(), false, aki);
987             }
988         } catch (IOException JavaDoc e) { // do nothing
989
}
990
991         // CertificatePolicies extension if supplied policy ID, always non-critical
992
if (policyId != null) {
993                 PolicyInformation pi = new PolicyInformation(new DERObjectIdentifier(policyId));
994                 DERSequence seq = new DERSequence(pi);
995                 certgen.addExtension(X509Extensions.CertificatePolicies.getId(), false, seq);
996         }
997
998         X509Certificate JavaDoc selfcert = certgen.generate(privKey);
999
1000        return selfcert;
1001    } //genselfCertForPurpose
1002

1003    /**
1004     * Get the authority key identifier from a certificate extensions
1005     *
1006     * @param cert certificate containing the extension
1007     * @return byte[] containing the authority key identifier, or null if it does not exist
1008     * @throws IOException if extension can not be parsed
1009     */

1010    public static byte[] getAuthorityKeyId(X509Certificate JavaDoc cert)
1011        throws IOException JavaDoc {
1012        if (cert == null) {
1013            return null;
1014        }
1015        byte[] extvalue = cert.getExtensionValue("2.5.29.35");
1016        if (extvalue == null) {
1017            return null;
1018        }
1019        DEROctetString oct = (DEROctetString) (new ASN1InputStream(new ByteArrayInputStream JavaDoc(extvalue)).readObject());
1020        AuthorityKeyIdentifier keyId = new AuthorityKeyIdentifier((ASN1Sequence) new ASN1InputStream(
1021                    new ByteArrayInputStream JavaDoc(oct.getOctets())).readObject());
1022        return keyId.getKeyIdentifier();
1023    } // getAuthorityKeyId
1024

1025    /**
1026     * Get the subject key identifier from a certificate extensions
1027     *
1028     * @param cert certificate containing the extension
1029     * @return byte[] containing the subject key identifier, or null if it does not exist
1030     * @throws IOException if extension can not be parsed
1031     */

1032    public static byte[] getSubjectKeyId(X509Certificate JavaDoc cert)
1033        throws IOException JavaDoc {
1034        if (cert == null) {
1035            return null;
1036        }
1037        byte[] extvalue = cert.getExtensionValue("2.5.29.14");
1038        if (extvalue == null) {
1039            return null;
1040        }
1041        ASN1OctetString str = ASN1OctetString.getInstance(new ASN1InputStream(new ByteArrayInputStream JavaDoc(extvalue)).readObject());
1042        SubjectKeyIdentifier keyId = SubjectKeyIdentifier.getInstance(new ASN1InputStream(new ByteArrayInputStream JavaDoc(str.getOctets())).readObject());
1043        return keyId.getKeyIdentifier();
1044    } // getSubjectKeyId
1045

1046    /**
1047     * Get a certificate policy ID from a certificate policies extension
1048     *
1049     * @param cert certificate containing the extension
1050     * @param pos position of the policy id, if several exist, the first is as pos 0
1051     * @return String with the certificate policy OID
1052     * @throws IOException if extension can not be parsed
1053     */

1054    public static String JavaDoc getCertificatePolicyId(X509Certificate JavaDoc cert, int pos)
1055        throws IOException JavaDoc {
1056        byte[] extvalue = cert.getExtensionValue(X509Extensions.CertificatePolicies.getId());
1057        if (extvalue == null) {
1058            return null;
1059        }
1060        DEROctetString oct = (DEROctetString) (new ASN1InputStream(new ByteArrayInputStream JavaDoc(extvalue)).readObject());
1061        ASN1Sequence seq = (ASN1Sequence)new ASN1InputStream(new ByteArrayInputStream JavaDoc(oct.getOctets())).readObject();
1062        // Check the size so we don't ArrayIndexOutOfBounds
1063
if (seq.size() < pos+1) {
1064            return null;
1065        }
1066        PolicyInformation pol = new PolicyInformation((ASN1Sequence)seq.getObjectAt(pos));
1067        String JavaDoc id = pol.getPolicyIdentifier().getId();
1068        return id;
1069    } // getCertificatePolicyId
1070

1071    /**
1072     * Gets the Microsoft specific UPN altName.
1073     *
1074     * @param cert certificate containing the extension
1075     * @return String with the UPN name or null if the altName does not exist
1076     */

1077    public static String JavaDoc getUPNAltName(X509Certificate JavaDoc cert) throws IOException JavaDoc, CertificateParsingException JavaDoc {
1078        Collection JavaDoc altNames = cert.getSubjectAlternativeNames();
1079        if (altNames != null) {
1080            Iterator JavaDoc i = altNames.iterator();
1081            while (i.hasNext()) {
1082                ASN1Sequence seq = getAltnameSequence((List JavaDoc)i.next());
1083                String JavaDoc ret = getUPNStringFromSequence(seq);
1084                if (ret != null) {
1085                    return ret;
1086                }
1087            }
1088        }
1089        return null;
1090    } // getUPNAltName
1091

1092    /** Helper method for the above method
1093     */

1094    private static String JavaDoc getUPNStringFromSequence(ASN1Sequence seq) {
1095        if ( seq != null) {
1096            // First in sequence is the object identifier, that we must check
1097
DERObjectIdentifier id = DERObjectIdentifier.getInstance(seq.getObjectAt(0));
1098            if (id.getId().equals(CertTools.UPN_OBJECTID)) {
1099                ASN1TaggedObject obj = (ASN1TaggedObject) seq.getObjectAt(1);
1100                DERUTF8String str = DERUTF8String.getInstance(obj.getObject());
1101                return str.getString();
1102            }
1103        }
1104        return null;
1105    }
1106    
1107    /**
1108     * Gets the Microsoft specific GUID altName, that is encoded as an octect string.
1109     *
1110     * @param cert certificate containing the extension
1111     * @return String with the hex-encoded GUID byte array or null if the altName does not exist
1112     */

1113    public static String JavaDoc getGuidAltName(X509Certificate JavaDoc cert)
1114        throws IOException JavaDoc, CertificateParsingException JavaDoc {
1115        Collection JavaDoc altNames = cert.getSubjectAlternativeNames();
1116        if (altNames != null) {
1117            Iterator JavaDoc i = altNames.iterator();
1118            while (i.hasNext()) {
1119                ASN1Sequence seq = getAltnameSequence((List JavaDoc)i.next());
1120                if ( seq != null) {
1121                    // First in sequence is the object identifier, that we must check
1122
DERObjectIdentifier id = DERObjectIdentifier.getInstance(seq.getObjectAt(0));
1123                    if (id.getId().equals(CertTools.GUID_OBJECTID)) {
1124                        ASN1TaggedObject obj = (ASN1TaggedObject) seq.getObjectAt(1);
1125                        ASN1OctetString str = ASN1OctetString.getInstance(obj.getObject());
1126                        return new String JavaDoc(Hex.encode(str.getOctets()));
1127                    }
1128                }
1129            }
1130        }
1131        return null;
1132    } // getGuidAltName
1133

1134    /** Helper for the above methods
1135     */

1136    private static ASN1Sequence getAltnameSequence(List JavaDoc listitem) throws IOException JavaDoc {
1137        Integer JavaDoc no = (Integer JavaDoc) listitem.get(0);
1138        if (no.intValue() == 0) {
1139            byte[] altName = (byte[]) listitem.get(1);
1140            return getAltnameSequence(altName);
1141        }
1142        return null;
1143    }
1144    private static ASN1Sequence getAltnameSequence(byte[] value) throws IOException JavaDoc {
1145        DERObject oct = (new ASN1InputStream(new ByteArrayInputStream JavaDoc(value)).readObject());
1146        ASN1Sequence seq = ASN1Sequence.getInstance(oct);
1147        return seq;
1148    }
1149    
1150    /**
1151     * SubjectAltName ::= GeneralNames
1152     *
1153     * GeneralNames :: = SEQUENCE SIZE (1..MAX) OF GeneralName
1154     *
1155     * GeneralName ::= CHOICE {
1156     * otherName [0] OtherName,
1157     * rfc822Name [1] IA5String,
1158     * dNSName [2] IA5String,
1159     * x400Address [3] ORAddress,
1160     * directoryName [4] Name,
1161     * ediPartyName [5] EDIPartyName,
1162     * uniformResourceIdentifier [6] IA5String,
1163     * iPAddress [7] OCTET STRING,
1164     * registeredID [8] OBJECT IDENTIFIER}
1165     *
1166     * SubjectAltName is of form \"rfc822Name=<email>,
1167     * dNSName=<host name>, uniformResourceIdentifier=<http://host.com/>,
1168     * iPAddress=<address>, guid=<globally unique id>, directoryName=<CN=testDirName|dir|name>
1169     *
1170     * Supported altNames are upn, rfc822Name, uniformResourceIdentifier, dNSName, iPAddress, directoryName
1171     *
1172     * @author Marco Ferrante, (c) 2005 CSITA - University of Genoa (Italy)
1173     * @author Tomas Gustavsson
1174     * @param certificate containing alt names
1175     * @return String containing altNames of form "rfc822Name=email, dNSName=hostname, uniformResourceIdentifier=uri, iPAddress=ip, upn=upn, directoryName=CN=testDirName|dir|name" or null if no altNames exist. Values in returned String is from CertTools constants. AltNames not supported are simply not shown in the resulting string.
1176     * @throws java.lang.Exception
1177     */

1178    public static String JavaDoc getSubjectAlternativeName(X509Certificate JavaDoc certificate) throws Exception JavaDoc {
1179        log.debug("Search for SubjectAltName");
1180        if (certificate.getSubjectAlternativeNames() == null)
1181            return null;
1182        
1183        java.util.Collection JavaDoc altNames = certificate.getSubjectAlternativeNames();
1184        if (altNames == null) {
1185            return null;
1186        }
1187        Iterator JavaDoc iter = altNames.iterator();
1188        String JavaDoc result = "";
1189        String JavaDoc append = "";
1190        while (iter.hasNext()) {
1191            java.util.List JavaDoc item = (java.util.List JavaDoc)iter.next();
1192            Integer JavaDoc type = (Integer JavaDoc)item.get(0);
1193            Object JavaDoc value = item.get(1);
1194            if (!StringUtils.isEmpty(result)) {
1195                // Result already contains one altname, so we have to add comma if there are more altNames
1196
append = ", ";
1197            }
1198            switch (type.intValue()) {
1199                case 0: ASN1Sequence seq = getAltnameSequence(item);
1200                    String JavaDoc upn = getUPNStringFromSequence(seq);
1201                    // OtherName can be something else besides UPN
1202
if (upn != null) {
1203                        result += append + CertTools.UPN+"="+upn;
1204                    }
1205                    break;
1206                case 1: result += append + CertTools.EMAIL+"=" + (String JavaDoc)value;
1207                    break;
1208                case 2: result += append + CertTools.DNS+"=" + (String JavaDoc)value;
1209                    break;
1210                case 3: // SubjectAltName of type x400Address not supported
1211
break;
1212                case 4: result += append + CertTools.DIRECTORYNAME+"=" + (String JavaDoc)value;
1213                    break;
1214                case 5: // SubjectAltName of type ediPartyName not supported
1215
break;
1216                case 6: result += append + CertTools.URI+"=" + (String JavaDoc)value;
1217                    break;
1218                case 7: result += append + CertTools.IPADDR+"=" + (String JavaDoc)value;
1219                    break;
1220                default: // SubjectAltName of unknown type
1221
break;
1222            }
1223        }
1224        if (StringUtils.isEmpty(result)) {
1225            return null;
1226        }
1227        return result;
1228    }
1229
1230    /**
1231     * From an altName string as defined in getSubjectAlternativeName
1232     * @param altName
1233     * @return ASN.1 GeneralNames
1234     * @see #getSubjectAlternativeName
1235     */

1236    public static GeneralNames getGeneralNamesFromAltName(String JavaDoc altName) {
1237        log.debug(">getGeneralNamesFromAltName: "+altName);
1238        
1239        ASN1EncodableVector vec = new ASN1EncodableVector();
1240
1241        ArrayList JavaDoc emails = CertTools.getEmailFromDN(altName);
1242        if (!emails.isEmpty()) {
1243            Iterator JavaDoc iter = emails.iterator();
1244            while (iter.hasNext()) {
1245                GeneralName gn = new GeneralName(1, new DERIA5String((String JavaDoc)iter.next()));
1246                vec.add(gn);
1247            }
1248        }
1249        
1250        ArrayList JavaDoc dns = CertTools.getPartsFromDN(altName, CertTools.DNS);
1251        if (!dns.isEmpty()) {
1252            Iterator JavaDoc iter = dns.iterator();
1253            while (iter.hasNext()) {
1254                GeneralName gn = new GeneralName(2, new DERIA5String((String JavaDoc)iter.next()));
1255                vec.add(gn);
1256            }
1257        }
1258        
1259        String JavaDoc directoryName = getDirectoryStringFromAltName(altName);
1260        if (directoryName != null) {
1261          X509Name x509DirectoryName = new X509Name(directoryName);
1262          GeneralName gn = new GeneralName(4, x509DirectoryName);
1263          vec.add(gn);
1264        }
1265                                
1266        ArrayList JavaDoc uri = CertTools.getPartsFromDN(altName, CertTools.URI);
1267        if (!uri.isEmpty()) {
1268            Iterator JavaDoc iter = uri.iterator();
1269            while (iter.hasNext()) {
1270                GeneralName gn = new GeneralName(6, new DERIA5String((String JavaDoc)iter.next()));
1271                vec.add(gn);
1272            }
1273        }
1274        uri = CertTools.getPartsFromDN(altName, CertTools.URI1);
1275        if (!uri.isEmpty()) {
1276            Iterator JavaDoc iter = uri.iterator();
1277            while (iter.hasNext()) {
1278                GeneralName gn = new GeneralName(6, new DERIA5String((String JavaDoc)iter.next()));
1279                vec.add(gn);
1280            }
1281        }
1282        uri = CertTools.getPartsFromDN(altName, CertTools.URI2);
1283        if (!uri.isEmpty()) {
1284            Iterator JavaDoc iter = uri.iterator();
1285            while (iter.hasNext()) {
1286                GeneralName gn = new GeneralName(6, new DERIA5String((String JavaDoc)iter.next()));
1287                vec.add(gn);
1288            }
1289        }
1290        
1291                
1292        ArrayList JavaDoc ipstr = CertTools.getPartsFromDN(altName, CertTools.IPADDR);
1293        if (!ipstr.isEmpty()) {
1294            Iterator JavaDoc iter = ipstr.iterator();
1295            while (iter.hasNext()) {
1296                byte[] ipoctets = StringTools.ipStringToOctets((String JavaDoc)iter.next());
1297                GeneralName gn = new GeneralName(7, new DEROctetString(ipoctets));
1298                vec.add(gn);
1299            }
1300        }
1301                    
1302        // UPN is an OtherName
1303
ArrayList JavaDoc upn = CertTools.getPartsFromDN(altName, CertTools.UPN);
1304        if (!upn.isEmpty()) {
1305            Iterator JavaDoc iter = upn.iterator();
1306            while (iter.hasNext()) {
1307                ASN1EncodableVector v = new ASN1EncodableVector();
1308                v.add(new DERObjectIdentifier(CertTools.UPN_OBJECTID));
1309                v.add(new DERTaggedObject(true, 0, new DERUTF8String((String JavaDoc)iter.next())));
1310                //GeneralName gn = new GeneralName(new DERSequence(v), 0);
1311
DERObject gn = new DERTaggedObject(false, 0, new DERSequence(v));
1312                vec.add(gn);
1313            }
1314        }
1315        
1316        
1317        ArrayList JavaDoc guid = CertTools.getPartsFromDN(altName, CertTools.GUID);
1318        if (!guid.isEmpty()) {
1319            Iterator JavaDoc iter = guid.iterator();
1320            while (iter.hasNext()) {
1321                ASN1EncodableVector v = new ASN1EncodableVector();
1322                byte[] guidbytes = Hex.decode((String JavaDoc)iter.next());
1323                if (guidbytes != null) {
1324                    v.add(new DERObjectIdentifier(CertTools.GUID_OBJECTID));
1325                    v.add(new DERTaggedObject(true, 0, new DEROctetString(guidbytes)));
1326                    DERObject gn = new DERTaggedObject(false, 0, new DERSequence(v));
1327                    vec.add(gn);
1328                } else {
1329                    log.error("Cannot decode hexadecimal guid: "+guid);
1330                }
1331            }
1332        }
1333        
1334        // To support custom OIDs in altNames, they must be added as an OtherName
1335
ArrayList JavaDoc customoids = CertTools.getCustomOids(altName);
1336        if (!customoids.isEmpty()) {
1337            Iterator JavaDoc iter = customoids.iterator();
1338            while (iter.hasNext()) {
1339                String JavaDoc oid = (String JavaDoc)iter.next();
1340                ArrayList JavaDoc oidval = CertTools.getPartsFromDN(altName, oid);
1341                if (!oidval.isEmpty()) {
1342                    Iterator JavaDoc valiter = oidval.iterator();
1343                    while (valiter.hasNext()) {
1344                        ASN1EncodableVector v = new ASN1EncodableVector();
1345                        v.add(new DERObjectIdentifier(oid));
1346                        v.add(new DERTaggedObject(true, 0, new DERUTF8String((String JavaDoc)valiter.next())));
1347                        DERObject gn = new DERTaggedObject(false, 0, new DERSequence(v));
1348                        vec.add(gn);
1349                    }
1350                }
1351            }
1352        }
1353
1354        GeneralNames ret = null;
1355        if (vec.size() > 0) {
1356            ret = new GeneralNames(new DERSequence(vec));
1357        }
1358        return ret;
1359    }
1360    
1361    /**
1362     * GeneralName ::= CHOICE {
1363     * otherName [0] OtherName,
1364     * rfc822Name [1] IA5String,
1365     * dNSName [2] IA5String,
1366     * x400Address [3] ORAddress,
1367     * directoryName [4] Name,
1368     * ediPartyName [5] EDIPartyName,
1369     * uniformResourceIdentifier [6] IA5String,
1370     * iPAddress [7] OCTET STRING,
1371     * registeredID [8] OBJECT IDENTIFIER}
1372     *
1373     * @param tag the no tag 0-8
1374     * @param value the DEREncodable value as returned by GeneralName.getName()
1375     * @return String in form rfc822Name=<email> or uri=<uri> etc
1376     * @throws IOException
1377     * @see #getSubjectAlternativeName
1378     */

1379    public static String JavaDoc getGeneralNameString(int tag, DEREncodable value) throws IOException JavaDoc {
1380        String JavaDoc ret = null;
1381        switch (tag) {
1382        case 0: ASN1Sequence seq = getAltnameSequence(value.getDERObject().getEncoded());
1383            String JavaDoc upn = getUPNStringFromSequence(seq);
1384            // OtherName can be something else besides UPN
1385
if (upn != null) {
1386                ret = CertTools.UPN+"="+upn;
1387            }
1388            break;
1389        case 1: ret = CertTools.EMAIL+"=" + DERIA5String.getInstance(value).getString();
1390            break;
1391        case 2: ret = CertTools.DNS+"=" + DERIA5String.getInstance(value).getString();
1392            break;
1393        case 3: // SubjectAltName of type x400Address not supported
1394
break;
1395        case 4: // SubjectAltName of type directoryName not supported
1396
break;
1397        case 5: // SubjectAltName of type ediPartyName not supported
1398
break;
1399        case 6: ret = CertTools.URI+"=" + DERIA5String.getInstance(value).getString();
1400            break;
1401        case 7: // SubjectAltName of type iPAddr not supported
1402
break;
1403        default: // SubjectAltName of unknown type
1404
break;
1405        }
1406        return ret;
1407    }
1408    
1409    /**
1410     * Check the certificate with CA certificate.
1411     *
1412     * @param certificate cert to verify
1413     * @param caCertPath collection of X509Certificate
1414     * @return true if verified OK, false if not
1415     */

1416    public static boolean verify(X509Certificate JavaDoc certificate, Collection JavaDoc caCertPath) throws Exception JavaDoc {
1417        try {
1418            ArrayList JavaDoc certlist = new ArrayList JavaDoc();
1419            // Create CertPath
1420
certlist.add(certificate);
1421            // Add other certs...
1422
CertificateFactory JavaDoc cf = CertificateFactory.getInstance("X.509", "BC");
1423            java.security.cert.CertPath JavaDoc cp = cf.generateCertPath(certlist);
1424            // Create TrustAnchor. Since EJBCA use BouncyCastle provider, we assume
1425
// certificate already in correct order
1426
X509Certificate JavaDoc[] cac = (X509Certificate JavaDoc[]) caCertPath.toArray(new X509Certificate JavaDoc[] {});
1427            java.security.cert.TrustAnchor JavaDoc anchor = new java.security.cert.
1428
JavaDoc            TrustAnchor(cac[0], null);
1429            // Set the PKIX parameters
1430
java.security.cert.PKIXParameters JavaDoc params = new java.security.cert.PKIXParameters JavaDoc(java.util.Collections.singleton(anchor));
1431            params.setRevocationEnabled(false);
1432            java.security.cert.CertPathValidator JavaDoc cpv = java.security.cert.
1433            CertPathValidator.getInstance("PKIX", "BC");
1434            java.security.cert.PKIXCertPathValidatorResult JavaDoc result =
1435                (java.security.cert.PKIXCertPathValidatorResult JavaDoc) cpv.validate(cp, params);
1436            log.debug("Certificate verify result: " + result.toString());
1437        } catch (java.security.cert.CertPathValidatorException JavaDoc cpve) {
1438            throw new Exception JavaDoc("Invalid certificate or certificate not issued by specified CA: " + cpve.getMessage());
1439        } catch (Exception JavaDoc e) {
1440            throw new Exception JavaDoc("Error checking certificate chain: " + e.getMessage());
1441        }
1442        return true;
1443    }
1444    
1445    /**
1446     * Return the CRL distribution point URL form a certificate.
1447     */

1448    public static URL JavaDoc getCrlDistributionPoint(X509Certificate JavaDoc certificate)
1449      throws CertificateParsingException JavaDoc {
1450        try {
1451            DERObject obj = getExtensionValue(certificate, X509Extensions
1452                                              .CRLDistributionPoints.getId());
1453            if (obj == null) {
1454                return null;
1455            }
1456            ASN1Sequence distributionPoints = (ASN1Sequence) obj;
1457            for (int i = 0; i < distributionPoints.size(); i++) {
1458                ASN1Sequence distrPoint = (ASN1Sequence) distributionPoints.getObjectAt(i);
1459                for (int j = 0; j < distrPoint.size(); j++) {
1460                    ASN1TaggedObject tagged = (ASN1TaggedObject) distrPoint.getObjectAt(j);
1461                    if (tagged.getTagNo() == 0) {
1462                        String JavaDoc url
1463                          = getStringFromGeneralNames(tagged.getObject());
1464                        if (url != null) {
1465                            return new URL JavaDoc(url);
1466                        }
1467                    }
1468                }
1469            }
1470        }
1471        catch (Exception JavaDoc e) {
1472            log.error("Error parsing CrlDistributionPoint", e);
1473            throw new CertificateParsingException JavaDoc(e.toString());
1474        }
1475        return null;
1476    }
1477    
1478    /** Returns OCSP URL that is inside AuthorithInformationAccess extension, or null.
1479     *
1480     * @param cert
1481     * @return
1482     * @throws CertificateParsingException
1483     */

1484    public static String JavaDoc getAuthorityInformationAccessOcspUrl(X509Certificate JavaDoc cert)
1485        throws CertificateParsingException JavaDoc {
1486            try {
1487                DERObject obj = getExtensionValue(cert, X509Extensions.AuthorityInfoAccess.getId());
1488                if (obj == null) {
1489                    return null;
1490                }
1491                AuthorityInformationAccess aia = AuthorityInformationAccess.getInstance(obj);
1492                AccessDescription[] ad = aia.getAccessDescriptions();
1493                if ( (ad == null) || (ad.length < 1) ) {
1494                    return null;
1495                }
1496                if (!ad[0].getAccessMethod().equals(X509ObjectIdentifiers.ocspAccessMethod)) {
1497                    return null;
1498                }
1499                GeneralName gn = ad[0].getAccessLocation();
1500                if (gn.getTagNo() != 6) {
1501                    return null;
1502                }
1503                DERIA5String str = DERIA5String.getInstance(gn.getDERObject());
1504                return str.getString();
1505            }
1506            catch (Exception JavaDoc e) {
1507                log.error("Error parsing AuthorityInformationAccess", e);
1508                throw new CertificateParsingException JavaDoc(e.toString());
1509            }
1510    }
1511
1512    /**
1513     * Return an Extension DERObject from a certificate
1514     */

1515    protected static DERObject getExtensionValue(X509Certificate JavaDoc cert, String JavaDoc oid)
1516      throws IOException JavaDoc {
1517        if (cert == null) {
1518            return null;
1519        }
1520        byte[] bytes = cert.getExtensionValue(oid);
1521        if (bytes == null) {
1522            return null;
1523        }
1524        ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream JavaDoc(bytes));
1525        ASN1OctetString octs = (ASN1OctetString) aIn.readObject();
1526        aIn = new ASN1InputStream(new ByteArrayInputStream JavaDoc(octs.getOctets()));
1527        return aIn.readObject();
1528    } //getExtensionValue
1529

1530    private static String JavaDoc getStringFromGeneralNames(DERObject names) {
1531         ASN1Sequence namesSequence = ASN1Sequence.getInstance((ASN1TaggedObject)names, false);
1532         if (namesSequence.size() == 0) {
1533             return null;
1534         }
1535         DERTaggedObject taggedObject
1536           = (DERTaggedObject)namesSequence.getObjectAt(0);
1537         return new String JavaDoc(ASN1OctetString.getInstance(taggedObject, false).getOctets());
1538     } //getStringFromGeneralNames
1539

1540    /**
1541     * Generate SHA1 fingerprint in string representation.
1542     *
1543     * @param ba Byte array containing DER encoded X509Certificate.
1544     *
1545     * @return String containing hex format of SHA1 fingerprint.
1546     */

1547    public static String JavaDoc getCertFingerprintAsString(byte[] ba) {
1548        try {
1549            X509Certificate JavaDoc cert = getCertfromByteArray(ba);
1550            byte[] res = generateSHA1Fingerprint(cert.getEncoded());
1551
1552            return new String JavaDoc(Hex.encode(res));
1553        } catch (CertificateEncodingException JavaDoc cee) {
1554            log.error("Error encoding X509 certificate.", cee);
1555        } catch (CertificateException JavaDoc cee) {
1556            log.error("Error decoding X509 certificate.", cee);
1557        }
1558
1559        return null;
1560    }
1561
1562    /**
1563     * Generate SHA1 fingerprint of certificate in string representation.
1564     *
1565     * @param cert X509Certificate.
1566     *
1567     * @return String containing hex format of SHA1 fingerprint, or null if input is null.
1568     */

1569    public static String JavaDoc getFingerprintAsString(X509Certificate JavaDoc cert) {
1570        if (cert == null) return null;
1571        try {
1572            byte[] res = generateSHA1Fingerprint(cert.getEncoded());
1573
1574            return new String JavaDoc(Hex.encode(res));
1575        } catch (CertificateEncodingException JavaDoc cee) {
1576            log.error("Error encoding X509 certificate.", cee);
1577        }
1578
1579        return null;
1580    }
1581
1582    /**
1583     * Generate SHA1 fingerprint of CRL in string representation.
1584     *
1585     * @param crl X509CRL.
1586     *
1587     * @return String containing hex format of SHA1 fingerprint.
1588     */

1589    public static String JavaDoc getFingerprintAsString(X509CRL JavaDoc crl) {
1590        try {
1591            byte[] res = generateSHA1Fingerprint(crl.getEncoded());
1592
1593            return new String JavaDoc(Hex.encode(res));
1594        } catch (CRLException JavaDoc ce) {
1595            log.error("Error encoding X509 CRL.", ce);
1596        }
1597
1598        return null;
1599    }
1600
1601    /**
1602     * Generate a SHA1 fingerprint from a byte array containing a X.509 certificate
1603     *
1604     * @param ba Byte array containing DER encoded X509Certificate.
1605     *
1606     * @return Byte array containing SHA1 hash of DER encoded certificate.
1607     */

1608    public static byte[] generateSHA1Fingerprint(byte[] ba) {
1609        try {
1610            MessageDigest JavaDoc md = MessageDigest.getInstance("SHA1");
1611
1612            return md.digest(ba);
1613        } catch (NoSuchAlgorithmException JavaDoc nsae) {
1614            log.error("SHA1 algorithm not supported", nsae);
1615        }
1616
1617        return null;
1618    } // generateSHA1Fingerprint
1619

1620    /**
1621     * Generate a MD5 fingerprint from a byte array containing a X.509 certificate
1622     *
1623     * @param ba Byte array containing DER encoded X509Certificate.
1624     *
1625     * @return Byte array containing MD5 hash of DER encoded certificate.
1626     */

1627    public static byte[] generateMD5Fingerprint(byte[] ba) {
1628        try {
1629            MessageDigest JavaDoc md = MessageDigest.getInstance("MD5");
1630
1631            return md.digest(ba);
1632        } catch (NoSuchAlgorithmException JavaDoc nsae) {
1633            log.error("MD5 algorithm not supported", nsae);
1634        }
1635
1636        return null;
1637    } // generateMD5Fingerprint
1638

1639    /** Converts Sun Key usage bits to Bouncy castle key usage kits
1640     *
1641     * @param sku key usage bit fields according to java.security.cert.X509Certificate#getKeyUsage, must be a boolean aray of size 9.
1642     * @return key usage int according to org.bouncycastle.jce.X509KeyUsage#X509KeyUsage, or -1 if input is null.
1643     * @see java.security.cert.X509Certificate#getKeyUsage
1644     * @see org.bouncycastle.jce.X509KeyUsage#X509KeyUsage
1645     */

1646    public static int sunKeyUsageToBC(boolean[] sku) {
1647        if (sku == null) {
1648            return -1;
1649        }
1650        int bcku = 0;
1651        if (sku[0] == true)
1652            bcku = bcku | X509KeyUsage.digitalSignature;
1653        if (sku[1] == true)
1654            bcku = bcku | X509KeyUsage.nonRepudiation;
1655        if (sku[2] == true)
1656            bcku = bcku | X509KeyUsage.keyEncipherment;
1657        if (sku[3] == true)
1658            bcku = bcku | X509KeyUsage.dataEncipherment;
1659        if (sku[4] == true)
1660            bcku = bcku | X509KeyUsage.keyAgreement;
1661        if (sku[5] == true)
1662            bcku = bcku | X509KeyUsage.keyCertSign;
1663        if (sku[6] == true)
1664            bcku = bcku | X509KeyUsage.cRLSign;
1665        if (sku[7] == true)
1666            bcku = bcku | X509KeyUsage.encipherOnly;
1667        if (sku[8] == true)
1668            bcku = bcku | X509KeyUsage.decipherOnly;
1669        return bcku;
1670    }
1671    
1672    /** Converts DERBitString ResonFlags to a RevokedCertInfo constant
1673     *
1674     * @param reasonFlags DERBITString received from org.bouncycastle.asn1.x509.ReasonFlags.
1675     * @return int according to org.ejbca.core.model.ca.crl.RevokedCertInfo
1676     */

1677    public static int bitStringToRevokedCertInfo(DERBitString reasonFlags) {
1678        int ret = RevokedCertInfo.REVOKATION_REASON_UNSPECIFIED;
1679        if (reasonFlags == null) {
1680            return ret;
1681        }
1682        int val = reasonFlags.intValue();
1683        if ( (val & ReasonFlags.aACompromise) != 0) {
1684            ret = RevokedCertInfo.REVOKATION_REASON_AACOMPROMISE;
1685        }
1686        if ( (val & ReasonFlags.affiliationChanged) != 0) {
1687            ret = RevokedCertInfo.REVOKATION_REASON_AFFILIATIONCHANGED;
1688        }
1689        if ( (val & ReasonFlags.cACompromise) != 0) {
1690            ret = RevokedCertInfo.REVOKATION_REASON_CACOMPROMISE;
1691        }
1692        if ( (val & ReasonFlags.certificateHold) != 0) {
1693            ret = RevokedCertInfo.REVOKATION_REASON_CERTIFICATEHOLD;
1694        }
1695        if ( (val & ReasonFlags.cessationOfOperation) != 0) {
1696            ret = RevokedCertInfo.REVOKATION_REASON_CESSATIONOFOPERATION;
1697        }
1698        if ( (val & ReasonFlags.keyCompromise) != 0) {
1699            ret = RevokedCertInfo.REVOKATION_REASON_KEYCOMPROMISE;
1700        }
1701        if ( (val & ReasonFlags.privilegeWithdrawn) != 0) {
1702            ret = RevokedCertInfo.REVOKATION_REASON_PRIVILEGESWITHDRAWN;
1703        }
1704        if ( (val & ReasonFlags.superseded) != 0) {
1705            ret = RevokedCertInfo.REVOKATION_REASON_SUPERSEDED;
1706        }
1707        if ( (val & ReasonFlags.unused) != 0) {
1708            ret = RevokedCertInfo.REVOKATION_REASON_UNSPECIFIED;
1709        }
1710        return ret;
1711    }
1712    /**
1713     * Method used to insert a CN postfix into DN by extracting the first found CN appending cnpostfix and then replacing the original CN
1714     * with the new one in DN.
1715     *
1716     * If no CN could be found in DN then should the given DN be returned untouched
1717     *
1718     * @param dn the DN to manipulate, cannot be null
1719     * @param cnpostfix the postfix to insert, cannot be null
1720     * @return the new DN
1721     */

1722    public static String JavaDoc insertCNPostfix(String JavaDoc dn, String JavaDoc cnpostfix){
1723      String JavaDoc newdn = null;
1724      
1725      if ((dn != null) && (cnpostfix != null)) {
1726          String JavaDoc o;
1727          X509NameTokenizer xt = new X509NameTokenizer(dn);
1728          boolean alreadyreplaced = false;
1729          while (xt.hasMoreTokens()) {
1730              o = xt.nextToken();
1731              if (!alreadyreplaced && (o.length() > 3) &&
1732                      o.substring(0, 3).equalsIgnoreCase("cn=")) {
1733                  o += cnpostfix;
1734                  alreadyreplaced = true;
1735              }
1736              if(newdn==null){
1737                  newdn=o;
1738              }else{
1739                newdn += "," + o;
1740              }
1741          }
1742      }
1743 
1744      return newdn;
1745    }
1746    
1747    /**
1748     * class for breaking up an X500 Name into it's component tokens, ala
1749     * java.util.StringTokenizer. Taken from BouncyCastle, but does NOT
1750     * use or consider escaped characters. Used for reversing DNs without unescaping.
1751     */

1752    private static class BasicX509NameTokenizer
1753    {
1754        private String JavaDoc oid;
1755        private int index;
1756        private StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1757
1758        public BasicX509NameTokenizer(
1759            String JavaDoc oid)
1760        {
1761            this.oid = oid;
1762            this.index = -1;
1763        }
1764
1765        public boolean hasMoreTokens()
1766        {
1767            return (index != oid.length());
1768        }
1769
1770        public String JavaDoc nextToken()
1771        {
1772            if (index == oid.length())
1773            {
1774                return null;
1775            }
1776
1777            int end = index + 1;
1778            boolean quoted = false;
1779            boolean escaped = false;
1780
1781            buf.setLength(0);
1782
1783            while (end != oid.length())
1784            {
1785                char c = oid.charAt(end);
1786                
1787                if (c == '"')
1788                {
1789                    if (!escaped)
1790                    {
1791                        buf.append(c);
1792                        quoted = !quoted;
1793                    }
1794                    else
1795                    {
1796                        buf.append(c);
1797                    }
1798                    escaped = false;
1799                }
1800                else
1801                {
1802                    if (escaped || quoted)
1803                    {
1804                        buf.append(c);
1805                        escaped = false;
1806                    }
1807                    else if (c == '\\')
1808                    {
1809                        buf.append(c);
1810                        escaped = true;
1811                    }
1812                    else if ( (c == ',') && (!escaped) )
1813                    {
1814                        break;
1815                    }
1816                    else
1817                    {
1818                        buf.append(c);
1819                    }
1820                }
1821                end++;
1822            }
1823
1824            index = end;
1825            return buf.toString().trim();
1826        }
1827    }
1828
1829    /**
1830     * Obtains a Vector with the DERObjectIdentifiers for
1831     * dNObjects names.
1832     *
1833     * @return Vector with DERObjectIdentifiers defining the known order we require
1834     */

1835    private static Vector JavaDoc getDefaultX509FieldOrder(){
1836      Vector JavaDoc fieldOrder = new Vector JavaDoc();
1837      String JavaDoc[] dNObjects = DnComponents.getDnObjects();
1838      for (int i = 0; i < dNObjects.length; i++) {
1839          fieldOrder.add(DnComponents.getOid(dNObjects[i]));
1840      }
1841      return fieldOrder;
1842    }
1843    
1844    /**
1845     * Obtain a X509Name reordered, if some fields from original X509Name
1846     * doesn't appear in "ordering" parameter, they will be added at end
1847     * in the original order.
1848     *
1849     * @param x509Name the X509Name that is unordered
1850     * @param ordering Vector of DERObjectIdentifier defining the desired order of components
1851     * @return X509Name with ordered conmponents according to the orcering vector
1852     */

1853    private static X509Name getOrderedX509Name( X509Name x509Name, Vector JavaDoc ordering, X509NameEntryConverter converter ){
1854        
1855        //-- Null prevent
1856
if ( ordering == null ){ ordering = new Vector JavaDoc(); }
1857        
1858        //-- New order for the X509 Fields
1859
Vector JavaDoc newOrdering = new Vector JavaDoc();
1860        Vector JavaDoc newValues = new Vector JavaDoc();
1861        
1862        Hashtable JavaDoc ht = new Hashtable JavaDoc();
1863        Iterator JavaDoc it = ordering.iterator();
1864        
1865        //-- Add ordered fields
1866
while( it.hasNext() ){
1867            DERObjectIdentifier oid = (DERObjectIdentifier) it.next();
1868            
1869            if ( !ht.containsKey(oid) ){
1870                Vector JavaDoc valueList = getX509NameFields(x509Name, oid);
1871                //-- Only add the OID if has not null value
1872
if ( valueList != null ){
1873                    Iterator JavaDoc itVals = valueList.iterator();
1874                    while( itVals.hasNext() ){
1875                        Object JavaDoc value = itVals.next();
1876                        ht.put(oid, value);
1877                        newOrdering.add(oid);
1878                        newValues.add(value);
1879                    }
1880                }
1881            } // if ht.containsKey
1882
} // while it.hasNext
1883

1884        Vector JavaDoc allOids = x509Name.getOIDs();
1885        
1886        //-- Add unexpected fields to the end
1887
for ( int i=0; i<allOids.size(); i++ ) {
1888            
1889            DERObjectIdentifier oid = (DERObjectIdentifier) allOids.get(i);
1890            
1891            
1892            if ( !ht.containsKey(oid) ){
1893                Vector JavaDoc valueList = getX509NameFields(x509Name, oid);
1894                
1895                //-- Only add the OID if has not null value
1896
if ( valueList != null ){
1897                    Iterator JavaDoc itVals = valueList.iterator();
1898                    
1899                    while( itVals.hasNext() ){
1900                        Object JavaDoc value = itVals.next();
1901                        ht.put(oid, value);
1902                        newOrdering.add(oid);
1903                        newValues.add(value);
1904                        log.debug("added --> " + oid + " val: " + value);
1905                    }
1906                }
1907            }
1908        }
1909        
1910        //-- Create X509Name with the ordered fields
1911
X509Name orderedName = new X509Name(newOrdering, newValues, converter);
1912        
1913        return orderedName;
1914    }
1915    
1916    
1917    /**
1918     * Obtain the values for a DN field from X509Name, or null in case
1919     * of the field does not exist.
1920     *
1921     * @param name
1922     * @param id
1923     * @return
1924     */

1925    private static Vector JavaDoc getX509NameFields( X509Name name, DERObjectIdentifier id ){
1926      
1927      Vector JavaDoc oids = name.getOIDs();
1928      Vector JavaDoc values = name.getValues();
1929      Vector JavaDoc vRet = null;
1930      
1931      for ( int i=0; i<oids.size(); i++ ){
1932        
1933        if ( id.equals(oids.elementAt(i)) ){
1934          if ( vRet == null ){ vRet = new Vector JavaDoc(); }
1935          vRet.add(values.get(i));
1936        }
1937        
1938      }
1939      
1940      return vRet;
1941      
1942    }
1943    
1944    
1945    /**
1946     * Obtain the directory string for the directoryName generation
1947     * form the Subject Alternative Name String.
1948     *
1949     * @param altName
1950     * @return
1951     */

1952    private static String JavaDoc getDirectoryStringFromAltName(String JavaDoc altName) {
1953      
1954        DNFieldExtractor dnfe = new DNFieldExtractor(altName, DNFieldExtractor.TYPE_SUBJECTALTNAME);
1955        String JavaDoc directoryName = dnfe.getField(DNFieldExtractor.DIRECTORYNAME, 0);
1956        
1957        /** TODO: Validate or restrict the directoryName Fields? */
1958      
1959        return ( "".equals(directoryName) ? null : directoryName );
1960    }
1961    
1962} // CertTools
1963
Popular Tags