dorkbox.util.crypto.CryptoX509 Maven / Gradle / Ivy
Show all versions of Utilities Show documentation
/*
* 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.Base64;
import java.util.Date;
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.BERSequence;
import org.bouncycastle.asn1.BERSet;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.DEROctetString;
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.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 = Base64.getMimeEncoder().encode(x509cert.getEncoded());
char[] encodeToChar = new char[derCert.length];
for (int i = 0; i < derCert.length; i++) {
encodeToChar[i] = (char) derCert[i];
}
int newLineCount = encodeToChar.length/64;
int length = encodeToChar.length;
output = new BufferedWriter(new FileWriter(fileName, 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) {
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 = Base64.getMimeEncoder().encode(x509cert.getEncoded());
char[] encodeToChar = new char[derCert.length];
for (int i = 0; i < derCert.length; i++) {
encodeToChar[i] = (char) derCert[i];
}
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 entriesVec = new Vector();
//
// // 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 result = new Vector(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.
*
* 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.
*
* MUST have BouncyCastle provider loaded by the security manager!
*
* @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.
*
* MUST have BouncyCastle provider loaded by the security manager!
*
* @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();
BERSequence seq = (BERSequence) ASN1Sequence.getInstance(signatureASN);
ContentInfo contentInfo = ContentInfo.getInstance(seq);
// Extract certificates
SignedData newSignedData = SignedData.getInstance(contentInfo.getContent());
InputStream newSigIn = new ByteArrayInputStream(newSignedData.getCertificates().parser().readObject().toASN1Primitive().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;
}
}
// throws exception if it fails
engineGenerateCert.verify(publicKey2);
return true;
} catch (Throwable t) {
return false;
} finally {
if (asn1InputStream != null) {
try {
asn1InputStream.close();
} catch (IOException e) {
logger.error("Error closing stream during DSA.", e);
}
}
}
}
}
@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 attrs = new Hashtable();
// //Vector order = new Vector();
// //
// //attrs.put(BCStyle.C, "US");
// //attrs.put(BCStyle.O, "Dorkbox");
// //attrs.put(BCStyle.OU, "Dorkbox Certificate Authority");
// //attrs.put(BCStyle.EmailAddress, "[email protected]");
// //
// //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.
*
* 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.
*
* MUST have BouncyCastle provider loaded by the security manager!
*
* @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.
*
* MUST have BouncyCastle provider loaded by the security manager!
*
* @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();
BERSequence seq = (BERSequence) ASN1Sequence.getInstance(signatureASN);
ContentInfo contentInfo = ContentInfo.getInstance(seq);
// Extract certificates
SignedData newSignedData = SignedData.getInstance(contentInfo.getContent());
InputStream newSigIn = new ByteArrayInputStream(newSignedData.getCertificates().parser().readObject().toASN1Primitive().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;
}
}
// throws exception if it fails.
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.
*
* MUST have BouncyCastle provider loaded by the security manager!
*
* @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.
*
* MUST have BouncyCastle provider loaded by the security manager!
*
* @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();
BERSequence seq = (BERSequence) ASN1Sequence.getInstance(signatureASN);
ContentInfo contentInfo = ContentInfo.getInstance(seq);
// Extract certificates
SignedData newSignedData = SignedData.getInstance(contentInfo.getContent());
InputStream newSigIn = new ByteArrayInputStream(newSignedData.getCertificates().parser().readObject().toASN1Primitive().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;
}
// throws an exception if not valid!
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 during ECDSA.", e);
}
}
}
}
}
/**
* 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
}
}