What Is The Best Keystore to use? - java

I want to use Keystore in my upcoming project. I saw that there is an Keystore API in Java. But that seems to be very complicated.
Are there any other good keystore available that work well with Java/Groovy?

Not that I know of. From my perspective, the API is relatively as minimal as possible give the things that it needs to supply.
KeyStore keyStore = keyStore.getInstance(KeyStore.getDefaultType());
keyStore.load( inputStream, storePassword.getBytes() );
// Get a key
if ( keyStore.isKeyEntry(alias) ) {
Key key = keyStore.getKey(alias, keyPassword.getBytes());
}
// Store a new key
KeyFactory keyFactory = KeyFactory.getInstance(KeyStore.getDefaultType());
KeySpec keySpec ...; // depends on what kind of key you want to create (ie. rsa, etc..)
Key key = keyFactory.generatePrivate(keySpec);
// sign the key here
Certificate certChain[] = ...; // get the cert chain
keyStore.setKeyEntry(newAlias, newKey, newKeyPassword.getBytes(), certChain);
keyStore.store(outputStream, storePassword.getBytes());
This is relatively minimal code to handle what a keystore needs to do.

Related

How do you convert PKCS#12 String to Certificate and PrivateKey?

I am receiving the following String from a certificate stored in Azure Key Vault. I am using the Secret API in order to retrieve both the certificate and the private key related to this cert.
Initially the certificate was uploaded using a .pfx file to Azure Key vault. Now I need to create a Certificate and a PrivateKey to allow client authentication to a 3rd party system and I am using the given String retrieved from the API, however I am note sure how to get around that in Java.
I took some hints from this link in C# however I am pretty certain that this method doesn't work like that in Java. In particular an X509Certificate or a Certificate in general doesn't hold any information about the PrivateKey in Java, unlike C#, and I am not sure how to extract that information from given String in Java.
This works as expected to retrieve the certificate from the String retrieved from the API
String secret = azureSecret.getValue();
byte[] certkey = Base64.getDecoder().decode(secret);
ByteArrayInputStream inputStream = new ByteArrayInputStream(certkey);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(inputStream);
The azureSecret.getValue() format is like the following however I am not sure how to get PrivateKey out of the given String
MIIKvgIBaaZd6Euf3EYwYdHrIIKYzCC...
YES, Java X509Certificate and Certificate is only the certificate. Instead use KeyStore which can contain multiple entries each of which is either a 'trusted' certificate (for someone else), or a privatekey plus certificate plus other chain cert(s) (if applicable) for yourself, or (not relevant here) a 'secret' (symmetric) key. PKCS12 is supported as one type of KeyStore along with others not relevant here, so after the base64-decoding you already have do something like:
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(inputstreamfromvaultvalue, password);
// then
PrivateKey pkey = (PrivateKey) ks.getKey(alias, password);
// and
Certificate cert = ks.getCertificate(alias); // if you only need the leaf cert
// or
Certificate[] chain = ks.getCertificateChain(alias); // usually
But if you want to do client authentication in TLS/SSL (including HTTPS), you give the JSSE KeyManager the whole keystore object not the individual pieces (privatekey and certificates). Similarly to verify the peer in TLS/SSL, you give TrustManager a keystore containing trusted certificates, usually root CAs and often defaulted to a built-in set of public root CAs.

Perform signature with a password protected MS store key using SecureBlackBox Java

Is there an equivalent for the following JCE SunMSCAPI based code for a password protected MS store key using SecureBlackBox Java API?.
KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null);
String firstAlias = keyStore.aliases().nextElement();
PrivateKey privateKey = (PrivateKey) keyStore.getKey(firstAlias, null);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(privateKey);
signature.update(new byte[]{0x01, 0x02}); // TBS
byte[] signatureBytes = signature.sign();
System.out.println(Arrays.toString(signatureBytes));
Yes, you totally - well, unless the underlying cryptographic provider is subject to some very non-standard peculiarities - can do that with SecureBlackbox. You can pass both 'key exchange' and 'signature' pins to the key provider.
We offer a no obligation evaluation package on the web site - please feel free to try it and see if it works with your keys. We can talk you through the process in more detail in the support channels.
Thanks,
Ken

