I'm trying to load the system trust store in Java. The problem is that my code will be shipping in a library will be used by applications for Android, Windows, linux, and OSX, and the location of the cacerts file is different on each system.
Here is my code:
// Load the JDK's cacerts keystore file.
String filename = System.getProperty("javax.net.ssl.trustStore");
if (filename == null)
filename = System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
FileInputStream is = new FileInputStream(filename);
// Load the root certificate authorities. Despite the name, KeyStore can also hold certificates.
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
// Yes this is really the password.
String password = "changeit";
keystore.load(is, password.toCharArray());
// Retrieves the most-trusted CAs from keystore.
PKIXParameters params = new PKIXParameters(keystore);
This works fine when testing on linux, but I don't think this will work on Android for example.
Is there an easy way to programatically find the location of the system trust store, or am I condemned to explicitly enumerate every possibility and hard-code the trust store path for each?
a call to :
KeyStore keystore = KeyStoreUtil.getCacertsKeyStore();
will return all the System CA trusted certificates, which is a platform independent way to read the file your code.
Remark: your code will work if you use a null password:
String password = null;//"changeit";
Related
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.
I am writing an Android application to connect a sensor to an AWS IoT service.
I have been provided with the X.509 certificate, a pair of public-private key, clientEndpoint, etc.
I am trying to follow the AWS's Sample code (see here).
The instructions are clear, but I don't want to generate the certificate and the keys (I already have them).
Below is the code snippet:
// Create a new private key and certificate. This call
// creates both on the server and returns them to the
// device.
CreateKeysAndCertificateRequest createKeysAndCertificateRequest = new CreateKeysAndCertificateRequest();
createKeysAndCertificateRequest.setSetAsActive(true);
final CreateKeysAndCertificateResult createKeysAndCertificateResult;
createKeysAndCertificateResult = mIotAndroidClient.createKeysAndCertificate(createKeysAndCertificateRequest);
Log.i(LOG_TAG,"Cert ID: " +createKeysAndCertificateResult.getCertificateId() +" created.");
// store in keystore for use in MQTT client
// saved as alias "default" so a new certificate isn't
// generated each run of this application
AWSIotKeystoreHelper.saveCertificateAndPrivateKey(certificateId,createKeysAndCertificateResult.getCertificatePem(),createKeysAndCertificateResult.getKeyPair().getPrivateKey(),
keystorePath, keystoreName, keystorePassword);
// load keystore from file into memory to pass on
// connection
clientKeyStore = AWSIotKeystoreHelper.getIotKeystore(certificateId,keystorePath, keystoreName, keystorePassword);
How can I use the existing certificates files instead of generating new certificate and keys?
Thank you
Here's a full snip that I've successfully used with some test code.
String tempFilePath = context.getFilesDir().getAbsolutePath();
if (!AWSIotKeystoreHelper.isKeystorePresent(tempFilePath, "iotkeystore")) {
Resources resources = context.getResources();
int certId = resources.getIdentifier("foo_device_cert", "raw", context.getPackageName());
int privkeyId = resources.getIdentifier("foo_device_private_key", "raw", context.getPackageName());
String cert = TestUtils.loadTestResource(context, certId);
String privKey = TestUtils.loadTestResource(context, privkeyId);
AWSIotKeystoreHelper.saveCertificateAndPrivateKey("iotcert", cert, privKey, tempFilePath, "iotkeystore", "iotpasswd");
}
KeyStore keystore = AWSIotKeystoreHelper.getIotKeystore("iotcert", tempFilePath, "iotkeystore", "iotpasswd");
AWSIotMqttManager mqttManager = new AWSIotMqttManager("sdk-java", "xxxxxxxxxxxx-ats.iot.us-east-2.amazonaws.com");
mqttManager.connect(keystore, new LocalMqttStatusCallback());
mqttManager.subscribeToTopic("sdk/test/java", AWSIotMqttQos.QOS0, new LocalMessageCallback());
logger.info("testLoadCertificate()...");
In this case, I simply saved the certificates in the res/raw folder and loaded them at run time as shown above. This prob. isn't the best from a security standpoint, but should help you get something working. I put this entire snip into a Robolectric test case and the keystore gets reloaded each time. It properly connects and receives messages though.
The AWS documentation is really poor here, I wasn't able to find any working sample code from them (bad links) and I'm not wanting to just turn over my entire project to Amplify and it's automatic reconfiguration.
Good luck.
Use
AWSIotKeystoreHelper.isKeystorePresent(mKeystorePath, mKeystoreName) to check if keystore is already on you device
Check Alias using AWSIotKeystoreHelper.keystoreContainsAlias(mCertificateId, mKeystorePath, mKeystorePassword)
Get keystore using keystore = AWSIotKeystoreHelper.getIotKeystore(mCertificateId, mKeystoreName, mKeystorePassword)
Use keystore on mqttManager to connect
For C# I am getting directly code, but for java I'm not able to find such code or API. Please help me regarding this.
I tried with KeyStore ks = KeyStore.getInstance("Windows-MY", "SunMSCAPI");,
but it returns server side installed certificate.
I want to read certificate from USB attached by client on his local machine.
As per the KeyStore JavaDocs:
Before a keystore can be accessed, it must be loaded.
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// get user password and file input stream
char[] password = getPassword();
try (FileInputStream fis = new FileInputStream("keyStoreName")) {
ks.load(fis, password);
}
I would like to know how can I rename an alias of a keystore, programmatically in java, not using keytool.
I have my java.security.KeyStore object, containing an certain alias. How can I rename it?
The KeyStore API does not provide a rename operation for aliases. But what you can do is:
Save the content (key pair, certificates) of the keystore entry that you want to rename.
Delete the entry.
Create a new entry with the saved content and the new alias.
As Java code:
Key privateKey = keyStore.getKey(alias, password.toCharArray());
Certificate[] certs = keyStore.getCertificateChain(alias);
keyStore.setKeyEntry(newAlias, privateKey, password.toCharArray(), certs);
keyStore.deleteEntry(alias);
Of course this does not work if the private key is stored on a hardware device (smartcard or HSM) and therefore is not readable.
If the keystore entry contains a trusted certificate, the code looks a bit different:
Certificate cert = keyStore.getCertificate(alias);
keyStore.setCertificateEntry(newAlias, cert);
keyStore.deleteEntry(alias);
I have an X.509 certificate created using bouncycastle library. How can I store it into a java Keystore?
I tried this code
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// get user password and file input stream
char[] password = getPassword();
java.io.FileInputStream fis =
new java.io.FileInputStream("keyStoreName");
ks.load(fis, password);
fis.close();
I found this code here, but the key store created using this way does not work with keytool, it tells me the keystore is corrupted.
Use KeyStore.setCertificateEntry(alias, cert) and give it an alias name of your choice. Then, use KeyStore.store(...) to save the keystore (typically using a FileOutputStream).