I need to generate certification chain in my java application becouse its needed when storing privatekey to keystore? Can anybody help me out. I have no idea how to do it..
I need to generate RSA keypair and then store it to keystore. Right now my code looks like this:
public static void main(String[] args)
{
String issuerDN = null;
String addKeyName = "mynewkey";
String delKeyName = null;
String password = "2222";
boolean listStore = true;
boolean deleteKeysAftherWrap = false;
try
{
/* make sure that we have access to the eracom provider */
Provider p = new ERACOMProvider();
Security.addProvider(p);
int keySize = 1024;
KeyPair keyPair = null;
/* get the eracom keystore - access to the adapter */
KeyStore keyStore = KeyStore.getInstance("CRYPTOKI", p.getName());
/* LOAD the keystore from the adapter */
keyStore.load(null, password.toCharArray());
if (addKeyName != null)
{
/* This key cannot be added to the keystore if it already exists */
if (keyStore.containsAlias(addKeyName))
{
println("");
println("Key name already exists");
println("");
}
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", p.getName());
keyPairGenerator.initialize(keySize);
keyPair = keyPairGenerator.generateKeyPair();
PublicKey pubKey = keyPair.getPublic();
PrivateKey privKey = keyPair.getPrivate();
keyStore.setKeyEntry("newpub", pubKey, null, null);
keyStore.setKeyEntry("newpriv", privKey, null, null});
}
the keys are generated but it asks certification chain for storing private key.
And that is the problem right now. How can i generate the certification chain, do i have to generate certifications first, when yes then how?
Not sure what are you trying to achieve, but some time ago I've used this little app (source code included) to insert an existing private key into a keystore. Hopefully you'll find this useful: http://www.agentbob.info/agentbob/79-AB.html
I believe the post http://www.pixelstech.net/article/1406726666-Generate-certificate-in-Java----2 will show you how to generate certificate chain with pure Java. It doesn't require you to use Bouncy Castle.
This post will show you how to generate a certificate chain which has a length longer than 1. While most posts on the Internet will show you creating a certificate chain of length 1 or using BC.
Related
Its best to show my problem and explain it after (I am using KeyStore Explorer to peek into my .pfx files):
Basically, I want the result on the right, but I get the result on the left.
To be more precise: The KeyStore should contain the private key and certs chain (KeyPair), in one entry.
I somehow couldn't get it working in java. This is what I tried to get the result on the left:
The problem with the code below is, that its not verifing the certs and adding them as trusted certs.
/**
* Creates and returns a new {#link KeyStore} from the provided information.
* #param keystoreType The {#link KeyStore}s type. Recommended: "pkcs12".
* Check {#link KeyStore#getInstance(String)} for details.
* #param passwordAsCharArray Password to encrypt this {#link KeyStore} and its keys.
* #param certificateChain The certificate chain is simply an array of {#link X509Certificate}s.
* #param privateKey The {#link PrivateKey}.
* #param publicKey The {#link PublicKey}.
* #throws KeyStoreException
*/
public KeyStore buildAndGetKeystore(String keystoreType, char[] passwordAsCharArray,
X509Certificate[] certificateChain, PrivateKey privateKey, PublicKey publicKey)
throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException, EmptyStringException, EmptyCharArrayException, EmptyCertificateChainException {
// Check for null parameters
Objects.requireNonNull(keystoreType);
Objects.requireNonNull(passwordAsCharArray);
Objects.requireNonNull(certificateChain);
Objects.requireNonNull(privateKey);
Objects.requireNonNull(publicKey);
// Check for empty parameters
if (keystoreType.isEmpty()) throw new EmptyStringException("Parameter 'keystoreType' should NOT be empty!");
if (passwordAsCharArray.length==0) throw new EmptyCharArrayException("Parameter 'passwordAsCharArray' should NOT be empty!");
if (certificateChain.length==0) throw new EmptyCertificateChainException("Parameter 'certificateChain' should NOT be empty!");
// Initialise a new keystore
KeyStore keystore = KeyStore.getInstance(keystoreType);
keystore.load(null, passwordAsCharArray); // Pass null to tell java this is a new keystore
// Insert certificates
for (int i = 0; i < certificateChain.length; i++) {
keystore.setCertificateEntry(""+i, certificateChain[i]);
}
// Write private key (with password protection) to keystore.
// NOTE: I tried this before and it only writes
// the private key to the .pfx file and ignores the domain chain
//keystore.setKeyEntry("sso-signing-key", privateKey, passwordAsCharArray, certificateChain);
return keystore;
}
Some extra details:
The KeyStore on the right was created like this: First generated the certs at sslforfree.com and then converted them to a PKCS12 KeyStore with https://decoder.link/converter
I created my own certificate chain and used this structure for the certificateChain:
X509Certificate[] certificateChain = new X509Certificate[3];
certificateChain[0] = topCertificate;
certificateChain[1] = middleCertificate;
certificateChain[2] = rootCertificate;
The keystore creation part is as simple like this (I left out the "notNull checks"):
KeyStore keystore = KeyStore.getInstance(keystoreType);
keystore.load(null,null);
keystore.setKeyEntry("use_your_own_alias", privateKey, passwordAsCharArray, certificateChain);
return keystore;
Kindly note that the publicKey is NOT part of the storage process as it is available with
certificateChain[0].getPublicKey()
Checking the keystore (I used a PKCS12 one) in KeyStoreExplorer gives this output:
and the key chain:
You guys won't believe this, but... The problem was the alias name 'priv-key'. Just removed the hyphen '-' and changed the alias name to 'myKey' like in https://stackoverflow.com/a/67147429/13600212 and voila it works...
So many hours lost for such a stupid thing...
I'm currently working on a project that involves getting an Elliptic Curve Certificate / Private Key package in either PEM or DER format from Vault and needing to import it into a Java Keystore. I can find plenty of information on doing this with keytool, but am unable to find information about how to successfully convert a DER encoded string into a PrivateKeyEntry to insert it into the keystore.
Below is my non-working code. certificate , key, and issuingCa are all PEM or DER encoded Strings (I can specify which format I want to get back from the issuer and pass whichever I can get to work)
private KeyStore packKeystore(String certificate, String key, String issuingCa, String name) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
// Create the keystore
KeyStore retVal = KeyStore.getInstance(KeyStore.getDefaultType());
retVal.load(null, sslKeystorePassword.toCharArray());
var cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME);
var cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificate.getBytes()));
retVal.setCertificateEntry(name, cert);
Certificate issuer = null;
if (issuingCa != null) {
issuer = cf.generateCertificate(new ByteArrayInputStream(issuingCa.getBytes(StandardCharsets.UTF_8)));
}
if (key != null) {
var certs = new HashSet<Certificate>();
certs.add(issuer);
certs.add(cert);
PrivateKeyEntry pk = /// How do I create this from what I have????
retVal.setKeyEntry( pk, certs.toArray());
}
return retVal;
}
After some experimentation and research I've learned that the PrivateKey class doesn't like the "old" PEM format where the private key looks like ---- BEGIN EC PRIVATE KEY-----.
I was eventually able to parse a PrivateKey object from a keypair like this:
var parsedKey = new org.bouncycastle.openssl.PEMParser(new StringReader(key)).readObject();
var pair = new org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter().getKeyPair((org.bouncycastle.openssl.PEMKeyPair) parsedKey);
retVal.setKeyEntry(name, pair.getPrivate(), "".toCharArray(), certArray);
From there I get an error on setKeyEntry about the certificate algorithm not matching the private key algorithm. Upon inspection it seems that the Certificate object says the algo is EC and the PK object says the algo is ECDSA.
I eventually solved it this way. Note the key must be in base64 encoded DER format:
private PrivateKey convertECPrivateKeyString(String key) {
...
byte[] keyBytes = null;
if (key != null) {
keyBytes = Base64.getDecoder().decode(key);
}
try (var asnStream = new ASN1InputStream(keyBytes)) {
var primitive = asnStream.readObject();
asnStream.close();
if (primitive instanceof ASN1Sequence) {
var sequence = (ASN1Sequence) primitive;
var pKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(sequence);
var pkInfo = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters()), pKey);
return new JcaPEMKeyConverter().getPrivateKey(pkInfo);
}
}
...
}
I would like to save a X509Certificate and its private key into the Android KeyStore, I tought I should 'merge' the X509Certificate (containing the public key) and its private key. The private key is used to create a CSR and then a server party sign the certificate and return to the application, can I merge the cert and the private key into one unique cert? Also I'm using spongycastle (aka bouncycastle's android wrapper).
I have no idea about Android KeyStore, but maybe you can try something like:
PrivateKey privateKey = ... //this is what you already have
X509Certificate certificate = ... //this is what you already have
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
Certificate[] certChain = new Certificate[1];
certChain[0] = certificate;
char[] myKeyPassword = "myKeyPassword".toCharArray();
keyStore.setKeyEntry("mykeyalias", (Key)privateKey, myKeyPassword, certChain);
See https://docs.oracle.com/javase/9/docs/api/java/security/KeyStore.html#setKeyEntry-java.lang.String-java.security.Key-char:A-java.security.cert.Certificate:A- for more information about KeyStore.setKeyEntry
I wanted to load the keystore file and get the details of its root SSL Certificate.
Link to Sample
I saw the code in the above link. The code is also visible below. What is the use of keyStore.isKeyEntry(alias)?
I check JavaDocs and it says
Returns true if the entry identified by the given alias was created by a call to setKeyEntry, or created by a call to setEntry with a PrivateKeyEntry or a SecretKeyEntry.
So, What is the use of it?
boolean isAliasWithPrivateKey = false;
KeyStore keyStore = KeyStore.getInstance("JKS");
// Provide location of Java Keystore and password for access
keyStore.load(new FileInputStream(jksPath), jksPassword.toCharArray());
// iterate over all aliases
Enumeration<String> es = keyStore.aliases();
String alias = "";
while (es.hasMoreElements()) {
alias = (String) es.nextElement();
// if alias refers to a private key break at that point
// as we want to use that certificate
if (isAliasWithPrivateKey = keyStore.isKeyEntry(alias)) {
break;
}
}
if (isAliasWithPrivateKey) {
KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias,
new KeyStore.PasswordProtection(jksPassword.toCharArray()));
PrivateKey myPrivateKey = pkEntry.getPrivateKey();
// Load certificate chain
Certificate[] chain = keyStore.getCertificateChain(alias);
certDetails = new CertificateDetails();
certDetails.setPrivateKey(myPrivateKey);
certDetails.setX509Certificate((X509Certificate) chain[0]);
}
You have 3 types of entries in a java keystore:
privateKeyEntry, private key and associated certificate chain
trustedKeyEntry, a trusted certificate (e.g. certificate from CA like Verisign, GoDaddy ...)
secretKeyEntry, a encryption key (e.g. a symmetric key AES)
as described in the java docs, KeyStore.isKeyEntry(alias) returns true is you have secret or a private key.
The code example will fail if the entry is a secret key:
`KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(jksPassword.toCharArray()));`
it would be preferable to use:
`KeyStore.entryInstanceOf(alias , KeyStore.PrivateKeyEntry.class)`
I have a requirement as below:
Create a self signed (say CA cert) and save the cert and private key
to files
Load the CA Cert (Created in Step 1) and its private key
Create a end
certificate which is signed using CA Cert and Private Key loaded in
Step 2
My Private Key is Stored to a file as below:
public static void writePrivateKey(PrivateKey privateKey, OutputStream os) throws IOException
{
BufferedOutputStream bos = new BufferedOutputStream(os);
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
privateKey.getEncoded());
bos.write(pkcs8EncodedKeySpec.getEncoded());
bos.close();
}
My private Key is loaded back as below:
public PrivateKey loadPrivatekey(InputStream privateKeyInputStream)
throws InvalidKeySpecException, NoSuchAlgorithmException, IOException
{
return KeyFactory.getInstance(algorithamForCreatingAndLoadingKeys)
.generatePrivate(new PKCS8EncodedKeySpec(IOUtils.toByteArray(privateKeyInputStream)));
}
I have my algorithm defined as RSA
private String algorithamForCreatingAndLoadingKeys = "RSA";
Iam signing my certificate as below:
private static X509CertImpl buildAndSignCert(X509CertInfo certInfo, PrivateKey privateKey)
throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
NoSuchProviderException, SignatureException, IOException
{
X509CertImpl cert = new X509CertImpl(certInfo);
String algorithm = "SHA1withRSA";
// Sign the cert to identify the algorithm that's used.
cert.sign(privateKey, algorithm);
// Update the algorith, and resign.
certInfo.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM,
cert.get(X509CertImpl.SIG_ALG));
X509CertImpl newCert = new X509CertImpl(certInfo);
newCert.sign(privateKey, algorithm);
return newCert;
}
Problem: If i create the CA cert and end certificate without saving and loading the key file I was able to validate the end certificate fine:
C:\Workspace.....\src\main\resources>openssl verify -CAfile ca.pem end.pem
end.pem: OK
But If i save and load the key files and verify, I get below error, which clearly says that my end certificate is not signed correct with my ca cert
C:\Workspace.....\src\main\resources>openssl verify -CAfile ca.pem
end.pem
end.pem: C = AU, L = ABC, CN = XYZ
error 20 at 0 depth lookup:unable to get local issuer certificate
So, Iam coming to the conclusion that my saving of private key and loading it is buggered.
Can any one pleas help what Iam doing wrong in saving and reading private keys ?
Many Thanks in advance.
All the code that I have posted above is correct and accurate to create and read a private key file.
I have spotted the problem to be with loading the Created CA Cert itself (not the private key), some how when we generate X509 Certificate as below:
CertificateFactory f = CertificateFactory.getInstance("X.509");
X509Certificate loadedCaCert = (X509Certificate) f
.generateCertificate(CertificateGenerator.class.getResourceAsStream("/ca.pem"));
the serial number that it is producing out of X509Certificate generated above is not the same as the one I passed when creating CA certtificate (Which is another issue but not realted to thsi thread anyways) and as we pass this serial number to subscriber certificate to identify its parent CA for certificate linking its failing.
Thank You