KickJava   Java API By Example, From Geeks To Geeks.

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


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.ByteArrayInputStream JavaDoc;
17 import java.io.IOException JavaDoc;
18 import java.security.InvalidAlgorithmParameterException JavaDoc;
19 import java.security.KeyFactory JavaDoc;
20 import java.security.KeyPair JavaDoc;
21 import java.security.KeyPairGenerator JavaDoc;
22 import java.security.KeyStore JavaDoc;
23 import java.security.KeyStoreException JavaDoc;
24 import java.security.NoSuchAlgorithmException JavaDoc;
25 import java.security.NoSuchProviderException JavaDoc;
26 import java.security.PrivateKey JavaDoc;
27 import java.security.PublicKey JavaDoc;
28 import java.security.SecureRandom JavaDoc;
29 import java.security.cert.Certificate JavaDoc;
30 import java.security.cert.CertificateException JavaDoc;
31 import java.security.cert.CertificateFactory JavaDoc;
32 import java.security.cert.X509Certificate JavaDoc;
33 import java.security.interfaces.ECPublicKey JavaDoc;
34 import java.security.interfaces.RSAPublicKey JavaDoc;
35 import java.security.spec.InvalidKeySpecException JavaDoc;
36 import java.security.spec.PKCS8EncodedKeySpec JavaDoc;
37 import java.util.ArrayList JavaDoc;
38 import java.util.Collection JavaDoc;
39
40 import org.apache.commons.lang.StringUtils;
41 import org.apache.log4j.Logger;
42 import org.bouncycastle.asn1.ASN1InputStream;
43 import org.bouncycastle.asn1.ASN1Sequence;
44 import org.bouncycastle.asn1.DERBMPString;
45 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
46 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
47 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
48 import org.bouncycastle.jce.ECNamedCurveTable;
49 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
50 import org.bouncycastle.jce.provider.JCEECPublicKey;
51 import org.ejbca.core.model.ca.catoken.CATokenConstants;
52
53
54 /**
55  * Tools to handle common key and keystore operations.
56  *
57  * @version $Id: KeyTools.java,v 1.4 2006/10/31 08:24:12 anatom Exp $
58  */

