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
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.
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 apologize that this is so long. If you are familiar with doing client-auth in Java you can probably skim/skip to the bottom. I took me a long time to find all the relevant bits of information from disparate sources. Maybe this will help someone else.
I'm working on a proof-of-concept to get client-side authentication working from a Java client using the Windows keystore. I have a servlet that I have created that can request or require client certificates. It simply returns an HTML response containing the certificate information including subject DN and the serial number. I've worked through a number of experiments with browsers (mainly Chrome and IE) to verify it is working. I've gotten successful client authentication working with both certs I've generated with SSL and also using certs issued by my companies internal CA.
My next step is to have a Java client that works with the MSCAPI keystore in Java. The reason I went this route is that the certificates we want to use are issued by our internal CA and are automatically added to the Personal keystore in Windows and are marked as non-exportable. I know there are ways to get the private key out of the keystore with some free tools if you are an admin on your workstation. This isn't a viable option in this case for various reasons. Here's the code I ended up with:
private static SSLSocketFactory getFactory() throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException, KeyManagementException
{
KeyStore roots = KeyStore.getInstance("Windows-ROOT", new SunMSCAPI());
KeyStore personal = KeyStore.getInstance("Windows-MY", new SunMSCAPI());
roots.load(null,null);
personal.load(null,null);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(roots);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(personal, "".toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
return context.getSocketFactory();
}
This works but when I connect, the Java client is authenticating with the client-certs I had previously generated. I don't see an obvious way to force the selection of a specific key so I figured it was just picking the first key it came across that the host trusts. I then removed these certs that I had created from the windows keystore and it would no longer authenticate. I triple-checked that the server-side was still works with the browsers and it does. I then readded these certs to the personal keystore and removed the trust of my pseudo-CA from the server-side. Still works in the browser and not in the client.
I added -Djavax.net.debug=ssl:handshake to my VM parameters and started looking through the output. One thing I see is are lines "found key for : [alias]" for each of the certs that I have added but not for the ones added through the 'self-enrollment' feature of certmgr.msc. Initially I thought it was because they were exportable but I removed them and added one back as non-exportable and java can still use it. I'm at a loss as to why SunMSCAPI won't use these certs. Both the ones created by me and internal CA are sha256RSA. Both are enabled for client authentication. When I add the following code, I see that isKeyEntry() is false for all the key-pairs I want to use:
Enumeration<String> aliases = personal.aliases();
while (aliases.hasMoreElements())
{
String alias = aliases.nextElement();
System.out.println(alias + " " + personal.isKeyEntry(alias));
}
I found someone else with a similar issue here but no definitive answer.
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";
Is it possible to change keystore at runtime? Currently I am setting up SSL before I do a server.start() -
sslContextFactory.setTrustStore(ks);
sslContextFactory.setTrustStorePassword(TRUSTSTORE_PASS);
sslContextFactory.setKeyStorePassword(KEYSTORE_PASS);
ServerConnector https = new ServerConnector(server, sslContextFactory);
server.start()
What I would like to do is create a certificate at runtime and use it. Basically I am creating a tool like Fiddler which creates certificates on the fly.
This has been fixed since Jetty 9.4.0, see https://github.com/eclipse/jetty.project/issues/918. You can now just override the Key/TrustStore etc. and call SslContextFactory.reload.
Note however there is a caveat with TLS session resumption: https://github.com/eclipse/jetty.project/issues/918#issuecomment-250791417. According to the comments, it shouldn't be an issue with common browsers, but who knows about IE, Mobile, non-browser clients, etc.
After posting this question in Jetty mailing list, I got response that is it not really feasible
I'm not an expert in java's security packages but to my knowledge there is no straight forward way to create the keypair from public API.
However, I is possible if you could allow your code do an import from sun's restricted packages like:
import sun.security.x509.*;
Here is an outline of code you are looking for:
PrivateKey privkey = pair.getPrivate();
X509CertInfo info = new X509CertInfo();
Date from = new Date();
//Validity for next one year
Date to = new Date(from.getTime() + (365) * 86400000l);
CertificateValidity interval = new CertificateValidity(from, to);
BigInteger sn = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(dn);
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
// Sign the cert
X509CertImpl cert = new X509CertImpl(info);
cert.sign(privkey, algorithm);
//cert object is ready to use
Hope this helps
Create your own KeyStore implementation.
You can create a class that overrides KeyStore and put this as a truststore to Jetty. Then you are free to return any Certificate you want.
Probably you have to use a 3rd party library to create certificates on the fly as Java cannot create certificates (with the official API). You can use BouncyCastle for this.
It seems there are two issues here: generating the certificate on the dynamically ("What I would like to do is create a certificate at runtime and use it.") and setting it up without restarting ("Is it possible to change keystore at runtime?").
To generate a certificate dynamically, you can use BouncyCastle and its X509V3CertificateGenerator class.
First, generate a self-signed CA (with the CA basic constraint set), using keytool for example (look at the -ext option for details). This will be your custom CA.
Export the certificate from that keystore (only the CA certificate, not its private key) and import it into the clients you're going to use.
In your application, using that private key for signing with the X509V3CertificateGenerator, and make sure the Issuer DN you use matches the Subject DN of the CA cert you've generated above.
Then, you'll need to configure the certificate generate with a Subject DN (or Subject Alternative Name) that matches the host name your client intended to contact. This may be the tricky bit if you intend to do this automatically as some sort of transparent proxy. (As far as I know, current versions of Java can't read the name coming from the SNI extension, at least not in advance or without doing more manual processing.)
The easier way would certainly be to have this host name as a configurable option in your tool.
To set it up without restarting the server, you could implement your own X509KeyManager that stays in place in the SSLContext you're using, but for which you keep a reference and custom accessors to re-configure the certificate later on. It's certainly not necessarily something "clean", and I haven't tried it, but it should work in principle. (You should certainly make sure the concurrency aspects are handled properly.)
This might allow you not to have to shut down the listening socket, reconfigure the SSLContext and restart the socket. Considering that you might need to interact with your application anyway (to reconfigure the host name), this might be overkill.