Create signature with bouncycastle api. Key always null

I'm using bouncycastle for generating detached signature for XML's signing. For key initialize I use this code:
Security.addProvider(new BouncyCastleProvider());
KeyStore ks = KeyStore.getInstance(KEYSTORE_INSTANCE);
ks.load(new FileInputStream(KEYSTORE_FILE), KEYSTORE_PWD.toCharArray());
Key key = ks.getKey(CERT_ALIAS, KEYSTORE_PWD.toCharArray());
I have JKS keystore with certificate. But if I do this:
Key key = ks.getKey(CERT_ALIAS, KEYSTORE_PWD.toCharArray());
key stay always null and I have InvalidKeyException
Where's my mistake? I new in crypto
I can't comment due to too low reputation. So I'll answer/edit instead.
The above example works fine, the error is probably in one of the constants being used. What are they, and what is the exact error you're getting?
I mean something like: java.security.InvalidKeyException: Illegal key size
Here is the working example I tried while loading a KeyStore from a file:
'secret1' is the store password, 'secret2' is the key password and 'myKey' is the key alias.
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(keyStoreFile.getAbsolutePath()), "secret1".toCharArray());
Key key = keyStore.getKey("myKey", "secret2".toCharArray());

How to load public certificate from pem file?

I was trying to extract RES public key from the file below
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
Here is the code i did..
public static PublicKey loadPublicKeyFromFile(File publicKeyFile) throws Exception {
FileReader file = new FileReader(publicKeyFile);
PemReader reader = new PemReader(file);
X509EncodedKeySpec caKeySpec = new X509EncodedKeySpec(reader.readPemObject().getContent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey caKey = kf.generatePublic(caKeySpec);
return caKey;
}
But It throws out
java.security.InvalidKeyException: IOException: ObjectIdentifier() -- data isn't an object ID
What's the appropriate way to extract RES Public key from a file..
An X.509 certificate and an X509EncodedKeySpec are quite different structures, and trying to parse a cert as a key won't work.
Java's X509EncodedKeySpec is actually SubjectPublicKeyInfo from X.509 or equivalent and more convenient PKIX also linked from Key, which is only a small part of a certificate.
What you need to do is read and parse the cert and then extract the pubkey from the cert.
Standard SunJCE CertificateFactory can do it
(and can read either PEM or DER to boot) like this:
CertificateFactory fact = CertificateFactory.getInstance("X.509");
FileInputStream is = new FileInputStream (args[0]);
X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
PublicKey key = cer.getPublicKey();
is.close();
// add error handling as appropriate, try-with-resources is often good
If you have BouncyCastle you can use its provider the same way (just add a second argument to .getInstance or set the default provider list order), or you can use PEMParser with JcaX509CertificateConverter -- which effectively does the same thing, internally running the data through a CertificateFactory.
With Sun JVM it is also possible with
import java.security.cert.X509Certificate;
import sun.security.x509.X509CertImpl;
InputStream is = ...
X509Certificate crt = new X509CertImpl(is);
, although I'd prefer the accepted answer as JVM implementation-independent one.
Under the hood, in Sun JVM, CertificateFactory(more precisely, X509Factory) does instantiate X509CertImpl , but there is very subtle difference between the two approaches: CertificateFactory caches X509 Certificate instances by binary content of the input stream, the one that can be retrieved by cer.getEncoded().
The cache can be cleared by
fact.generateCertificate(null);

Creating certificates for SSL communication

I am working on a distributed application with a number of uniquely identified slave processes that will communicate with a master application via SSL enabled sockets. The application is written in java.
I need some help understanding SSLSockets, or rather, the certificates they use.
What i am looking for is someone who can tell me if i have understood the basic workings of certificate chains correctly, but i wouldn't say no to a code sample either.
I would like a setup where the server itself has a CA signed certificate, and every slave will get their own certificate created by the master application.
CA->Main server cert->Master SSL cert
CA->Main server cert->Slave SSL cert 1
CA->Main server cert->Slave SSL cert 2
CA->Main server cert->Slave SSL cert 3
First question: Is this kind of certificate chain the correct way to tackle the problem?
I am thinking this is the simplest way of achieving the master and slaves all have a unique identity without having to CA sign every certificate.
Second question:
How do i programatically go about creating an SSL certificate in java? I am trying to create the last certificate in the chain here, assuming i already have the "Main server cert" for now.
I have gotten so far as generating a key for the certificate (Where type is RSA):
public KeyPair generateKeypair(String type, int bytes)
throws NoSuchAlgorithmException{
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(type);
keyPairGenerator.initialize(bytes);
return keyPairGenerator.generateKeyPair();
}
X509Principal issuer = PrincipalUtil.getSubjectX509Principal(serverCert);
SubjectPublicKeyInfo key
= SubjectPublicKeyInfo.getInstance(kpair.getPublic().getEncoded());
X509v3CertificateBuilder certGen
= new X509v3CertificateBuilder(
issuer,
BigInteger.valueOf(new SecureRandom().nextInt()),
before,
after,
subject,
key
);
AlgorithmIdentifier sigAlgId
= new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId
= new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
I don't assume that setting the serverCert as the issuer is enough to sign the certificate? As far as i have understood i need to sign the new certificate with the next certificate in the chain somehow, but how do i do that? Do i sign the certificate with the serverCert's private key like:
AsymmetricKeyParameter akp
= PrivateKeyFactory.createKey(serverPrivateKey.getEncoded());
AlgorithmIdentifier sigAlgId
= new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId
= new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
ContentSigner sigGen
= new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(akp);
Are there any other steps i missed?
From a technical point of view your solution is correct. However do not forget the security considerations: who can request a certificate, how the authentication is performed, how the certificates/private keys are distributed to the servers...
These elements are mandatory for a certificate generation:
Subject Name
Issuer name
certificate serial number
subject public key
validity dates (not before, not after)
It is also a good practice to add some extensions:
Subject Key Identifier
Authority Key Indentifier
Basic Constraints
Key Usage
Extended Key Usages
This code snippet outlines the certificate generation:
ContentSigner getCertSigner(PrivateKey issuerKey) {
AsymmetricKeyParameter akp = PrivateKeyFactory.createKey(issuerKey.getEncoded());
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
return new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(akp);
}
X509CertificateHolder generateCertificate(X509Certificate issuerCert, PrivateKey issuerKey, X500Name subject, PublicKey subjectKey, Date notBefore, Date notAfter) {
X509Principal issuerDN = PrincipalUtil.getSubjectX509Principal(issuerCert);
SubjectPublicKeyInfo key = SubjectPublicKeyInfo.getInstance(subjectKey.getEncoded());
X509v3CertificateBuilder builder = new X509v3CertificateBuilder(issuerDN, BigInteger.valueOf(new SecureRandom().nextInt()), before, after, subject, key);
// Add authority key identifier
builder.addExtension(X509Extension.authorityKeyIdentifier, false, JcaX509ExtensionUtils.createAuthorityKeyIdentifier(issuerCert));
// Add subject key identifier
builder.addExtension(X509Extension.subjectKeyIdentifier, false, JcaX509ExtensionUtils.createSubjectKeyIdentifier(subjectKey));
// Add basic constraints
builder.addExtension(X509Extension.basicConstraints, true, new BasicConstraints(false));
// Add key usage
KeyUsage keyUsage = new KeyUsage(KeyUsage.keyEncipherment|KeyUsage.digitalSignature);
builder.addExtension(X509Extension.keyUsage, true, keyUsage);
// Add extended key usage
ExtendedKeyUsage extKeyUsage = new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth);
builder.addExtension(X509Extension.extendedKeyUsage, false, extKeyUsage);
return builder.build(getCertSigner(issuerKey));
}
UPDATE: fixed the code according to Martin Nielsen's comment.

Categories