59 public class KeyTools {
60     private static Logger log = Logger.getLogger(KeyTools.class);
61
62     /**
63      * Prevent from creating new KeyTools object
64      */

65     private KeyTools() {
66     }
67
68     /**
69      * Generates a keypair
70      *
71      * @param keyspec specification of keys to generate, typical value is 1024 for RSA keys or prime192v1 for ECDSA keys
72      * @param keyalg algorithm of keys to generate, typical value is RSA or ECDSA, see org.ejbca.core.model.ca.catoken.CATokenConstants.KEYALGORITHM_XX
73      *
74      * @see org.ejbca.core.model.ca.catoken.CATokenConstants
75      * @see org.bouncycastle.asn1.x9.X962NamedCurves
76      * @see org.bouncycastle.asn1.nist.NISTNamedCurves
77      * @see org.bouncycastle.asn1.sec.SECNamedCurves
78      *
79      * @return KeyPair the generated keypair
80      * @throws InvalidAlgorithmParameterException
81      */

82     public static KeyPair JavaDoc genKeys(String JavaDoc keySpec, String JavaDoc keyAlg)
83         throws NoSuchAlgorithmException JavaDoc, NoSuchProviderException JavaDoc, InvalidAlgorithmParameterException JavaDoc {
84         if (log.isDebugEnabled()) {
85             log.debug(">genKeys("+keySpec+", "+keyAlg+")");
86         }
87
88         KeyPairGenerator JavaDoc keygen = KeyPairGenerator.getInstance(keyAlg, "BC");
89         if (StringUtils.equals(keyAlg, CATokenConstants.KEYALGORITHM_ECDSA)) {
90             org.bouncycastle.jce.spec.ECParameterSpec ecSpec = null;
91             if ( (keySpec == null) || StringUtils.equals(keySpec,"implicitlyCA") ) {
92                 log.debug("Generating implicitlyCA encoded ECDSA key pair");
93                 // If the keySpec is null, we have "implicitlyCA" defined EC parameters
94
// The parameters were already installed when we installed the provider
95
// We just make sure that ecSpec == null here
96
} else {
97                 log.debug("Generating named curve ECDSA key pair");
98                 // We have EC keys
99
ecSpec = ECNamedCurveTable.getParameterSpec(keySpec);
100             }
101             keygen.initialize(ecSpec, new SecureRandom JavaDoc());
102         } else {
103             // RSA keys
104
int keysize = Integer.parseInt(keySpec);
105             keygen.initialize(keysize);
106         }
107
108         KeyPair JavaDoc keys = keygen.generateKeyPair();
109
110         if (log.isDebugEnabled()) {
111             PublicKey JavaDoc pk = keys.getPublic();
112             int len = getKeyLength(pk);
113             log.debug("Generated " + keys.getPublic().getAlgorithm() + " keys with length " + len);
114             log.debug("<genKeys()");
115         }
116
117         return keys;
118     } // genKeys
119

120     /**
121      * Gets the key length of supported keys
122      * @param priv PrivateKey to check
123      * @return -1 if key is unsupported, otherwise a number >= 0. 0 usually means the length can not be calculated,
124      * for example if the key is en EC key and the "implicitlyCA" encoding is used.
125      */

126     public static int getKeyLength(PublicKey JavaDoc pk) {
127         int len = -1;
128         if (pk instanceof RSAPublicKey JavaDoc) {
129             RSAPublicKey JavaDoc rsapub = (RSAPublicKey JavaDoc) pk;
130             len = rsapub.getModulus().bitLength();
131         } else if (pk instanceof JCEECPublicKey) {
132             JCEECPublicKey ecpriv = (JCEECPublicKey) pk;
133             org.bouncycastle.jce.spec.ECParameterSpec spec = ecpriv.getParameters();
134             if (spec != null) {
135                 len = spec.getN().bitLength();
136             } else {
137                 // We support the key, but we don't know the key length
138
len = 0;
139             }
140         } else if (pk instanceof ECPublicKey JavaDoc) {
141             ECPublicKey JavaDoc ecpriv = (ECPublicKey JavaDoc) pk;
142             java.security.spec.ECParameterSpec JavaDoc spec = ecpriv.getParams();
143             if (spec != null) {
144                 len = spec.getOrder().bitLength(); // does this really return something we expect?
145
} else {
146                 // We support the key, but we don't know the key length
147
len = 0;
148             }
149         }
150         return len;
151     }
152
153     /**
154      * Creates PKCS12-file that can be imported in IE or Netscape. The alias for the private key is
155      * set to 'privateKey' and the private key password is null.
156      *
157      * @param alias the alias used for the key entry
158      * @param privKey RSA private key
159      * @param cert user certificate
160      * @param cacert CA-certificate or null if only one cert in chain, in that case use 'cert'.
161      *
162      * @return KeyStore containing PKCS12-keystore
163      *
164      * @exception Exception if input parameters are not OK or certificate generation fails
165      */

166     public static KeyStore JavaDoc createP12(String JavaDoc alias, PrivateKey JavaDoc privKey, X509Certificate JavaDoc cert, X509Certificate JavaDoc cacert)
167     throws IOException JavaDoc, KeyStoreException JavaDoc, CertificateException JavaDoc, NoSuchProviderException JavaDoc, NoSuchAlgorithmException JavaDoc, InvalidKeySpecException JavaDoc {
168         Certificate JavaDoc[] chain;
169
170         if (cacert == null) {
171             chain = null;
172         } else {
173             chain = new Certificate JavaDoc[1];
174             chain[0] = cacert;
175         }
176
177         return createP12(alias, privKey, cert, chain);
178     } // createP12
179

180     /**
181      * Creates PKCS12-file that can be imported in IE or Netscape.
182      * The alias for the private key is set to 'privateKey' and the private key password is null.
183      * @param alias the alias used for the key entry
184      * @param privKey RSA private key
185      * @param cert user certificate
186      * @param cacert Collection of X509Certificate, or null if only one cert in chain, in that case use 'cert'.
187      * @param username user's username
188      * @param password user's password
189      * @return KeyStore containing PKCS12-keystore
190      * @exception Exception if input parameters are not OK or certificate generation fails
191      */

192     public static KeyStore JavaDoc createP12(String JavaDoc alias, PrivateKey JavaDoc privKey, X509Certificate JavaDoc cert, Collection JavaDoc cacerts)
193     throws IOException JavaDoc, KeyStoreException JavaDoc, CertificateException JavaDoc, NoSuchProviderException JavaDoc, NoSuchAlgorithmException JavaDoc, InvalidKeySpecException JavaDoc {
194         Certificate JavaDoc[] chain;
195         if (cacerts == null)
196             chain = null;
197         else {
198             chain = new Certificate JavaDoc[cacerts.size()];
199             chain = (Certificate JavaDoc[])cacerts.toArray(chain);
200         }
201         return createP12(alias, privKey, cert, chain);
202     } // createP12
203

204     /**
205      * Creates PKCS12-file that can be imported in IE or Netscape. The alias for the private key is
206      * set to 'privateKey' and the private key password is null.
207      *
208      * @param alias the alias used for the key entry
209      * @param privKey RSA private key
210      * @param cert user certificate
211      * @param cachain CA-certificate chain or null if only one cert in chain, in that case use 'cert'.
212      * @return KeyStore containing PKCS12-keystore
213      * @exception Exception if input parameters are not OK or certificate generation fails
214      */

215     public static KeyStore JavaDoc createP12(String JavaDoc alias, PrivateKey JavaDoc privKey, X509Certificate JavaDoc cert, Certificate JavaDoc[] cachain)
216     throws IOException JavaDoc, KeyStoreException JavaDoc, CertificateException JavaDoc, NoSuchProviderException JavaDoc, NoSuchAlgorithmException JavaDoc, InvalidKeySpecException JavaDoc {
217         log.debug(">createP12: alias=" + alias + ", privKey, cert=" + CertTools.getSubjectDN(cert) +", cachain.length=" + ((cachain == null) ? 0 : cachain.length));
218
219         // Certificate chain
220
if (cert == null) {
221             throw new IllegalArgumentException JavaDoc("Parameter cert cannot be null.");
222         }
223         int len = 1;
224         if (cachain != null) {
225             len += cachain.length;
226         }
227         Certificate JavaDoc[] chain = new Certificate JavaDoc[len];
228         // To not get a ClassCastException we need to genereate a real new certificate with BC
229
CertificateFactory JavaDoc cf = CertTools.getCertificateFactory();
230         chain[0] = cf.generateCertificate(new ByteArrayInputStream JavaDoc(cert.getEncoded()));
231
232         if (cachain != null) {
233             for (int i = 0; i < cachain.length; i++) {
234                 X509Certificate JavaDoc tmpcert = (X509Certificate JavaDoc) cf.generateCertificate(new ByteArrayInputStream JavaDoc(
235                             cachain[i].getEncoded()));
236                 chain[i + 1] = tmpcert;
237             }
238         }
239         if (chain.length > 1) {
240             for (int i = 1; i < chain.length; i++) {
241                 X509Certificate JavaDoc cacert = (X509Certificate JavaDoc) cf.generateCertificate(new ByteArrayInputStream JavaDoc(
242                             chain[i].getEncoded()));
243                 // Set attributes on CA-cert
244
PKCS12BagAttributeCarrier caBagAttr = (PKCS12BagAttributeCarrier) chain[i];
245                 // We constuct a friendly name for the CA, and try with some parts from the DN if they exist.
246
String JavaDoc cafriendly = CertTools.getPartFromDN(CertTools.getSubjectDN(cacert), "CN");
247                 // On the ones below we +i to make it unique, O might not be otherwise
248
if (cafriendly == null) {
249                     cafriendly = CertTools.getPartFromDN(CertTools.getSubjectDN(cacert), "O")+i;
250                 }
251                 if (cafriendly == null) {
252                     cafriendly = CertTools.getPartFromDN(CertTools.getSubjectDN(cacert), "OU"+i);
253                 }
254                 if (cafriendly == null) {
255                     cafriendly = "CA_unknown"+i;
256                 }
257                 caBagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,
258                     new DERBMPString(cafriendly));
259             }
260         }
261
262         // Set attributes on user-cert
263
PKCS12BagAttributeCarrier certBagAttr = (PKCS12BagAttributeCarrier) chain[0];
264         certBagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(alias));
265         // in this case we just set the local key id to that of the public key
266
certBagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, createSubjectKeyId(chain[0].getPublicKey()));
267         // "Clean" private key, i.e. remove any old attributes
268
KeyFactory JavaDoc keyfact = KeyFactory.getInstance(privKey.getAlgorithm(), "BC");
269         PrivateKey JavaDoc pk = keyfact.generatePrivate(new PKCS8EncodedKeySpec JavaDoc(privKey.getEncoded()));
270         // Set attributes for private key
271
PKCS12BagAttributeCarrier keyBagAttr = (PKCS12BagAttributeCarrier) pk;
272         // in this case we just set the local key id to that of the public key
273
keyBagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(alias));
274         keyBagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, createSubjectKeyId(chain[0].getPublicKey()));
275         // store the key and the certificate chain
276
KeyStore JavaDoc store = KeyStore.getInstance("PKCS12", "BC");
277         store.load(null, null);
278         store.setKeyEntry(alias, pk, null, chain);
279         log.debug("<createP12: alias=" + alias + ", privKey, cert=" + CertTools.getSubjectDN(cert) + ", cachain.length=" + ((cachain == null) ? 0 : cachain.length));
280
281         return store;
282     } // createP12
283

