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