Can somebody explain me, how do I need to implement the first step from this blog? I can't find it in AWS documentation.
In other words, I need to translate a command:
curl --cert eeb81a0eb6-certificate.pem.crt --key eeb81a0eb6-private.pem.key -H "x-amzn-iot-thingname: myThingName" --cacert AmazonRootCA1.pem https://<prefix>.credentials.iot.us-west-2.amazonaws.com/role-aliases/MyAlias/credentials
to JAVA. How can I do it? I need AWS SDK for it (I prefer a solution without "custom client to make an HTTPS request")
UPDATE:
I tried to use a custom client to make an HTTPS request, but I stuck when strated to export my keys to Java KeyStore (BUT curl command works for me fine):
$ winpty openssl pkcs12 -export -in eeb81a0eb6-certificate.pem.crt -inkey eeb81a0eb6-private.pem.key -chain -CAfile AmazonRootCA1.pem -name mycompany.com -out my.p12
Error unable to get local issuer certificate getting chain.
ANOTHER UPDATE (WHAT I TRIED ALREADY)
Convert myPrivateKey and deviceCertificate to JKS:
winpty openssl pkcs12 -export -in eeb81a0eb6-certificate.pem.crt -inkey eeb81a0eb6-private.pem.key -name mycompany.com -out my.p12
keytool -importkeystore -destkeystore mycompany.jks -srckeystore my.p12 -srcstoretype PKCS12
Use this JKS from my code:
System.setProperty("deployment.security.TLSv1.2", "true"); System.setProperty("https.protocols", "TLSv1.2"); System.setProperty("javax.net.debug", "ssl"); HttpPost request = new HttpPost(clientEndpoint); request.setHeader("x-amzn-iot-thingname", "0ad16050-d974-4f78-88ea-c6ee2b0a551e"); KeyStore keyStore; try (InputStream keyStoreStream = this.getClass().getResourceAsStream(KEYSTOREPATH)) { keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(keyStoreStream, KEYSTOREPASS.toCharArray()); } SSLContext sslContext = SSLContexts.custom() .loadKeyMaterial(keyStore, KEYPASS.toCharArray()) // use null as second param if you don't have a separate key password .loadTrustMaterial(null, new TrustSelfSignedStrategy()) .build(); SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("https", sslConnectionSocketFactory) .register("http", new PlainConnectionSocketFactory()) .build(); BasicHttpClientConnectionManager manager = new BasicHttpClientConnectionManager(registry); try (CloseableHttpClient httpClient = HttpClients .custom() .setSSLSocketFactory(sslConnectionSocketFactory) .setConnectionManager(manager) .build(); CloseableHttpResponse response = httpClient.execute(request)) { System.out.println(); } catch (IOException e) { System.err.println(e); }
I get exception:
javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
The AWS SDK provides several implementations of
SdkHttpClient
that you can use to interact with your Amazon Services, both synchronously or asynchronously.For instance, you can use the
ApacheHttpClient
class.All this HTTP clients are created and configured with
Builder
s,ApacheHttpClient.Builder
forApacheHttpClient
.ApacheHttpClient.Builder
provides methods that allows you to configure secure HTTP connections for client side, remote peer, or mutual authentication.If the client must be authenticated, it is necessary to provide the certificate and private key that must be used for that purpose, corresponding to the
--cert
and--key
arguments of yourcurl
invocation.Typically, this certificate and private key are stored in one password protected
KeyStore
, usually in PKCS#12 format (a.p12
or.pfx
file).This information can be made accessible to
ApacheHttpClient.Builder
in two ways.First, by setting a series of
System
properties:NOTE: The
static
imports are only constants for the standard JSSE propertiesjavax.net.ssl.keyStore
,javax.net.ssl.keyStorePassword
, andjavax.net.ssl.keyStoreType
.Second, by providing a
TlsKeyManagersProvider
implementation to thetlsKeyManagersProvider
method ofApacheHttpClient.Builder
. For instance:In fact, under the hood, the above mentioned
System
properties based configuration is used bySystemPropertyTlsKeyManagersProvider
, anotherTlsKeyManagersProvider
implementation.If you need to authenticate the server, you also have two options.
First, again, by setting several
System
properties:As you can see, for simplicity, this time we are using a different kind of
KeyStore
,jks
. You can build such aKeyStore
from your AWS server certificate PEM file (the one associated with the--cacert
in yourcurl
command) with something like this:In the case of mutual authentication, although you can reuse the same
KeyStore
, it is a best practice maintain two, one with the client private key and certificate, and other with the server certificates you will trust (the trust store).Alternatively, you can also configure server side authentication by defining the
TrustManager
s that need to be used.For this task, the
ApacheHttpClient.Builder
provides the methodtlsTrustManagersProvider
. This method requires an implementation of the TlsTrustManagersProvider interface.This interface define a single method,
trustManagers
, that returns the array ofTrustManager
s that must be used to check the remote peer in the SSL communication.Unfortunately, the AWS SDK does not provide an implementation of this interface, you need to implement your own (let me know if you need further info).
Once initialized and configured, you can provide this
SdkHttpClient
or itsSdkHttpClient.Builder
, to a custom service client, likeIotClient
, using thehttpClient
or thehttpClientBuilder
methods, respectively.If you just need to test TLS connectivity like with your
curl
command, you can try something like this:Please, review this test in the AWS Java SDK, it can be also helpful.
Finally, there is also async HTTP clients that you can use in your project. The way in which secure HTTP communication is configured in these clients is very similar to the one described in the above paragraphs.
You can find all these resources in the AWS Java SDK v2 GitHub repository.
You can import in your project the whole SDK (I assume you are using Maven):
Although, for testing Apache HTTP client, I think that the following dependency will be the only necessary:
Although I have tried to focus the answer on code provided by the AWS SDK, as I understood it was necessary, to obtain these temporary credentials it is also possible to use any mechanism that allows a secure connection to AWS, such as Apache HttpClient, like in your example, OkHttp, etcetera.
These temporary credentials can be used to sign any AWS Request and perform operations - according to the assumed IAM role - on AWS services. For instance, following the example in the blog that you indicated, you can insert an item in a DynamoDB table:
In relation to your question in the comments above how to renew the obtained token, I must recognize that I am unable to give you an answer.
In my opinion, I am afraid that the temporary credentials returned by the above mentioned call cannot be refreshed, at least the AWS SDK does not provide any mechanism for that: this credential provider is a very specific use case designed for IoT as indicated in the blog you cited and in the official AWS documentation.
The AWS SDK provides different
AWSCredentialsProvider
s that supports token renewal, likeStsAssumeRoleCredentialsProvider
orStsGetSessionTokenCredentialsProvider
, among others, but there is no specific provider for this use case.If it is of any help, you can review the source code of the base class
StsCredentialsProvider
, specially the code in its constructor related with the setup ofCachedSupplier
and related stuff.