284     /**
285      * Creates JKS-file that can be used with JDK. The alias for the private key is set to
286      * 'privateKey' and the private key password is null.
287      *
288      * @param alias the alias used for the key entry
289      * @param privKey RSA private key
290      * @param password user's password
291      * @param cert user certificate
292      * @param cachain CA-certificate chain or null if only one cert in chain, in that case use
293      * 'cert'.
294      *
295      * @return KeyStore containing JKS-keystore
296      *
297      * @exception Exception if input parameters are not OK or certificate generation fails
298      */

299     public static KeyStore JavaDoc createJKS(String JavaDoc alias, PrivateKey JavaDoc privKey, String JavaDoc password,
300         X509Certificate JavaDoc cert, Certificate JavaDoc[] cachain) throws Exception JavaDoc {
301         log.debug(">createJKS: alias=" + alias + ", privKey, cert=" + CertTools.getSubjectDN(cert) +
302             ", cachain.length=" + ((cachain == null) ? 0 : cachain.length));
303
304         String JavaDoc caAlias = "cacert";
305
306         // Certificate chain
307
if (cert == null) {
308             throw new IllegalArgumentException JavaDoc("Parameter cert cannot be null.");
309         }
310         int len = 1;
311         if (cachain != null) {
312             len += cachain.length;
313         }
314         Certificate JavaDoc[] chain = new Certificate JavaDoc[len];
315         chain[0] = cert;
316         if (cachain != null) {
317             for (int i = 0; i < cachain.length; i++) {
318                 chain[i + 1] = cachain[i];
319             }
320         }
321
322         // store the key and the certificate chain
323
KeyStore JavaDoc store = KeyStore.getInstance("JKS");
324         store.load(null, null);
325
326         // First load the key entry
327
X509Certificate JavaDoc[] usercert = new X509Certificate JavaDoc[1];
328         usercert[0] = cert;
329         store.setKeyEntry(alias, privKey, password.toCharArray(), usercert);
330
331         // Add the root cert as trusted
332
if (cachain != null) {
333             if (!CertTools.isSelfSigned((X509Certificate JavaDoc) cachain[cachain.length - 1])) {
334                 throw new IllegalArgumentException JavaDoc("Root cert is not self-signed.");
335             }
336             store.setCertificateEntry(caAlias, cachain[cachain.length - 1]);
337         }
338
339         // Set the complete chain
340
log.debug("Storing cert chain of length " + chain.length);
341         store.setKeyEntry(alias, privKey, password.toCharArray(), chain);
342         log.debug("<createJKS: alias=" + alias + ", privKey, cert=" + CertTools.getSubjectDN(cert) +
343             ", cachain.length=" + ((cachain == null) ? 0 : cachain.length));
344
345         return store;
346     } // createJKS
347

