Java tutorial
/* * Copyright 2010 dorkbox, llc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dorkbox.util.crypto; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Writer; import java.lang.reflect.Method; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.Provider; import java.security.PublicKey; import java.security.Security; import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.DSAParams; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Date; import java.util.Enumeration; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.BERSet; import org.bouncycastle.asn1.DERBMPString; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; import org.bouncycastle.asn1.cms.SignedData; import org.bouncycastle.asn1.cms.SignerIdentifier; import org.bouncycastle.asn1.cms.SignerInfo; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RSAPublicKey; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.DSAParameter; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.CMSTypedData; import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.DSAParameters; import org.bouncycastle.crypto.params.DSAPrivateKeyParameters; import org.bouncycastle.crypto.params.DSAPublicKeyParameters; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; import org.bouncycastle.jcajce.provider.asymmetric.dsa.BCDSAPublicKey; import org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil; import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey; import org.bouncycastle.jcajce.provider.asymmetric.rsa.RSAUtil; import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory; import org.bouncycastle.jce.PrincipalUtil; import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.JCEECPublicKey; import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.ContentVerifierProvider; import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.bc.BcContentSignerBuilder; import org.bouncycastle.operator.bc.BcDSAContentSignerBuilder; import org.bouncycastle.operator.bc.BcDSAContentVerifierProviderBuilder; import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; import org.bouncycastle.operator.bc.BcRSAContentVerifierProviderBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import dorkbox.util.Base64Fast; import dorkbox.util.crypto.signers.BcECDSAContentSignerBuilder; import dorkbox.util.crypto.signers.BcECDSAContentVerifierProviderBuilder; public class CryptoX509 { private static final Logger logger = LoggerFactory.getLogger(CryptoX509.class); public static void addProvider() { // make sure we only add it once (in case it's added elsewhere...) Provider provider = Security.getProvider("BC"); if (provider == null) { Security.addProvider(new BouncyCastleProvider()); } } public static class Util { /** * @return true if saving the x509 certificate to a PEM format file was successful */ public static boolean convertToPemFile(X509Certificate x509cert, String fileName) { boolean failed = false; Writer output = null; try { String lineSeparator = "\r\n"; String cert_begin = "-----BEGIN CERTIFICATE-----"; String cert_end = "-----END CERTIFICATE-----"; byte[] derCert = x509cert.getEncoded(); char[] encodeToChar = Base64Fast.encodeToChar(derCert, false); int newLineCount = encodeToChar.length / 64; int length = encodeToChar.length; output = new BufferedWriter(new FileWriter("dorkbox.crt", false), cert_begin.length() + cert_end.length() + length + newLineCount + 3); output.write(cert_begin); output.write(lineSeparator); int copyCount = 64; for (int i = 0; i < length; i += 64) { if (i + 64 > length) { copyCount = length - i; } output.write(encodeToChar, i, copyCount); output.write(lineSeparator); } output.write(cert_end); output.write(lineSeparator); } catch (Exception e) { logger.error("Error during conversion.", e); failed = true; } finally { if (output != null) { try { output.close(); } catch (IOException e) { logger.error("Error closing resource.", e); } } } return !failed; } public static String convertToPem(X509Certificate x509cert) throws CertificateEncodingException { String lineSeparator = "\r\n"; String cert_begin = "-----BEGIN CERTIFICATE-----"; String cert_end = "-----END CERTIFICATE-----"; byte[] derCert = x509cert.getEncoded(); char[] encodeToChar = Base64Fast.encodeToChar(derCert, false); int newLineCount = encodeToChar.length / 64; int length = encodeToChar.length; int lastIndex = 0; StringBuilder sb = new StringBuilder( cert_begin.length() + cert_end.length() + length + newLineCount + 2); sb.append(cert_begin); sb.append(lineSeparator); for (int i = 64; i < length; i += 64) { sb.append(encodeToChar, lastIndex, i); sb.append(lineSeparator); lastIndex = i; } sb.append(cert_end); return sb.toString(); } public static String getDigestNameFromCert(X509CertificateHolder x509CertificateHolder) { return Util.getDigestNameFromSigAlgId(x509CertificateHolder.getSignatureAlgorithm().getAlgorithm()); } public static String getDigestNameFromSigAlgId(ASN1ObjectIdentifier algorithm) { String digest = null; try { // have to use reflection in order to access the DIGEST method used by the key. DefaultCMSSignatureAlgorithmNameGenerator defaultCMSSignatureAlgorithmNameGenerator = new DefaultCMSSignatureAlgorithmNameGenerator(); Method declaredMethod = DefaultCMSSignatureAlgorithmNameGenerator.class .getDeclaredMethod("getDigestAlgName", ASN1ObjectIdentifier.class); declaredMethod.setAccessible(true); digest = (String) declaredMethod.invoke(defaultCMSSignatureAlgorithmNameGenerator, algorithm); } catch (Throwable t) { throw new RuntimeException("Weird error using reflection to get the digest name: " + algorithm.getId() + t.getMessage()); } if (algorithm.getId().equals(digest)) { throw new RuntimeException("Unable to get digest name from algorithm ID: " + algorithm.getId()); } return digest; } // @SuppressWarnings("rawtypes") // public static void verify(JarFile jf, X509Certificate[] trustedCaCerts) throws IOException, CertificateException { // Vector<JarEntry> entriesVec = new Vector<JarEntry>(); // // // Ensure there is a manifest file // Manifest man = jf.getManifest(); // if (man == null) { // throw new SecurityException("The JAR is not signed"); // } // // // Ensure all the entries' signatures verify correctly // byte[] buffer = new byte[8192]; // Enumeration entries = jf.entries(); // // while (entries.hasMoreElements()) { // JarEntry je = (JarEntry) entries.nextElement(); // entriesVec.addElement(je); // InputStream is = jf.getInputStream(je); // @SuppressWarnings("unused") // int n; // while ((n = is.read(buffer, 0, buffer.length)) != -1) { // // we just read. this will throw a SecurityException // // if a signature/digest check fails. // } // is.close(); // } // jf.close(); // // // Get the list of signer certificates // Enumeration e = entriesVec.elements(); // while (e.hasMoreElements()) { // JarEntry je = (JarEntry) e.nextElement(); // // if (je.isDirectory()) { // continue; // } // // Every file must be signed - except // // files in META-INF // Certificate[] certs = je.getCertificates(); // if (certs == null || certs.length == 0) { // if (!je.getName().startsWith("META-INF")) { // throw new SecurityException("The JCE framework has unsigned class files."); // } // } else { // // Check whether the file // // is signed as expected. // // The framework may be signed by // // multiple signers. At least one of // // the signers must be a trusted signer. // // // First, determine the roots of the certificate chains // X509Certificate[] chainRoots = getChainRoots(certs); // boolean signedAsExpected = false; // // for (int i = 0; i < chainRoots.length; i++) { // if (isTrusted(chainRoots[i], trustedCaCerts)) { // signedAsExpected = true; // break; // } // } // // if (!signedAsExpected) { // throw new SecurityException("The JAR is not signed by a trusted signer"); // } // } // } // } public static boolean isTrusted(X509Certificate cert, X509Certificate[] trustedCaCerts) { // Return true iff either of the following is true: // 1) the cert is in the trustedCaCerts. // 2) the cert is issued by a trusted CA. // Check whether the cert is in the trustedCaCerts for (int i = 0; i < trustedCaCerts.length; i++) { // If the cert has the same SubjectDN // as a trusted CA, check whether // the two certs are the same. if (cert.getSubjectDN().equals(trustedCaCerts[i].getSubjectDN())) { if (cert.equals(trustedCaCerts[i])) { return true; } } } // Check whether the cert is issued by a trusted CA. // Signature verification is expensive. So we check // whether the cert is issued // by one of the trusted CAs if the above loop failed. for (int i = 0; i < trustedCaCerts.length; i++) { // If the issuer of the cert has the same name as // a trusted CA, check whether that trusted CA // actually issued the cert. if (cert.getIssuerDN().equals(trustedCaCerts[i].getSubjectDN())) { try { cert.verify(trustedCaCerts[i].getPublicKey()); return true; } catch (Exception e) { // Do nothing. } } } return false; } // public static X509Certificate[] getChainRoots(Certificate[] certs) { // Vector<X509Certificate> result = new Vector<X509Certificate>(3); // // choose a Vector size that seems reasonable // for (int i = 0; i < certs.length - 1; i++) { // if (!((X509Certificate) certs[i + 1]).getSubjectDN().equals( // ((X509Certificate) certs[i]).getIssuerDN())) { // // We've reached the end of a chain // result.addElement((X509Certificate) certs[i]); // } // } // // // The final entry in the certs array is always // // a "root" certificate // result.addElement((X509Certificate) certs[certs.length - 1]); // X509Certificate[] ret = new X509Certificate[result.size()]; // result.copyInto(ret); // // return ret; // } } public static class DSA { static { addProvider(); } /** * Creates a X509 certificate holder object. <p> * * Look at BCStyle for a list of all valid X500 Names. */ public static X509CertificateHolder createCertHolder(Date startDate, Date expiryDate, X500Name issuerName, X500Name subjectName, BigInteger serialNumber, DSAPrivateKeyParameters privateKey, DSAPublicKeyParameters publicKey) { String signatureAlgorithm = "SHA1withDSA"; AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm); AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); SubjectPublicKeyInfo subjectPublicKeyInfo; DSAParameters parameters = publicKey.getParameters(); try { byte[] encoded = new SubjectPublicKeyInfo( new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(parameters.getP(), parameters.getQ(), parameters.getG())), new ASN1Integer(publicKey.getY())).getEncoded(ASN1Encoding.DER); ASN1Sequence seq = (ASN1Sequence) ASN1Primitive.fromByteArray(encoded); subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq); } catch (IOException e) { logger.error("Error during DSA.", e); return null; } X509v3CertificateBuilder v3CertBuilder = new X509v3CertificateBuilder(issuerName, serialNumber, startDate, expiryDate, subjectName, subjectPublicKeyInfo); BcDSAContentSignerBuilder contentSignerBuilder = new BcDSAContentSignerBuilder(sigAlgId, digAlgId); ContentSigner build; try { build = contentSignerBuilder.build(privateKey); } catch (OperatorCreationException e) { logger.error("Error creating certificate.", e); return null; } return v3CertBuilder.build(build); } /** * Verifies that the certificate is legitimate. * <p> * MUST have BouncyCastle provider loaded by the security manager! * <p> * @return true if it was a valid cert. */ public static boolean validate(X509CertificateHolder x509CertificateHolder) { try { // this is unique in that it verifies that the certificate is a LEGIT certificate, but not necessarily // valid during this time period. ContentVerifierProvider contentVerifierProvider = new BcDSAContentVerifierProviderBuilder( new DefaultDigestAlgorithmIdentifierFinder()).build(x509CertificateHolder); boolean signatureValid = x509CertificateHolder.isSignatureValid(contentVerifierProvider); if (!signatureValid) { return false; } CertificateFactory certificateFactory = new CertificateFactory(); java.security.cert.Certificate certificate = certificateFactory .engineGenerateCertificate(new ByteArrayInputStream(x509CertificateHolder.getEncoded())); // Note: this requires the BC provider to be loaded! if (certificate == null || certificate.getPublicKey() == null) { return false; } // TODO: when validating the certificate, it is important to use a date from somewhere other than the host computer! (maybe use google? or something...) // this will validate the DATES of the certificate, to make sure the cert is valid during the correct time period. // Verify the TIME/DATE of the certificate ((X509Certificate) certificate).checkValidity(new Date()); // if we get here, it means that our cert is LEGIT and VALID. return true; } catch (Throwable t) { throw new RuntimeException(t); } } /** * Verifies the given x509 based signature against the OPTIONAL original public key. If not specified, then * the public key from the signature is used. * <p> * MUST have BouncyCastle provider loaded by the security manager! * <p> * @return true if the signature was valid. */ public static boolean verifySignature(byte[] signatureBytes, DSAPublicKeyParameters optionalOriginalPublicKey) { ASN1InputStream asn1InputStream = null; try { asn1InputStream = new ASN1InputStream(new ByteArrayInputStream(signatureBytes)); ASN1Primitive signatureASN = asn1InputStream.readObject(); ASN1Sequence seq = ASN1Sequence.getInstance(signatureASN); ASN1TaggedObject tagged = (ASN1TaggedObject) seq.getObjectAt(1); // Extract certificates SignedData newSignedData = SignedData.getInstance(tagged.getObject()); @SuppressWarnings("rawtypes") Enumeration newSigObjects = newSignedData.getCertificates().getObjects(); Object newSigElement = newSigObjects.nextElement(); if (newSigElement instanceof DERSequence) { DERSequence newSigDERElement = (DERSequence) newSigElement; InputStream newSigIn = new ByteArrayInputStream(newSigDERElement.getEncoded()); org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory certificateFactory = new org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory(); java.security.cert.Certificate engineGenerateCert = certificateFactory .engineGenerateCertificate(newSigIn); BCDSAPublicKey publicKey2 = (BCDSAPublicKey) engineGenerateCert.getPublicKey(); if (optionalOriginalPublicKey != null) { DSAParams params = publicKey2.getParams(); DSAParameters parameters = optionalOriginalPublicKey.getParameters(); if (!publicKey2.getY().equals(optionalOriginalPublicKey.getY()) || !params.getP().equals(parameters.getP()) || !params.getQ().equals(parameters.getQ()) || !params.getG().equals(parameters.getG())) { return false; } } engineGenerateCert.verify(publicKey2); } } catch (Throwable t) { return false; } finally { if (asn1InputStream != null) { try { asn1InputStream.close(); } catch (IOException e) { logger.error("Error closing stream during DSA.", e); } } } return true; } } @SuppressWarnings("unused") public static class RSA { static { addProvider(); } // public static class CertificateAuthority { // public static X509Certificate generateCert(KeyFactory factory, Date startDate, Date expiryDate, // String issuer, String subject, String friendlyName, // RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey) throws InvalidKeySpecException, IOException, InvalidKeyException, OperatorCreationException { // // return CryptoX509.RSA.generateCert(factory, startDate, expiryDate, new X500Name(issuer), new X500Name(subject), friendlyName, publicKey, privateKey, null); // } // // public static X509Certificate generateCert(KeyFactory factory, Date startDate, Date expiryDate, // X509Principal issuer, String subject, String friendlyName, // RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey) throws InvalidKeySpecException, InvalidKeyException, IOException, OperatorCreationException { // // return CryptoX509.RSA.generateCert(factory, startDate, expiryDate, X500Name.getInstance(issuer), new X500Name(subject), friendlyName, publicKey, privateKey, null); // } // } // // // public static class IntermediateAuthority { // public static X509Certificate generateCert(KeyFactory factory, Date startDate, Date expiryDate, // String issuer, String subject, String friendlyName, // RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey, // X509Certificate caCertificate) throws InvalidKeySpecException, IOException, InvalidKeyException, OperatorCreationException { // // return CryptoX509.RSA.generateCert(factory, startDate, expiryDate, new X500Name(issuer), new X500Name(subject), friendlyName, publicKey, privateKey, caCertificate); // } // // public static X509Certificate generateCert(KeyFactory factory, Date startDate, Date expiryDate, // X509Principal issuer, String subject, String friendlyName, // RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey, // X509Certificate caCertificate) throws InvalidKeySpecException, InvalidKeyException, IOException, OperatorCreationException { // // return CryptoX509.RSA.generateCert(factory, startDate, expiryDate, X500Name.getInstance(issuer), new X500Name(subject), friendlyName, publicKey, privateKey, caCertificate); // } // } // public static class CertificateAuthrority { public static X509Certificate generateCert(KeyFactory factory, Date startDate, Date endDate, String subject, String friendlyName, RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey) { String signatureAlgorithm = "SHA1withRSA"; AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder() .find(signatureAlgorithm); // specify it's RSA AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); // specify SHA try { // JCE format needed for the certificate - because getEncoded() is necessary... PublicKey jcePublicKey = convertToJCE(factory, publicKey); // PrivateKey jcePrivateKey = convertToJCE(factory, publicKey, privateKey); SubjectPublicKeyInfo subjectPublicKeyInfo = createSubjectPublicKey(jcePublicKey); X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(new X500Name(subject), BigInteger.valueOf(System.currentTimeMillis()), startDate, endDate, new X500Name(subject), subjectPublicKeyInfo); // // extensions // JcaX509ExtensionUtils jcaX509ExtensionUtils = new JcaX509ExtensionUtils(); // SHA1 SubjectKeyIdentifier createSubjectKeyIdentifier = jcaX509ExtensionUtils .createSubjectKeyIdentifier(subjectPublicKeyInfo); certBuilder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier); certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(1)); ContentSigner hashSigner = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKey); X509CertificateHolder certHolder = certBuilder.build(hashSigner); java.security.cert.Certificate certificate = new CertificateFactory() .engineGenerateCertificate(new ByteArrayInputStream(certHolder.getEncoded())); if (!(certificate instanceof X509Certificate)) { logger.error("Error generating certificate, it's the wrong type."); return null; } certificate.verify(jcePublicKey); if (certificate instanceof PKCS12BagAttributeCarrier) { PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) certificate; // // this is actually optional - but if you want to have control // over setting the friendly name this is the way to do it... // bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(friendlyName)); } return (X509Certificate) certificate; } catch (Exception e) { logger.error("Error generating certificate.", e); return null; } } } public static class SelfSigned { public static X509Certificate generateCert(KeyFactory factory, Date startDate, Date endDate, String subject, String friendlyName, RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey) { String signatureAlgorithm = "SHA1withRSA"; AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder() .find(signatureAlgorithm); // specify it's RSA AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); // specify SHA try { // JCE format needed for the certificate - because getEncoded() is necessary... PublicKey jcePublicKey = convertToJCE(factory, publicKey); // PrivateKey jcePrivateKey = convertToJCE(factory, publicKey, privateKey); SubjectPublicKeyInfo subjectPublicKeyInfo = createSubjectPublicKey(jcePublicKey); X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(new X500Name(subject), BigInteger.valueOf(System.currentTimeMillis()), startDate, endDate, new X500Name(subject), subjectPublicKeyInfo); // // extensions // JcaX509ExtensionUtils jcaX509ExtensionUtils = new JcaX509ExtensionUtils(); // SHA1 SubjectKeyIdentifier createSubjectKeyIdentifier = jcaX509ExtensionUtils .createSubjectKeyIdentifier(subjectPublicKeyInfo); certBuilder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier); certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); ContentSigner hashSigner = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKey); X509CertificateHolder certHolder = certBuilder.build(hashSigner); java.security.cert.Certificate certificate = new CertificateFactory() .engineGenerateCertificate(new ByteArrayInputStream(certHolder.getEncoded())); if (!(certificate instanceof X509Certificate)) { logger.error("Error generating certificate, it's the wrong type."); return null; } certificate.verify(jcePublicKey); if (certificate instanceof PKCS12BagAttributeCarrier) { PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) certificate; // // this is actually optional - but if you want to have control // over setting the friendly name this is the way to do it... // bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(friendlyName)); } return (X509Certificate) certificate; } catch (Exception e) { logger.error("Error generating certificate.", e); return null; } } } /** * Generate a cert that is signed by a CA cert. */ public static X509Certificate generateCert(KeyFactory factory, Date startDate, Date expiryDate, X509Certificate issuerCert, String subject, String friendlyName, RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters signingCaKey) throws InvalidKeySpecException, InvalidKeyException, IOException, OperatorCreationException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException { return CryptoX509.RSA.generateCert(factory, startDate, expiryDate, X500Name.getInstance(PrincipalUtil.getSubjectX509Principal(issuerCert)), new X500Name(subject), friendlyName, publicKey, issuerCert, signingCaKey); } /** * Generate a cert that is self signed. */ public static X509Certificate generateCert(KeyFactory factory, Date startDate, Date expiryDate, String subject, String friendlyName, RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey) throws InvalidKeySpecException, InvalidKeyException, IOException, OperatorCreationException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException { return CryptoX509.RSA.generateCert(factory, startDate, expiryDate, new X500Name(subject), new X500Name(subject), friendlyName, publicKey, null, privateKey); } private static X509Certificate generateCert(KeyFactory factory, Date startDate, Date expiryDate, X500Name issuer, X500Name subject, String friendlyName, RSAKeyParameters certPublicKey, X509Certificate signingCertificate, RSAPrivateCrtKeyParameters signingPrivateKey) throws InvalidKeySpecException, IOException, InvalidKeyException, OperatorCreationException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException { String signatureAlgorithm = "SHA1withRSA"; AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm); // specify it's RSA AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); // specify SHA // JCE format needed for the certificate - because getEncoded() is necessary... PublicKey jcePublicKey = convertToJCE(factory, certPublicKey); // PrivateKey jcePrivateKey = convertToJCE(factory, publicKey, privateKey); SubjectPublicKeyInfo subjectPublicKeyInfo = createSubjectPublicKey(jcePublicKey); X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(issuer, BigInteger.valueOf(System.currentTimeMillis()), startDate, expiryDate, subject, subjectPublicKeyInfo); // // extensions // JcaX509ExtensionUtils jcaX509ExtensionUtils = new JcaX509ExtensionUtils(); // SHA1 SubjectKeyIdentifier createSubjectKeyIdentifier = jcaX509ExtensionUtils .createSubjectKeyIdentifier(subjectPublicKeyInfo); certBuilder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier); if (signingCertificate != null) { AuthorityKeyIdentifier createAuthorityKeyIdentifier = jcaX509ExtensionUtils .createAuthorityKeyIdentifier(signingCertificate.getPublicKey()); certBuilder.addExtension(Extension.authorityKeyIdentifier, false, createAuthorityKeyIdentifier); // new AuthorityKeyIdentifierStructure(signingCertificate)); } certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(signingPrivateKey); X509CertificateHolder certHolder = certBuilder.build(signer); java.security.cert.Certificate certificate = new CertificateFactory() .engineGenerateCertificate(new ByteArrayInputStream(certHolder.getEncoded())); if (!(certificate instanceof X509Certificate)) { logger.error("Error generating certificate, it's the wrong type."); return null; } if (signingCertificate != null) { certificate.verify(signingCertificate.getPublicKey()); } else { certificate.verify(jcePublicKey); } if (certificate instanceof PKCS12BagAttributeCarrier) { PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) certificate; // // this is actually optional - but if you want to have control // over setting the friendly name this is the way to do it... // bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(friendlyName)); if (signingCertificate != null) { bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, subjectPublicKeyInfo); } } return (X509Certificate) certificate; // //// subject name table. // //Hashtable<ASN1ObjectIdentifier, String> attrs = new Hashtable<ASN1ObjectIdentifier, String>(); // //Vector<ASN1ObjectIdentifier> order = new Vector<ASN1ObjectIdentifier>(); // // // //attrs.put(BCStyle.C, "US"); // //attrs.put(BCStyle.O, "Dorkbox"); // //attrs.put(BCStyle.OU, "Dorkbox Certificate Authority"); // //attrs.put(BCStyle.EmailAddress, "admin@dorkbox.com"); // // // //order.addElement(BCStyle.C); // //order.addElement(BCStyle.O); // //order.addElement(BCStyle.OU); // //order.addElement(BCStyle.EmailAddress); // // // //X509Principal issuer = new X509Principal(order, attrs); // // MASTER CERT // // //// signers name // //String issuer = "C=US, O=dorkbox llc, OU=Dorkbox Certificate Authority"; // // // //// subjects name - the same as we are self signed. // //String subject = "C=US, O=dorkbox llc, OU=Dorkbox Certificate Authority"; } private static SubjectPublicKeyInfo createSubjectPublicKey(PublicKey jcePublicKey) throws IOException { ASN1InputStream asn1InputStream = null; try { asn1InputStream = new ASN1InputStream(new ByteArrayInputStream(jcePublicKey.getEncoded())); return SubjectPublicKeyInfo.getInstance(asn1InputStream.readObject()); } finally { if (asn1InputStream != null) { asn1InputStream.close(); } } } public static PublicKey convertToJCE(RSAKeyParameters publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return convertToJCE(keyFactory, publicKey); } public static PublicKey convertToJCE(KeyFactory keyFactory, RSAKeyParameters publicKey) throws InvalidKeySpecException { return keyFactory.generatePublic(new RSAPublicKeySpec(publicKey.getModulus(), publicKey.getExponent())); } public static RSAKeyParameters convertToBC(PublicKey publicKey) { RSAPublicKey pubKey = RSAPublicKey.getInstance(publicKey); return new RSAKeyParameters(false, pubKey.getModulus(), pubKey.getPublicExponent()); } public static PrivateKey convertToJCE(RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey) throws InvalidKeySpecException, NoSuchAlgorithmException { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return convertToJCE(keyFactory, publicKey, privateKey); } public static PrivateKey convertToJCE(KeyFactory keyFactory, RSAKeyParameters publicKey, RSAPrivateCrtKeyParameters privateKey) throws InvalidKeySpecException { return keyFactory.generatePrivate(new RSAPrivateCrtKeySpec(publicKey.getModulus(), publicKey.getExponent(), privateKey.getExponent(), privateKey.getP(), privateKey.getQ(), privateKey.getDP(), privateKey.getDQ(), privateKey.getQInv())); } /** * Creates a X509 certificate holder object. <p> * * Look at BCStyle for a list of all valid X500 Names. */ public static X509CertificateHolder createCertHolder(Date startDate, Date expiryDate, X500Name issuerName, X500Name subjectName, BigInteger serialNumber, RSAPrivateCrtKeyParameters privateKey, RSAKeyParameters publicKey) { String signatureAlgorithm = "SHA256withRSA"; AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm); AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); SubjectPublicKeyInfo subjectPublicKeyInfo; try { // JCE format needed for the certificate - because getEncoded() is necessary... PublicKey jcePublicKey = convertToJCE(publicKey); // PrivateKey jcePrivateKey = convertToJCE(factory, publicKey, privateKey); subjectPublicKeyInfo = createSubjectPublicKey(jcePublicKey); } catch (Exception e) { logger.error("Unable to create RSA keyA.", e); return null; } try { X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(issuerName, serialNumber, startDate, expiryDate, subjectName, subjectPublicKeyInfo); // // extensions // JcaX509ExtensionUtils jcaX509ExtensionUtils = new JcaX509ExtensionUtils(); // SHA1 SubjectKeyIdentifier createSubjectKeyIdentifier = jcaX509ExtensionUtils .createSubjectKeyIdentifier(subjectPublicKeyInfo); certBuilder.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier); certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); ContentSigner hashSigner = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKey); return certBuilder.build(hashSigner); } catch (Exception e) { logger.error("Error generating certificate.", e); return null; } } /** * Verifies that the certificate is legitimate. * <p> * MUST have BouncyCastle provider loaded by the security manager! * <p> * @return true if it was a valid cert. */ public static boolean validate(X509CertificateHolder x509CertificateHolder) { try { // this is unique in that it verifies that the certificate is a LEGIT certificate, but not necessarily // valid during this time period. ContentVerifierProvider contentVerifierProvider = new BcRSAContentVerifierProviderBuilder( new DefaultDigestAlgorithmIdentifierFinder()).build(x509CertificateHolder); boolean signatureValid = x509CertificateHolder.isSignatureValid(contentVerifierProvider); if (!signatureValid) { return false; } java.security.cert.Certificate certificate = new CertificateFactory() .engineGenerateCertificate(new ByteArrayInputStream(x509CertificateHolder.getEncoded())); // Note: this requires the BC provider to be loaded! if (certificate == null || certificate.getPublicKey() == null) { return false; } if (!(certificate instanceof X509Certificate)) { return false; } // TODO: when validating the certificate, it is important to use a date from somewhere other than the host computer! (maybe use google? or something...) // this will validate the DATES of the certificate, to make sure the cert is valid during the correct time period. // Verify the TIME/DATE of the certificate ((X509Certificate) certificate).checkValidity(new Date()); // if we get here, it means that our cert is LEGIT and VALID. return true; } catch (Throwable t) { logger.error("Error validating certificate.", t); return false; } } /** * Verifies the given x509 based signature against the OPTIONAL original public key. If not specified, then * the public key from the signature is used. * <p> * MUST have BouncyCastle provider loaded by the security manager! * <p> * @return true if the signature was valid. */ public static boolean verifySignature(byte[] signatureBytes, RSAKeyParameters publicKey) { ASN1InputStream asn1InputStream = null; try { asn1InputStream = new ASN1InputStream(new ByteArrayInputStream(signatureBytes)); ASN1Primitive signatureASN = asn1InputStream.readObject(); ASN1Sequence seq = ASN1Sequence.getInstance(signatureASN); ASN1TaggedObject tagged = (ASN1TaggedObject) seq.getObjectAt(1); // Extract certificates SignedData newSignedData = SignedData.getInstance(tagged.getObject()); @SuppressWarnings("rawtypes") Enumeration newSigOjects = newSignedData.getCertificates().getObjects(); Object newSigElement = newSigOjects.nextElement(); if (newSigElement instanceof DERSequence) { DERSequence newSigDERElement = (DERSequence) newSigElement; InputStream newSigIn = new ByteArrayInputStream(newSigDERElement.getEncoded()); org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory certFactory = new org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory(); java.security.cert.Certificate certificate = certFactory.engineGenerateCertificate(newSigIn); BCRSAPublicKey publicKey2 = (BCRSAPublicKey) certificate.getPublicKey(); if (publicKey != null) { if (!publicKey.getModulus().equals(publicKey2.getModulus()) || !publicKey.getExponent().equals(publicKey2.getPublicExponent())) { return false; } } certificate.verify(publicKey2); } return true; } catch (Throwable t) { logger.error("Error validating certificate.", t); return false; } finally { if (asn1InputStream != null) { try { asn1InputStream.close(); } catch (IOException e) { logger.error("Error closing stream during RSA.", e); } } } } } public static class ECDSA { static { // make sure we only add it once (in case it's added elsewhere...) Provider provider = Security.getProvider("BC"); if (provider == null) { Security.addProvider(new BouncyCastleProvider()); } } /** * Creates a X509 certificate holder object. */ public static X509CertificateHolder createCertHolder(String digestName, Date startDate, Date expiryDate, X500Name issuerName, X500Name subjectName, BigInteger serialNumber, ECPrivateKeyParameters privateKey, ECPublicKeyParameters publicKey) { String signatureAlgorithm = digestName + "withECDSA"; // we WANT the ECparameterSpec to be null, so it's created from the public key JCEECPublicKey pubKey = new JCEECPublicKey("EC", publicKey, (ECParameterSpec) null); AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm); AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); SubjectPublicKeyInfo subjectPublicKeyInfo; try { byte[] encoded = pubKey.getEncoded(); ASN1Sequence seq = (ASN1Sequence) ASN1Primitive.fromByteArray(encoded); subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(seq); } catch (IOException e) { logger.error("Unable to perform DSA.", e); return null; } X509v3CertificateBuilder v3CertBuilder = new X509v3CertificateBuilder(issuerName, serialNumber, startDate, expiryDate, subjectName, subjectPublicKeyInfo); BcECDSAContentSignerBuilder contentSignerBuilder = new BcECDSAContentSignerBuilder(sigAlgId, digAlgId); ContentSigner build; try { build = contentSignerBuilder.build(privateKey); } catch (OperatorCreationException e) { logger.error("Error creating certificate.", e); return null; } return v3CertBuilder.build(build); } /** * Verifies that the certificate is legitimate. * <p> * MUST have BouncyCastle provider loaded by the security manager! * <p> * @return true if it was a valid cert. */ public static boolean validate(X509CertificateHolder x509CertificateHolder) { try { // this is unique in that it verifies that the certificate is a LEGIT certificate, but not necessarily // valid during this time period. ContentVerifierProvider contentVerifierProvider = new BcECDSAContentVerifierProviderBuilder( new DefaultDigestAlgorithmIdentifierFinder()).build(x509CertificateHolder); boolean signatureValid = x509CertificateHolder.isSignatureValid(contentVerifierProvider); if (!signatureValid) { return false; } CertificateFactory certFactory = new CertificateFactory(); java.security.cert.Certificate certificate = certFactory .engineGenerateCertificate(new ByteArrayInputStream(x509CertificateHolder.getEncoded())); // Note: this requires the BC provider to be loaded! if (certificate == null || certificate.getPublicKey() == null) { return false; } // TODO: when validating the certificate, it is important to use a date from somewhere other than the host computer! (maybe use google? or something...) // this will validate the DATES of the certificate, to make sure the cert is valid during the correct time period. // Verify the TIME/DATE of the certificate ((X509Certificate) certificate).checkValidity(new Date()); // if we get here, it means that our cert is LEGIT and VALID. return true; } catch (Throwable t) { logger.error("Error validating certificate.", t); return false; } } /** * Verifies the given x509 based signature against the OPTIONAL original public key. If not specified, then * the public key from the signature is used. * <p> * MUST have BouncyCastle provider loaded by the security manager! * <p> * @return true if the signature was valid. */ public static boolean verifySignature(byte[] signatureBytes, ECPublicKeyParameters optionalOriginalPublicKey) { ASN1InputStream asn1InputStream = null; try { asn1InputStream = new ASN1InputStream(new ByteArrayInputStream(signatureBytes)); ASN1Primitive signatureASN = asn1InputStream.readObject(); ASN1Sequence seq = ASN1Sequence.getInstance(signatureASN); ASN1TaggedObject tagged = (ASN1TaggedObject) seq.getObjectAt(1); // Extract certificates SignedData newSignedData = SignedData.getInstance(tagged.getObject()); @SuppressWarnings("rawtypes") Enumeration newSigOjects = newSignedData.getCertificates().getObjects(); Object newSigElement = newSigOjects.nextElement(); if (newSigElement instanceof DERSequence) { DERSequence newSigDERElement = (DERSequence) newSigElement; InputStream newSigIn = new ByteArrayInputStream(newSigDERElement.getEncoded()); org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory certificateFactory = new org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory(); java.security.cert.Certificate certificate = certificateFactory .engineGenerateCertificate(newSigIn); PublicKey publicKey2 = certificate.getPublicKey(); if (optionalOriginalPublicKey != null) { ECDomainParameters parameters = optionalOriginalPublicKey.getParameters(); ECParameterSpec ecParameterSpec = new ECParameterSpec(parameters.getCurve(), parameters.getG(), parameters.getN(), parameters.getH()); BCECPublicKey origPublicKey = new BCECPublicKey("EC", optionalOriginalPublicKey, ecParameterSpec, null); boolean equals = origPublicKey.equals(publicKey2); if (!equals) { return false; } publicKey2 = origPublicKey; } certificate.verify(publicKey2); } } catch (Throwable t) { logger.error("Error validating certificate.", t); return false; } finally { if (asn1InputStream != null) { try { asn1InputStream.close(); } catch (IOException e) { logger.error("Error during ECDSA.", e); } } } return true; } } /** * Creates a NEW signature block that contains the pkcs7 (minus content, which is the .SF file) * signature of the .SF file. * * It contains the hash of the data, and the verification signature. */ public static byte[] createSignature(byte[] signatureSourceData, X509CertificateHolder x509CertificateHolder, AsymmetricKeyParameter privateKey) { try { CMSTypedData content = new CMSProcessableByteArray(signatureSourceData); ASN1ObjectIdentifier contentTypeOID = new ASN1ObjectIdentifier(content.getContentType().getId()); ASN1EncodableVector digestAlgs = new ASN1EncodableVector(); ASN1EncodableVector signerInfos = new ASN1EncodableVector(); AlgorithmIdentifier sigAlgId = x509CertificateHolder.getSignatureAlgorithm(); AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); // use the bouncy-castle lightweight API to generate a hash of the signature source data (usually the signature file bytes) BcContentSignerBuilder contentSignerBuilder; AlgorithmIdentifier digEncryptionAlgorithm; if (privateKey instanceof ECPrivateKeyParameters) { contentSignerBuilder = new BcECDSAContentSignerBuilder(sigAlgId, digAlgId); digEncryptionAlgorithm = new AlgorithmIdentifier(DSAUtil.dsaOids[0], null); // 1.2.840.10040.4.1 // DSA hashID } else if (privateKey instanceof DSAPrivateKeyParameters) { contentSignerBuilder = new BcDSAContentSignerBuilder(sigAlgId, digAlgId); digEncryptionAlgorithm = new AlgorithmIdentifier(DSAUtil.dsaOids[0], null); // 1.2.840.10040.4.1 // DSA hashID } else if (privateKey instanceof RSAPrivateCrtKeyParameters) { contentSignerBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId); digEncryptionAlgorithm = new AlgorithmIdentifier(RSAUtil.rsaOids[0], null); // 1.2.840.113549.1.1.1 // RSA hashID } else { throw new RuntimeException("Invalid signature type. Only ECDSA, DSA, RSA supported."); } ContentSigner hashSigner = contentSignerBuilder.build(privateKey); OutputStream outputStream = hashSigner.getOutputStream(); outputStream.write(signatureSourceData, 0, signatureSourceData.length); outputStream.flush(); byte[] sigBytes = hashSigner.getSignature(); SignerIdentifier sigId = new SignerIdentifier( new IssuerAndSerialNumber(x509CertificateHolder.toASN1Structure())); SignerInfo inf = new SignerInfo(sigId, digAlgId, null, digEncryptionAlgorithm, new DEROctetString(sigBytes), (ASN1Set) null); digestAlgs.add(inf.getDigestAlgorithm()); signerInfos.add(inf); ASN1EncodableVector certs = new ASN1EncodableVector(); certs.add(x509CertificateHolder.toASN1Structure()); ContentInfo encInfo = new ContentInfo(contentTypeOID, null); SignedData sd = new SignedData(new DERSet(digestAlgs), encInfo, new BERSet(certs), null, new DERSet(signerInfos)); ContentInfo contentInfo = new ContentInfo(CMSObjectIdentifiers.signedData, sd); CMSSignedData cmsSignedData2 = new CMSSignedData(content, contentInfo); return cmsSignedData2.getEncoded(); } catch (Throwable t) { logger.error("Error signing data.", t); throw new RuntimeException("Error trying to sign data. " + t.getMessage()); } } /** * Load a key and certificate from a Java KeyStore, and convert the key to a bouncy-castle key. * * Code is present but commented out, as it was a PITA to figure it out, as documentation is lacking.... */ public static void loadKeystore(String keystoreLocation, String alias, char[] passwd, char[] keypasswd) { // FileInputStream fileIn = new FileInputStream(keystoreLocation); // KeyStore keyStore = KeyStore.getInstance("JKS"); // keyStore.load(fileIn, passwd); // java.security.cert.Certificate[] chain = keyStore.getCertificateChain(alias); // X509Certificate certChain[] = new X509Certificate[chain.length]; // // CertificateFactory cf = CertificateFactory.getInstance("X.509"); // for (int count = 0; count < chain.length; count++) { // ByteArrayInputStream certIn = new ByteArrayInputStream(chain[0].getEncoded()); // X509Certificate cert = (X509Certificate) cf.generateCertificate(certIn); // certChain[count] = cert; // } // // Key key = keyStore.getKey(alias, keypasswd); // KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm()); // KeySpec keySpec; // if (key instanceof DSAPrivateKey) { // keySpec = keyFactory.getKeySpec(key, DSAPrivateKeySpec.class); // } else { // //keySpec = keyFactory.getKeySpec(key, RSAPrivateKeySpec.class); // throw new RuntimeException("Only able to support DSA algorithm!"); // } // // DSAPrivateKey privateKey = (DSAPrivateKey) keyFactory.generatePrivate(keySpec); // convert private key to bouncycastle specific // DSAParams params = privateKey.getParams(); // DSAPrivateKeyParameters wimpyPrivKey = new DSAPrivateKeyParameters(privateKey.getX(), new DSAParameters(params.getP(), params.getQ(), params.getG())); // X509CertificateHolder x509CertificateHolder = new X509CertificateHolder(certChain[0].getEncoded()); // // fileIn.close(); // close JKS } }