View Javadoc

1   package org.eparapher.core.crypto.cert;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.ByteArrayOutputStream;
5   import java.io.File;
6   import java.io.FileWriter;
7   import java.io.IOException;
8   import java.io.UnsupportedEncodingException;
9   import java.math.BigInteger;
10  import java.security.GeneralSecurityException;
11  import java.security.InvalidAlgorithmParameterException;
12  import java.security.InvalidKeyException;
13  import java.security.KeyPair;
14  import java.security.KeyPairGenerator;
15  import java.security.KeyStore;
16  import java.security.KeyStoreException;
17  import java.security.NoSuchAlgorithmException;
18  import java.security.NoSuchProviderException;
19  import java.security.Principal;
20  import java.security.PrivateKey;
21  import java.security.PublicKey;
22  import java.security.SecureRandom;
23  import java.security.Signature;
24  import java.security.SignatureException;
25  import java.security.cert.CertPath;
26  import java.security.cert.CertPathBuilder;
27  import java.security.cert.CertPathBuilderException;
28  import java.security.cert.CertPathValidator;
29  import java.security.cert.CertPathValidatorException;
30  import java.security.cert.CertPathValidatorResult;
31  import java.security.cert.CertSelector;
32  import java.security.cert.CertStore;
33  import java.security.cert.Certificate;
34  import java.security.cert.CertificateEncodingException;
35  import java.security.cert.CertificateException;
36  import java.security.cert.CertificateFactory;
37  import java.security.cert.CertificateParsingException;
38  import java.security.cert.CollectionCertStoreParameters;
39  import java.security.cert.PKIXBuilderParameters;
40  import java.security.cert.PKIXCertPathValidatorResult;
41  import java.security.cert.PKIXParameters;
42  import java.security.cert.X509CRL;
43  import java.security.cert.X509CertSelector;
44  import java.security.cert.X509Certificate;
45  import java.util.ArrayList;
46  import java.util.Arrays;
47  import java.util.Collection;
48  import java.util.Collections;
49  import java.util.Date;
50  import java.util.Enumeration;
51  import java.util.HashMap;
52  import java.util.Hashtable;
53  import java.util.LinkedList;
54  import java.util.List;
55  import java.util.Map;
56  import java.util.Vector;
57  import java.util.regex.Matcher;
58  import java.util.regex.Pattern;
59  
60  import org.apache.log4j.Level;
61  import org.apache.log4j.Logger;
62  import org.bouncycastle.asn1.ASN1Encodable;
63  import org.bouncycastle.asn1.ASN1InputStream;
64  import org.bouncycastle.asn1.ASN1Sequence;
65  import org.bouncycastle.asn1.DEREncodable;
66  import org.bouncycastle.asn1.DERObjectIdentifier;
67  import org.bouncycastle.asn1.DEROutputStream;
68  import org.bouncycastle.asn1.DERSequence;
69  import org.bouncycastle.asn1.DERTaggedObject;
70  import org.bouncycastle.asn1.DERUTF8String;
71  import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
72  import org.bouncycastle.asn1.x509.DistributionPoint;
73  import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
74  import org.bouncycastle.asn1.x509.GeneralName;
75  import org.bouncycastle.asn1.x509.GeneralNames;
76  import org.bouncycastle.asn1.x509.KeyPurposeId;
77  import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
78  import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
79  import org.bouncycastle.asn1.x509.X509Extensions;
80  import org.bouncycastle.asn1.x509.X509Name;
81  import org.bouncycastle.cms.CMSSignedGenerator;
82  import org.bouncycastle.jce.ECNamedCurveTable;
83  import org.bouncycastle.jce.PKCS10CertificationRequest;
84  import org.bouncycastle.jce.X509KeyUsage;
85  import org.bouncycastle.jce.spec.ECParameterSpec;
86  import org.bouncycastle.openssl.PEMWriter;
87  import org.bouncycastle.x509.X509V3CertificateGenerator;
88  import org.eparapher.core.EParapherManager;
89  import org.eparapher.core.crypto.EPCryptoProviderManager;
90  import org.eparapher.core.crypto.EPKeystoreManager;
91  import org.eparapher.core.tools.JVMSettings;
92  
93  public class CertificateManager {
94  	
95  	private static Logger log = Logger.getLogger(CertificateManager.class);
96  	
97  	private static Pattern cnPattern = Pattern.compile("(?i)(cn=)([^,]*)");
98  
99  	    /**
100 	     * Returns the identities of the remote server as defined in the specified certificate. The
101 	     * identities are defined in the subjectDN of the certificate and it can also be defined in
102 	     * the subjectAltName extensions of type "xmpp". When the extension is being used then the
103 	     * identities defined in the extension are going to be returned. Otherwise, the value stored in
104 	     * the subjectDN is returned.
105 	     *
106 	     * @param x509Certificate the certificate the holds the identities of the remote server.
107 	     * @return the identities of the remote server as defined in the specified certificate.
108 	     */
109 	    public static List<String> getPeerIdentities(X509Certificate x509Certificate) {
110 	        // Look the identity in the subjectAltName extension if available
111 	        List<String> names = getSubjectAlternativeNames(x509Certificate);
112 	        if (names.isEmpty()) {
113 	            String name = x509Certificate.getSubjectDN().getName();
114 	            Matcher matcher = cnPattern.matcher(name);
115 	            if (matcher.find()) {
116 	                name = matcher.group(2);
117 	            }
118 	            // Create an array with the unique identity
119 	            names = new ArrayList<String>();
120 	            names.add(name);
121 	        }
122 	        return names;
123 	    }
124 
125 	    /**
126 	     * Returns the JID representation of an XMPP entity contained as a SubjectAltName extension
127 	     * in the certificate. If none was found then return <tt>null</tt>.
128 	     *
129 	     * @param certificate the certificate presented by the remote entity.
130 	     * @return the JID representation of an XMPP entity contained as a SubjectAltName extension
131 	     *         in the certificate. If none was found then return <tt>null</tt>.
132 	     */
133 	    private static List<String> getSubjectAlternativeNames(X509Certificate certificate) {
134 	        List<String> identities = new ArrayList<String>();
135 	        try {
136 	            Collection<List<?>> altNames = certificate.getSubjectAlternativeNames();
137 	            // Check that the certificate includes the SubjectAltName extension
138 	            if (altNames == null) {
139 	                return Collections.emptyList();
140 	            }
141 	            // Use the type OtherName to search for the certified server name
142 	            for (List<?> item : altNames) {
143 	                Integer type = (Integer) item.get(0);
144 	                if (type == 0) {
145 	                    // Type OtherName found so return the associated value
146 	                    try {
147 	                        // Value is encoded using ASN.1 so decode it to get the server's identity
148 	                        ASN1InputStream decoder = new ASN1InputStream((byte[]) item.toArray()[1]);
149 	                        DEREncodable encoded = decoder.readObject();
150 	                        encoded = ((DERSequence) encoded).getObjectAt(1);
151 	                        encoded = ((DERTaggedObject) encoded).getObject();
152 	                        encoded = ((DERTaggedObject) encoded).getObject();
153 	                        String identity = ((DERUTF8String) encoded).getString();
154 	                        // Add the decoded server name to the list of identities
155 	                        identities.add(identity);
156 	                    }
157 	                    catch (UnsupportedEncodingException e) {
158 	                    	log.error("Error decoding subjectAltName",e);
159 	                    }
160 	                    catch (IOException e) {
161 	                    	log.error("Error decoding subjectAltName",e);
162 	                    }
163 	                    catch (Exception e) {
164 	                        log.error("Error decoding subjectAltName",e);
165 	                    }
166 	                }
167 	                // Other types are not good for XMPP so ignore them
168 	                log.info("SubjectAltName of invalid type found: " + certificate);
169 	            }
170 	        }
171 	        catch (CertificateParsingException e) {
172 	            log.error("Error parsing SubjectAltName in certificate: ",e);
173 	        }
174 	        return identities;
175 	    }
176 		public static boolean isValidKeyUsageForEncryption(X509Certificate cert) {
177 			boolean[] keyUsage = cert.getKeyUsage();
178 			return keyUsage[CertificateInfo.KEYENCIPHERMENT];
179 		}
180 
181 		public static boolean isValidKeyUsageForNonRepudiation(X509Certificate cert) {
182 			boolean[] keyUsage = cert.getKeyUsage();
183 			return keyUsage[CertificateInfo.NONREPUDIATION];
184 		}
185 
186 		public static boolean isValidKeyUsageForSignature(X509Certificate cert) {
187 			boolean[] keyUsage = cert.getKeyUsage();
188 			return keyUsage[CertificateInfo.DIGITALSIGNATURE];
189 		}
190 	    /**
191 	     * Returns true if an RSA certificate was found in the specified keystore for the specified domain.
192 	     *
193 	     * @param ksKeys the keystore that contains the certificates.
194 	     * @param domain domain of the server signed by the certificate.
195 	     * @return true if an RSA certificate was found in the specified keystore for the specified domain.
196 	     * @throws KeyStoreException
197 	     */
198 	    public static boolean isRSACertificate(KeyStore ksKeys, String domain) throws KeyStoreException {
199 	        return isCertificate(ksKeys, domain, "RSA");
200 	    }
201 
202 	    /**
203 	     * Returns true if an DSA certificate was found in the specified keystore for the specified domain.
204 	     *
205 	     * @param ksKeys the keystore that contains the certificates.
206 	     * @param domain domain of the server signed by the certificate.
207 	     * @return true if an DSA certificate was found in the specified keystore for the specified domain.
208 	     * @throws KeyStoreException
209 	     */
210 	    public static boolean isDSACertificate(KeyStore ksKeys, String domain) throws KeyStoreException {
211 	        return isCertificate(ksKeys, domain, "DSA");
212 	    }
213 
214 	    /**
215 	     * Returns true if the specified certificate is using the DSA algorithm. The DSA algorithm is not
216 	     * good for encryption but only for authentication. On the other hand, the RSA algorithm is good
217 	     * for encryption and authentication.
218 	     *
219 	     * @param certificate the certificate to analyze.
220 	     * @return true if the specified certificate is using the DSA algorithm.
221 	     * @throws KeyStoreException
222 	     */
223 	    public static boolean isDSACertificate(X509Certificate certificate) throws KeyStoreException {
224 	        return certificate.getPublicKey().getAlgorithm().equals("DSA");
225 	    }
226 
227 	    /**
228 	     * Returns true if the specified certificate is using the Elliptic Curve (EC) algorithm. This algorithm is
229 	     * good for signature operations.
230 	     *
231 	     * @param certificate the certificate to analyze.
232 	     * @return true if the specified certificate is using the DSA algorithm.
233 	     * @throws KeyStoreException
234 	     */
235 	    public static boolean isECCertificate(X509Certificate certificate) throws KeyStoreException {
236 	        return (certificate.getPublicKey().getAlgorithm().equals("EC") || 
237 	        		certificate.getPublicKey().getAlgorithm().equals("GOST")) ;
238 	    }
239 	    
240 	    /**
241 	     * Returns true if a certificate with the specifed configuration was found in the key store.
242 	     *
243 	     * @param ksKeys the keystore to use for searching the certificate.
244 	     * @param domain the domain present in the subjectAltName.
245 	     * @param algorithm the DSA or RSA algorithm used by the certificate.
246 	     * @return true if a certificate with the specifed configuration was found in the key store.
247 	     * @throws KeyStoreException
248 	     */
249 	    private static boolean isCertificate(KeyStore ksKeys, String domain, String algorithm) throws KeyStoreException {
250 	        for (Enumeration<String> aliases = ksKeys.aliases(); aliases.hasMoreElements();) {
251 	            X509Certificate certificate = (X509Certificate) ksKeys.getCertificate(aliases.nextElement());
252 	            for (String identity : getPeerIdentities(certificate)) {
253 	                if (identity.endsWith(domain) && certificate.getPublicKey().getAlgorithm().equals(algorithm)) {
254 	                    return true;
255 	                }
256 	            }
257 	        }
258 	        return false;
259 	    }
260 
261 	    /**
262 	     * Creates and returns the content of a new singing request for the specified certificate. Signing
263 	     * requests are required by Certificate Authorities as part of their signing process. The signing request
264 	     * contains information about the certificate issuer, subject DN, subject alternative names and public key.
265 	     * Private keys are not included. After the Certificate Authority verified and signed the certificate a new
266 	     * certificate is going to be returned. Use {@link #installReply(java.security.KeyStore, java.security.KeyStore, String, String, java.io.InputStream, boolean, boolean)}
267 	     * to import the CA reply.
268 	     *
269 	     * @param cert the certificate to create a signing request.
270 	     * @param privKey the private key of the certificate.
271 	     * @return the content of a new singing request for the specified certificate.
272 	     * @throws Exception
273 	     */
274 	    public static String createSigningRequest(X509Certificate cert, PrivateKey privKey) throws Exception {
275 	        StringBuilder sb = new StringBuilder();
276 
277 	        String subject = cert.getSubjectDN().getName();
278 	        X509Name xname = new X509Name(subject);
279 
280 	        PublicKey pubKey = cert.getPublicKey();
281 
282 	        String signatureAlgorithm = cert.getSigAlgName();
283 
284 	        PKCS10CertificationRequest csr =
285 	                new PKCS10CertificationRequest(signatureAlgorithm, xname, pubKey, null, privKey);
286 
287 	        ByteArrayOutputStream baos = new ByteArrayOutputStream();
288 	        DEROutputStream deros = new DEROutputStream(baos);
289 	        deros.writeObject(csr.getDERObject());
290 	        String sTmp = new String(org.bouncycastle.util.encoders.Base64.encode(baos.toByteArray()));
291 
292 	        // Header
293 	        sb.append(X509Util.BEGIN_CERT_REQ + "\n");
294 
295 	        // Add signing request content (base 64 encoded)
296 	        for (int iCnt = 0; iCnt < sTmp.length(); iCnt += X509Util.CERT_REQ_LINE_LENGTH) {
297 	            int iLineLength;
298 
299 	            if ((iCnt + X509Util.CERT_REQ_LINE_LENGTH) > sTmp.length()) {
300 	                iLineLength = sTmp.length() - iCnt;
301 	            } else {
302 	                iLineLength = X509Util.CERT_REQ_LINE_LENGTH;
303 	            }
304 
305 	            sb.append(sTmp.substring(iCnt, iCnt + iLineLength)).append("\n");
306 	        }
307 
308 	        // Footer
309 	        sb.append(X509Util.END_CERT_REQ + "\n");
310 	        return sb.toString();
311 	    }
312 
313 	    /**
314 	     * Create a PKSC10 certification signing request using the Bouncycastle provider.<br>
315 	     * Save the CSR in a file.
316 	     * 
317 	     * @param params 
318 	     * @return the filename that contains the CSR
319 	     * @throws SignatureException 
320 	     * @throws NoSuchProviderException 
321 	     * @throws NoSuchAlgorithmException 
322 	     * @throws InvalidKeyException 
323 	     * @throws IOException 
324 	     */
325 	  public static String createSigningRequest( NewCertParams params, KeyPair keypair ) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException, IOException {
326 
327 			//Extract info
328 			PrivateKey privateKey = keypair.getPrivate();
329 			PublicKey  publicKey  = keypair.getPublic();
330 			X509Name subject = new X509Name(params.getSubjectDN());
331 			
332 			//Generate the CSR			
333 			//TODO : Manage Subject Alternative Name.
334 			//http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation
335 			PKCS10CertificationRequest certificateRequest = new PKCS10CertificationRequest( params.getCertSigAlg(), subject, publicKey, null, privateKey);
336 			
337 			//Save it to a file
338 			String csrFile = JVMSettings.getEParapherCSRDirectory() + File.separator + params.getAlias() + ".pem";
339 
340 			PEMWriter pem = new PEMWriter(new FileWriter( csrFile ));
341 			pem.writeObject(certificateRequest);
342 			pem.close();
343 
344 			return csrFile;
345 	  }
346 	  
347 	  public static X509Certificate[] generateNewCertificate( NewCertParams params, KeyPair keypair ) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchAlgorithmException, SignatureException, IOException, NoSuchProviderException {
348 
349 			PrivateKey privateKey = keypair.getPrivate();
350 			PublicKey publicKey = keypair.getPublic();
351 			
352 			X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
353 
354 			X509Name subject = new X509Name(params.getSubjectDN());
355 
356 			//TODO : sign with another alias key/cert
357 			if (params.isSelfCertSigned() || params.isCSR())
358 				certGen.setIssuerDN(subject);
359 			//else ...
360 			
361 			certGen.setSubjectDN(subject);
362 			certGen.setPublicKey(publicKey);
363 			certGen.setSignatureAlgorithm(params.getCertSigAlg());
364 			certGen.setNotBefore(params.getValidFrom());
365 			certGen.setNotAfter(params.getValidUntil());
366 			
367 			//Generate random serial number
368 			certGen.setSerialNumber(generateRandomSerial());
369 			
370 			// Setting KeyUsage
371 			int keyusage = X509KeyUsage.digitalSignature + X509KeyUsage.nonRepudiation;
372 			X509KeyUsage ku = new X509KeyUsage(keyusage);
373 			certGen.addExtension(X509Extensions.KeyUsage.getId(), false, ku);
374 			
375 			// Setting Extended KeyUsage (any)
376 			Vector<KeyPurposeId> extkeyusage = new Vector<KeyPurposeId>();
377 			extkeyusage.add(KeyPurposeId.anyExtendedKeyUsage);
378 			//extkeyusage.add(KeyPurposeId.id_kp_clientAuth);
379 			//extkeyusage.add(KeyPurposeId.id_kp_emailProtection);
380 			//extkeyusage.add(KeyPurposeId.id_kp_ipsecUser);
381 			ExtendedKeyUsage extendedKeyUsage = new ExtendedKeyUsage(KeyPurposeId.id_kp_clientAuth);
382 			certGen.addExtension(X509Extensions.ExtendedKeyUsage, false, extendedKeyUsage);
383 
384 			// Setting Subject Alternative Name
385 			if (!params.getSubaltnameEMail().equals("") 
386 					 || !params.getSubaltnameDNSName().equals("")
387 					 || !params.getSubaltnameOtherName().equals("")) {
388 				GeneralNames subjectAltName = null;
389 				ASN1Encodable[] asn1san;
390 				GeneralName email=null,othername=null,dnsname=null;
391 				int nbsubaltname = 0;
392 				
393 				if (!params.getSubaltnameEMail().equals("")) {
394 					email = new GeneralName(GeneralName.rfc822Name, params.getSubaltnameEMail());
395 					nbsubaltname++;
396 				}
397 				if (!params.getSubaltnameDNSName().equals("")) {
398 					dnsname = new GeneralName(GeneralName.dNSName, params.getSubaltnameDNSName());
399 					nbsubaltname++;
400 				}
401 				if (!params.getSubaltnameOtherName().equals("")) {
402 					ASN1Encodable[] othernameasn1 = new ASN1Encodable[]{ new DERObjectIdentifier("1.3.6.1.5.5.7.8.5"),
403 							             								 new DERTaggedObject(true, 0, new DERUTF8String(params.getSubaltnameOtherName())) };
404 					DERSequence othernameSequence = new DERSequence( othernameasn1 );
405 			        othername = new GeneralName(GeneralName.otherName, othernameSequence);
406 					nbsubaltname++;
407 				}
408 				
409 				asn1san= new ASN1Encodable[nbsubaltname];
410 				nbsubaltname = 0;
411 				if (email!=null)
412 					asn1san[nbsubaltname++] = email;
413 				if (dnsname!=null)
414 					asn1san[nbsubaltname++] = dnsname;
415 				if (othername!=null)
416 					asn1san[nbsubaltname++] = othername;
417 				
418 				subjectAltName = new GeneralNames( new DERSequence(asn1san));
419 				certGen.addExtension(X509Extensions.SubjectAlternativeName, false, subjectAltName);
420 			}
421 			
422 			// Subject and Authority key identifier is always non-critical
423 			//  and MUST be present for certificates to verify in Mozilla.
424 			SubjectKeyIdentifier ski;
425 			AuthorityKeyIdentifier aki;
426 			
427 			ASN1Sequence spki_asn1 = (ASN1Sequence) new ASN1InputStream( new ByteArrayInputStream(publicKey.getEncoded())).readObject();
428 			SubjectPublicKeyInfo spki = new	SubjectPublicKeyInfo( spki_asn1);
429 			ski = new SubjectKeyIdentifier(spki);
430 			ASN1Sequence ac_spki_asn1 = (ASN1Sequence) new ASN1InputStream(new	ByteArrayInputStream(publicKey.getEncoded())).readObject();
431 			SubjectPublicKeyInfo apki = new	SubjectPublicKeyInfo(ac_spki_asn1);
432 			aki = new AuthorityKeyIdentifier(apki);
433 
434 			certGen.addExtension(X509Extensions.SubjectKeyIdentifier.getId(), false, ski);
435 			certGen.addExtension(X509Extensions.AuthorityKeyIdentifier.getId(),	false, aki);
436 			
437 			//Load Private key
438 			X509Certificate[] userCertChain = new X509Certificate[1];
439 			PrivateKey certsigningpk = privateKey;
440 			String currentalias = EPKeystoreManager.getInstance().getUserkeystore().getDefaultAlias();
441 			
442 			//Generate CSR
443 			if (!params.isSelfCertSigned() && !params.isCSR()) {
444 				EPKeystoreManager.getInstance().getUserkeystore().setDefaultAlias(params.getIssueralias());
445 				if (!EPKeystoreManager.getInstance().getUserkeystore().loadPrivateKey()) {
446 					log.error("Need private key access to generate new certificate from " + params.getIssueralias());
447 					return null;
448 				}
449 				certsigningpk = EPKeystoreManager.getInstance().getUserkeystore().getPrivateKey();
450 
451 				X509Certificate[] issuerCertChain = EPKeystoreManager.getInstance().getUserkeystore().getX509CertificateChain();
452 				userCertChain = new X509Certificate[issuerCertChain.length+1];
453 			}
454 
455 			//Generate Certificate
456 			/*
457 			if ( EPKeystoreManager.isPKCS11Used() ) {
458 				String userKSProv = EPKeystoreManager.getInstance().getUserkeystore().getProviderName();
459 				
460 				String testData = "Test for sigggg";
461 				Signature sig = Signature.getInstance("SHA1withRSA", userKSProv);
462 			    sig.initSign( certsigningpk );
463 			    sig.update(testData.getBytes());
464 			    byte[] signedData = sig.sign();
465 			    log.debug("Signed data : " + signedData );
466 			    //sig.initVerify();
467 			    //sig.update(testData.getBytes());
468 
469 				
470 				userCertChain[userCertChain.length-1] = certGen.generate(certsigningpk, userKSProv);
471 			} else*/
472 				userCertChain[userCertChain.length-1] = certGen.generate(certsigningpk, "BC");
473 
474 			if (!params.isSelfCertSigned()) {
475 				EPKeystoreManager.getInstance().getUserkeystore().setDefaultAlias(currentalias);
476 			}
477 			
478 			return userCertChain;
479 	  }
480 
481 	  public static boolean validateCertChain( X509Certificate[] certificate, boolean trustCACerts) throws Exception {
482 		  try {
483 	            // Generate cert path
484 	            List certList = Arrays.asList(certificate);
485 	            CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC");
486 	            CertPath path = factory.generateCertPath(certList);
487 
488 	            // Use the certificates in the keystore as TrustAnchors
489 	            // TODO : Build trustanchors from trustestore and, if configured, from userkeystore.
490 	            PKIXParameters param = new PKIXParameters(EPKeystoreManager.getInstance().getTrustStore().getKeystore());
491 
492 	            // Do not check a revocation list
493 	            //TODO : check CRL here
494 	            param.setRevocationEnabled(false);
495 
496 	            // Verify the trust path using the above settings            
497 	            CertPathValidator certPathValidator = CertPathValidator.getInstance("PKIX", "BC");
498 	            CertPathValidatorResult cpvResult = certPathValidator.validate(path, param);
499 
500 	            return true;
501 	        } catch (NoSuchAlgorithmException ex) {
502 	        	log.error("error while validating a Certificate Chain",ex);
503 	            //throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[]{ex.getMessage()}, (Throwable) ex);
504 	        } catch (CertificateException ex) {
505 	        	log.error("error while validating a Certificate Chain",ex);
506 	            //throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[]{ex.getMessage()}, (Throwable) ex);
507 	        } catch (InvalidAlgorithmParameterException ex) {
508 	        	log.error("error while validating a Certificate Chain",ex);
509 	            //throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[]{ex.getMessage()}, (Throwable) ex);
510 	        } catch (KeyStoreException ex) {
511 	        	log.error("error while validating a Certificate Chain",ex);
512 	            //throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[]{ex.getMessage()}, (Throwable) ex);
513 	        } catch (CertPathValidatorException ex) {
514 	        	log.error("error while validating a Certificate Chain",ex);
515 	            //throw new WSSecurityException(WSSecurityException.FAILURE, "certpath", new Object[]{ex.getMessage()}, (Throwable) ex);
516 	        }
517 	        return false;
518 	  }
519 	  public static List<X509Certificate> establishCertChain( X509Certificate certificate, boolean trustCACerts)
520 	            throws Exception {
521 	    	
522 	    	KeyStore keyStore   = EPKeystoreManager.getInstance().getUserkeystore().getKeystore();
523 	    	KeyStore trustStore = EPKeystoreManager.getInstance().getTrustStore().getKeystore();
524 			  
525 	        Map<Principal, List<X509Certificate>> knownCerts = new Hashtable<Principal, List<X509Certificate>>();
526 	        if ( keyStore!=null && keyStore.size() > 0) {
527 	            knownCerts.putAll(getCertsByIssuer(keyStore));
528 	        }
529 	        
530 	        if (trustCACerts && trustStore.size() > 0) {
531 	            knownCerts.putAll(getCertsByIssuer(trustStore));
532 	        }
533 
534 	        LinkedList<X509Certificate> answer = new LinkedList<X509Certificate>();
535 	        if (buildChain(certificate, answer, knownCerts)) {
536 	            return answer;
537 	        } else {
538 	            throw new Exception("Failed to establish chain from reply");
539 	        }
540 	    }
541 
542 
543 	    /**
544 	     * Builds the certificate chain of the specified certificate based on the known list of certificates
545 	     * that were issued by their respective Principals. Returns true if the entire chain of all certificates
546 	     * was successfully built.
547 	     *
548 	     * @param certificate certificate to build its chain.
549 	     * @param answer      the certificate chain for the corresponding certificate.
550 	     * @param knownCerts  list of known certificates grouped by their issues (i.e. Principals).
551 	     * @return true if the entire chain of all certificates was successfully built.
552 	     */
553 	    private static boolean buildChain(X509Certificate certificate, LinkedList<X509Certificate> answer,
554 	                                      Map<Principal, List<X509Certificate>> knownCerts) {
555 	        Principal subject = certificate.getSubjectDN();
556 	        Principal issuer = certificate.getIssuerDN();
557 	        // Check if the certificate is a root certificate (i.e. was issued by the same Principal that
558 	        // is present in the subject)
559 	        if (subject.equals(issuer)) {
560 	            answer.addFirst(certificate);
561 	            return true;
562 	        }
563 	        // Get the list of known certificates of the certificate's issuer
564 	        List<X509Certificate> issuerCerts = knownCerts.get(issuer);
565 	        if (issuerCerts == null || issuerCerts.isEmpty()) {
566 	            // No certificates were found so building of chain failed
567 	            return false;
568 	        }
569 	        for (X509Certificate issuerCert : issuerCerts) {
570 	            PublicKey publickey = issuerCert.getPublicKey();
571 	            try {
572 	                // Verify the certificate with the specified public key
573 	                certificate.verify(publickey);
574 	                // Certificate was verified successfully so build chain of issuer's certificate
575 	                if (!buildChain(issuerCert, answer, knownCerts)) {
576 	                    return false;
577 	                }
578 	            }
579 	            catch (Exception exception) {
580 	                // Failed to verify certificate
581 	                return false;
582 	            }
583 	        }
584 	        answer.addFirst(certificate);
585 	        return true;
586 	    }
587 
588 	    /**
589 	     * Returns a Map where the key holds the certificate issuers and values the certificates of each issuer.
590 	     *
591 	     * @param ks the keystore to get its certs per issuer.
592 	     * @return a map with the certificates per issuer.
593 	     * @throws Exception
594 	     */
595 	    private static Map<Principal, List<X509Certificate>> getCertsByIssuer(KeyStore ks)
596 	            throws Exception {
597 	        Map<Principal, List<X509Certificate>> answer = new HashMap<Principal, List<X509Certificate>>();
598 	        Enumeration<String> aliases = ks.aliases();
599 	        while (aliases.hasMoreElements()) {
600 	            String alias = aliases.nextElement();
601 	            X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
602 	            cert = X509Util.getBCCertificate(cert);
603 	            if (cert != null) {
604 	                Principal subjectDN = cert.getSubjectDN();
605 	                List<X509Certificate> vec = answer.get(subjectDN);
606 	                if (vec == null) {
607 	                    vec = new ArrayList<X509Certificate>();
608 	                    vec.add(cert);
609 	                }
610 	                else {
611 	                    if (!vec.contains(cert)) {
612 	                        vec.add(cert);
613 	                    }
614 	                }
615 	                answer.put(subjectDN, vec);
616 	            }
617 	        }
618 	        return answer;
619 	    }
620 
621 	    /**
622 	     * Validates chain in certification reply, and returns the ordered
623 	     * elements of the chain (with user certificate first, and root
624 	     * certificate last in the array).
625 	     *
626 	     * @param alias the alias name
627 	     * @param userCert the user certificate of the alias
628 	     * @param replyCerts the chain provided in the reply
629 	     */
630 	    private static List<X509Certificate> validateReply(KeyStore keyStore, KeyStore trustStore, String alias,
631 	                                                             X509Certificate userCert, List<X509Certificate> replyCerts,
632 	                                                             boolean trustCACerts, boolean verifyRoot)
633 	            throws Exception {
634 	        // order the certs in the reply (bottom-up).
635 	        int i;
636 	        PublicKey userPubKey = userCert.getPublicKey();
637 	        for (i = 0; i < replyCerts.size(); i++) {
638 	            if (userPubKey.equals(replyCerts.get(i).getPublicKey())) {
639 	                break;
640 	            }
641 	        }
642 	        if (i == replyCerts.size()) {
643 	            throw new Exception(
644 	                    "Certificate reply does not contain public key for <alias>: " + alias);
645 	        }
646 
647 	        X509Certificate tmpCert = replyCerts.get(0);
648 	        replyCerts.set(0, replyCerts.get(i));
649 	        replyCerts.set(i, tmpCert);
650 	        Principal issuer = replyCerts.get(0).getIssuerDN();
651 
652 	        for (i = 1; i < replyCerts.size() - 1; i++) {
653 	            // find a cert in the reply whose "subject" is the same as the
654 	            // given "issuer"
655 	            int j;
656 	            for (j = i; j < replyCerts.size(); j++) {
657 	                Principal subject = replyCerts.get(j).getSubjectDN();
658 	                if (subject.equals(issuer)) {
659 	                    tmpCert = replyCerts.get(i);
660 	                    replyCerts.set(i, replyCerts.get(j));
661 	                    replyCerts.set(j, tmpCert);
662 	                    issuer = replyCerts.get(i).getIssuerDN();
663 	                    break;
664 	                }
665 	            }
666 	            if (j == replyCerts.size()) {
667 	                throw new Exception("Incomplete certificate chain in reply");
668 	            }
669 	        }
670 
671 	        // now verify each cert in the ordered chain
672 	        for (i = 0; i < replyCerts.size() - 1; i++) {
673 	            PublicKey pubKey = replyCerts.get(i + 1).getPublicKey();
674 	            try {
675 	                replyCerts.get(i).verify(pubKey);
676 	            }
677 	            catch (Exception e) {
678 	                throw new Exception(
679 	                        "Certificate chain in reply does not verify: " + e.getMessage());
680 	            }
681 	        }
682 
683 	        if (!verifyRoot) {
684 	            return replyCerts;
685 	        }
686 
687 	        // do we trust the (root) cert at the top?
688 	        X509Certificate topCert = replyCerts.get(replyCerts.size() - 1);
689 	        boolean foundInKeyStore = keyStore.getCertificateAlias(topCert) != null;
690 	        boolean foundInCAStore = trustCACerts && (trustStore.getCertificateAlias(topCert) != null);
691 	        if (!foundInKeyStore && !foundInCAStore) {
692 	            boolean verified = false;
693 	            X509Certificate rootCert = null;
694 	            if (trustCACerts) {
695 	                for (Enumeration<String> aliases = trustStore.aliases(); aliases.hasMoreElements();) {
696 	                    String name = aliases.nextElement();
697 	                    rootCert = (X509Certificate) trustStore.getCertificate(name);
698 	                    if (rootCert != null) {
699 	                        try {
700 	                            topCert.verify(rootCert.getPublicKey());
701 	                            verified = true;
702 	                            break;
703 	                        }
704 	                        catch (Exception e) {
705 	                            // Ignore
706 	                        }
707 	                    }
708 	                }
709 	            }
710 	            if (!verified) {
711 	                return null;
712 	            }
713 	            else {
714 	                // Check if the cert is a self-signed cert
715 	                if (!topCert.getSubjectDN().equals(topCert.getIssuerDN())) {
716 	                    // append the (self-signed) root CA cert to the chain
717 	                    replyCerts.add(rootCert);
718 	                }
719 	            }
720 	        }
721 
722 	        return replyCerts;
723 	    }
724 
725 	    /**
726 	     * Creates an X509 version3 certificate.
727 	     *
728 	     * @param kp           KeyPair that keeps the public and private keys for the new certificate.
729 	     * @param months       time to live
730 	     * @param issuerDN     Issuer string e.g "O=Grid,OU=OGSA,CN=ACME"
731 	     * @param subjectDN    Subject string e.g "O=Grid,OU=OGSA,CN=John Doe"
732 	     * @param domain       Domain of the server.
733 	     * @param signAlgoritm Signature algorithm. This can be either a name or an OID.
734 	     * @return X509 V3 Certificate
735 	     * @throws GeneralSecurityException
736 	     * @throws IOException
737 	     */
738 	    public static synchronized X509Certificate createX509V3Certificate(KeyPair kp, int months, String issuerDN,
739 	                                                                        String subjectDN, String domain,
740 	                                                                        String signAlgoritm)
741 	            throws GeneralSecurityException, IOException {
742 	        PublicKey pubKey = kp.getPublic();
743 	        PrivateKey privKey = kp.getPrivate();
744 
745 	        byte[] serno = new byte[8];
746 	        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
747 	        random.setSeed((new Date().getTime()));
748 	        random.nextBytes(serno);
749 	        BigInteger serial = (new java.math.BigInteger(serno)).abs();
750 
751 	        X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
752 	        certGenerator.reset();
753 
754 	        certGenerator.setSerialNumber(serial);
755 	        certGenerator.setIssuerDN(new X509Name(issuerDN));
756 	        certGenerator.setNotBefore(new Date(System.currentTimeMillis()));
757 	        certGenerator.setNotAfter(
758 	                new Date(System.currentTimeMillis() + months * (1000L * 60 * 60 * 24 * 30)));
759 	        certGenerator.setSubjectDN(new X509Name(subjectDN));
760 	        certGenerator.setPublicKey(pubKey);
761 	        certGenerator.setSignatureAlgorithm(signAlgoritm);
762 
763 	        // Generate the subject alternative name
764 	        boolean critical = subjectDN == null || "".equals(subjectDN.trim());
765 	        DERSequence othernameSequence = new DERSequence(new ASN1Encodable[]{
766 	                new DERObjectIdentifier("1.3.6.1.5.5.7.8.5"), new DERTaggedObject(true, 0, new DERUTF8String(domain))});
767 	        GeneralName othernameGN = new GeneralName(GeneralName.otherName, othernameSequence);
768 	        GeneralNames subjectAltNames = new GeneralNames(new DERSequence(new ASN1Encodable[]{othernameGN}));
769 	        // Add subject alternative name extension
770 	        certGenerator.addExtension(X509Extensions.SubjectAlternativeName, critical, subjectAltNames);
771 
772 	        X509Certificate cert =
773 	                certGenerator.generateX509Certificate(privKey, "BC", new SecureRandom());
774 	        cert.checkValidity(new Date());
775 	        cert.verify(pubKey);
776 
777 	        return cert;
778 	    }
779 	    
780 	    public static X509CRL getCRLFromCertCDP(X509Certificate certificate)
781 	    		throws CertificateParsingException {
782 	    	DistributionPoint[] dps = X509Util.getCrlDistributionPoint(certificate);
783 	    	X509CRL latestCRL = null;
784 	    	for (int i = 0; i < dps.length; i++) {
785 	    		  DistributionPoint dp = dps[i];
786 	    		  X509CRL crl = null;
787 	    		  crl = X509Util.loadCRLFromDP(dp);
788 	    		  if (latestCRL==null) {
789 	    			  latestCRL = crl;
790 	    		  } else if (crl !=null) {
791 	    			  if (latestCRL.getNextUpdate().before(crl.getNextUpdate()))
792 	    				  latestCRL = crl;
793 	    		  }
794 			}
795 			return latestCRL;
796 	    }
797 
798 	    public static KeyPair generateKeyPair(NewCertParams params) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException  {
799 			if (params.getKeypairAlg().equals("ECDSA"))
800 				return CertificateManager.generateECKeyPair( params.getEcDSASpecName() );
801 			else
802 				return CertificateManager.generateKeyPair( params.getKeypairAlg(), params.getKeypairSize() );
803 	    }
804 	    /**
805 	     * Returns a new public & private key with the specified algorithm (e.g. DSA, RSA, etc.).
806 	     *
807 	     * @param algorithm DSA, RSA, etc.
808 	     * @param keysize   the keysize. This is an algorithm-specific metric, such as modulus
809 	     *                  length, specified in number of bits.
810 	     * @return a new public & private key with the specified algorithm (e.g. DSA, RSA, etc.).
811 	     * @throws NoSuchAlgorithmException 
812 	     * @throws NoSuchProviderException 
813 	     * @throws GeneralSecurityException
814 	     */
815 	    public static KeyPair generateKeyPair(String algorithm, int keysize, String provider) throws NoSuchAlgorithmException, NoSuchProviderException  {
816 	        KeyPairGenerator generator;
817 	        if (provider == null) {
818 	            generator = KeyPairGenerator.getInstance(algorithm);
819 	        } else {
820 	            generator = KeyPairGenerator.getInstance(algorithm, provider);
821 	        }
822 	        generator.initialize(keysize, new SecureRandom());
823 	        return generator.generateKeyPair();
824 	    }
825 	    
826 	    public static KeyPair generateKeyPair(String algorithm, int keysize) throws NoSuchAlgorithmException, NoSuchProviderException  {
827 			//Select the Security Provider that will be used
828 	    	//Strange : don't use SunMSCAPI Provider : setkey will failed!....
829 			String prov=null;
830 	    	if ( EPKeystoreManager.isPKCS11Used() )
831 					prov = EPCryptoProviderManager.getDefaultProviderName();//getUserkeystore().getProviderName();
832 			else	prov = EPCryptoProviderManager.getDefaultProviderName();
833 
834 	        return generateKeyPair( algorithm, keysize, prov );
835 	    }
836 	    public static KeyPair generateECKeyPair(String ecspecs) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException   {
837 
838 		    ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec(ecspecs);
839 
840 		    KeyPairGenerator generator = KeyPairGenerator.getInstance("ECDSA", "BC");
841 
842 			generator.initialize(ecSpec, new SecureRandom());
843 
844 	        return generator.generateKeyPair();
845 	    }
846 	    /** Generate Random Serial Number
847 	     * if it's windows and Java 1.6 min, then use CAPI (Windows-PRNG)
848 	     * Otherwise, use SHA1PRNG.
849 	     * 
850 	     * @return
851 	     */
852 	    private static BigInteger generateRandomSerial() throws NoSuchAlgorithmException {
853 	        byte[] serno = new byte[8];
854 	        SecureRandom random;
855 			try {
856 				if (JVMSettings.isJava16Min() && JVMSettings.isWindowsOS())
857 					random = SecureRandom.getInstance("Windows-PRNG");
858 				else 
859 					random = SecureRandom.getInstance("SHA1PRNG");
860 			} catch (NoSuchAlgorithmException e) {
861 				String msg = "Error while generating a random serial number : " + e.getMessage();
862 				EParapherManager.getInstance().getUI().errorMessage(msg,e);
863 				log.error(msg, e);
864 				return null;
865 			}
866 	        random.setSeed( System.currentTimeMillis() );
867 	        random.nextBytes(serno);
868 			return new BigInteger(serno).abs();
869 	    }
870 		/**
871 		 * Check the certificate with CA certificate.
872 		 *
873 		 * @param certificate cert to verify
874 		 * @param caCertPath collection of X509Certificate
875 		 * @return true if verified OK, false if not
876 		 */
877 		public static boolean verify(X509Certificate certificate, Collection<X509Certificate[]> caCertPath)
878 					throws Exception {
879 			try {
880 				// Create CertPath
881 				ArrayList<X509Certificate> certlist = new ArrayList<X509Certificate>();
882 				certlist.add(certificate);
883 				
884 				// Add other certs...
885 				CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
886 				java.security.cert.CertPath cp = cf.generateCertPath(certlist);
887 
888 				// Create TrustAnchor. Since BouncyCastle provider is used, we assume
889 				//  certificate already in correct order
890 				X509Certificate[] cac = (X509Certificate[]) caCertPath.toArray(new X509Certificate[] {});
891 				java.security.cert.TrustAnchor anchor = new java.security.cert.TrustAnchor(cac[0], null);
892 				// Set the PKIX parameters
893 				java.security.cert.PKIXParameters params = new java.security.cert.PKIXParameters(java.util.Collections.singleton(anchor));
894 				params.setRevocationEnabled(true);
895 				params.setDate( new Date() );
896 				
897 				CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC");
898 				PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) cpv.validate(cp, params);
899 				log.info("Certificate verify result: " + result.toString());
900 			} catch (java.security.cert.CertPathValidatorException cpve) {
901 				throw new Exception("Invalid certificate or certificate not issued by specified CA: " + cpve.getMessage());
902 			} catch (Exception e) {
903 				throw new Exception("Error checking certificate chain: " + e.getMessage());
904 			}
905 			return true;
906 		}
907 		
908 		public static X509Certificate[] buildChain(X509Certificate[] certs) {
909 	        try {
910 	        	if (Logger.getRootLogger().getLevel().toInt()<=Level.DEBUG_INT)
911 	        		System.setProperty("java.security.debug","certpath");
912 	            CertPathBuilder certPathBuilder = CertPathBuilder.getInstance("PKIX","BC");
913 	            X509CertSelector targetConstraints = new X509CertSelector();
914 
915 	            KeyStore trustedKS = EPKeystoreManager.getInstance().getTrustStore().getKeystore();
916 	            if (trustedKS == null) {
917 	                log.warn("trusted KeyStore is null");
918 	                return null;
919 	            }
920 
921 	            PKIXBuilderParameters params = new PKIXBuilderParameters( trustedKS, targetConstraints);
922 	            
923 	            //Build the certificate
924 	            ArrayList<X509Certificate> certsList = new ArrayList<X509Certificate>();
925 	            for (int i=0; i < certs.length; i++) {
926 	                certsList.add(certs[i]);
927 	            }
928 	            CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(certsList);
929 	            CertStore store = CertStore.getInstance("Collection", ccsp);
930 	            params.addCertStore(store);
931 	            params.setRevocationEnabled(false);
932 	            
933 	            CertPath certPath = certPathBuilder.build(params).getCertPath();
934 	            log.info("Cert Path building OK : " + certPath);
935 	            List<Certificate> trustedChain = (List<Certificate>) certPath.getCertificates();
936 	            
937 	            Certificate[] cerchain = (Certificate[]) trustedChain.toArray(new Certificate[] {} );
938 	            
939 	            System.setProperty("java.security.debug","false");
940 	            return X509Util.convertCertChaintoX509( cerchain );
941 	        } catch (NoSuchAlgorithmException e) {
942 	            log.error(e.getLocalizedMessage(),e);
943 	        } catch (KeyStoreException e) {
944 	            log.error(e.getLocalizedMessage(),e);
945 	        } catch (CertPathBuilderException e) {
946 	            //if (log.getLevel().toInt()<=Level.DEBUG_INT)
947 	            	 log.debug(e.getLocalizedMessage(),e);
948 	            //else
949 	            	 log.warn(e.getLocalizedMessage());
950 	        } catch (InvalidAlgorithmParameterException e) {
951 	            log.error(e.getLocalizedMessage(),e);
952 	        } catch (NoSuchProviderException e) {
953 	            log.error(e.getLocalizedMessage(),e);
954 			}
955             System.setProperty("java.security.debug","false");
956 	        return null;
957 		}
958 }