348     /**
349      * Retrieves the certificate chain from a keystore.
350      *
351      * @param keyStore the keystore, which has been loaded and opened.
352      * @param privateKeyAlias the alias of the privatekey for which the certchain belongs.
353      *
354      * @return array of Certificate, or null if no certificates are found.
355      */

356     public static Certificate JavaDoc[] getCertChain(KeyStore JavaDoc keyStore, String JavaDoc privateKeyAlias)
357         throws KeyStoreException JavaDoc {
358         log.debug(">getCertChain: alias='" + privateKeyAlias + "'");
359
360         Certificate JavaDoc[] certchain = keyStore.getCertificateChain(privateKeyAlias);
361         if (certchain == null) {
362             return null;
363         }
364         log.debug("Certchain retrieved from alias '" + privateKeyAlias + "' has length " +
365             certchain.length);
366
367         if (certchain.length < 1) {
368             log.error("Cannot load certificate chain with alias '" + privateKeyAlias +
369                 "' from keystore.");
370             log.debug("<getCertChain: alias='" + privateKeyAlias + "', retlength=" +
371                 certchain.length);
372
373             return certchain;
374         } else if (certchain.length > 0) {
375             if (CertTools.isSelfSigned((X509Certificate JavaDoc) certchain[certchain.length - 1])) {
376                 log.debug("Issuer='" +
377                     CertTools.getIssuerDN((X509Certificate JavaDoc) certchain[certchain.length - 1]) +
378                     "'.");
379                 log.debug("Subject='" +
380                     CertTools.getSubjectDN((X509Certificate JavaDoc) certchain[certchain.length - 1]) +
381                     "'.");
382                 log.debug("<getCertChain: alias='" + privateKeyAlias + "', retlength=" +
383                     certchain.length);
384
385                 return certchain;
386             }
387         }
388
389         // If we came here, we have a cert which is not root cert in 'cert'
390
ArrayList JavaDoc array = new ArrayList JavaDoc();
391
392         for (int i = 0; i < certchain.length; i++) {
393             array.add(certchain[i]);
394         }
395
396         boolean stop = false;
397
398         while (!stop) {
399             X509Certificate JavaDoc cert = (X509Certificate JavaDoc) array.get(array.size() - 1);
400             String JavaDoc ialias = CertTools.getPartFromDN(CertTools.getIssuerDN(cert), "CN");
401             Certificate JavaDoc[] chain1 = keyStore.getCertificateChain(ialias);
402
403             if (chain1 == null) {
404                 stop = true;
405             } else {
406                 log.debug("Loaded certificate chain with length " + chain1.length +
407                     " with alias '" + ialias + "'.");
408
409                 if (chain1.length == 0) {
410                     log.error("No RootCA certificate found!");
411                     stop = true;
412                 }
413
414                 for (int j = 0; j < chain1.length; j++) {
415                     array.add(chain1[j]);
416
417                     // If one cert is slefsigned, we have found a root certificate, we don't need to go on anymore
418
if (CertTools.isSelfSigned((X509Certificate JavaDoc) chain1[j])) {
419                         stop = true;
420                     }
421                 }
422             }
423         }
424
425         Certificate JavaDoc[] ret = new Certificate JavaDoc[array.size()];
426
427         for (int i = 0; i < ret.length; i++) {
428             ret[i] = (X509Certificate JavaDoc) array.get(i);
429             log.debug("Issuer='" + CertTools.getIssuerDN((X509Certificate JavaDoc) ret[i]) + "'.");
430             log.debug("Subject='" + CertTools.getSubjectDN((X509Certificate JavaDoc) ret[i]) + "'.");
431         }
432
433         log.debug("<getCertChain: alias='" + privateKeyAlias + "', retlength=" + ret.length);
434
435         return ret;
436     } // getCertChain
437

438     /**
439      * create the subject key identifier.
440      *
441      * @param pubKey the public key
442      *
443      * @return SubjectKeyIdentifer asn.1 structure
444      */

445     public static SubjectKeyIdentifier createSubjectKeyId(PublicKey JavaDoc pubKey) {
446         try {
447             ByteArrayInputStream JavaDoc bIn = new ByteArrayInputStream JavaDoc(pubKey.getEncoded());
448             SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(
449                         bIn).readObject());
450
451             return new SubjectKeyIdentifier(info);
452         } catch (Exception JavaDoc e) {
453             throw new RuntimeException JavaDoc("error creating key");
454         }
455     } // createSubjectKeyId
456

457 } // KeyTools
458
Popular Tags