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
for ApacheHttpClient
.
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 your curl
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:
import static software.amazon.awssdk.utils.JavaSystemSetting.SSL_KEY_STORE;
import static software.amazon.awssdk.utils.JavaSystemSetting.SSL_KEY_STORE_PASSWORD;
import static software.amazon.awssdk.utils.JavaSystemSetting.SSL_KEY_STORE_TYPE;
//...
Path clientKeyStore = Paths.get(...);
System.setProperty(SSL_KEY_STORE.property(), clientKeyStore.toAbsolutePath().toString());
System.setProperty(SSL_KEY_STORE_TYPE.property(), "pkcs12");
System.setProperty(SSL_KEY_STORE_PASSWORD.property(), "password");
NOTE: The static
imports are only constants for the standard JSSE properties javax.net.ssl.keyStore
, javax.net.ssl.keyStorePassword
, and javax.net.ssl.keyStoreType
.
Second, by providing a TlsKeyManagersProvider
implementation to the tlsKeyManagersProvider
method of ApacheHttpClient.Builder
. For instance:
Path clientKeyStore = ...
TlsKeyManagersProvider keyManagersProvider = FileStoreTlsKeyManagersProvider.create(clientKeyStore, "pkcs12", "password");
In fact, under the hood, the above mentioned System
properties based configuration is used by SystemPropertyTlsKeyManagersProvider
, another TlsKeyManagersProvider
implementation.
If you need to authenticate the server, you also have two options.
First, again, by setting several System
properties:
Path serverKeyStore = Paths.get(...);
System.setProperty("javax.net.ssl.trustStore", serverKeyStore.toAbsolutePath().toString());
System.setProperty("javax.net.ssl.trustStorePassword", "password");
System.setProperty("javax.net.ssl.trustStoreType", "jks");
As you can see, for simplicity, this time we are using a different kind of KeyStore
, jks
. You can build such a KeyStore
from your AWS server certificate PEM file (the one associated with the --cacert
in your curl
command) with something like this:
Path pemPath = ...;
try(final InputStream is = Files.newInputStream(pemPath) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(is);
String alias = cert.getSubjectX500Principal().getName();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
keyStore.setCertificateEntry(alias, cert);
}
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 method tlsTrustManagersProvider
. This method requires an implementation of the TlsTrustManagersProvider interface.
This interface define a single method, trustManagers
, that returns the array of TrustManager
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 its SdkHttpClient.Builder
, to a custom service client, like IotClient
, using the httpClient
or the httpClientBuilder
methods, respectively.
If you just need to test TLS connectivity like with your curl
command, you can try something like this:
Path clientKeyStore = Paths.get(...);
System.setProperty("javax.net.ssl.keyStore", clientKeyStore.toAbsolutePath().toString());
System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
Path serverKeyStore = Paths.get(...);
System.setProperty("javax.net.ssl.trustStore", serverKeyStore.toAbsolutePath().toString());
System.setProperty("javax.net.ssl.trustStorePassword", "password");
System.setProperty("javax.net.ssl.trustStoreType", "jks");
SdkHttpClient client = ApacheHttpClient.builder().build();
SdkHttpRequest httpRequest = SdkHttpFullRequest.builder()
.method(SdkHttpMethod.GET)
.uri(new URI("https://<prefix>.credentials.iot.us-west-2.amazonaws.com/role-aliases/MyAlias/credentials"))
.putHeader("x-amzn-iot-thingname", "myThingName")
.build();
HttpExecuteRequest request = HttpExecuteRequest.builder()
.request(httpRequest)
.build();
HttpExecuteResponse response = client.prepareRequest(request).call();
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):
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>aws-sdk-java</artifactId>
<version>2.15.7</version>
</dependency>
Although, for testing Apache HTTP client, I think that the following dependency will be the only necessary:
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
<version>2.15.7</version>
</dependency>
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:
AwsSessionCredentials credentials = AwsSessionCredentials.create(
"the_returned_access_key_id",
"the_returned_secret_key_id",
"the_returned_session_token"
);
DynamoDbClient ddb = DynamoDbClient.builder()
.region(Region.US_EAST_1)
.credentialsProvider(StaticCredentialsProvider.create(credentials))
.build();
HashMap<String,AttributeValue> itemValues = new HashMap<String,AttributeValue>();
itemValues.put("serial_number", AttributeValue.builder().s("123456789").build());
itemValues.put("timestamp", AttributeValue.builder().s("2017-11-20T06:00:00.000Z").build());
itemValues.put("current_temp", AttributeValue.builder().n("65").build());
itemValues.put("target_temp", AttributeValue.builder().n("70").build());
itemValues.put("humidity", AttributeValue.builder().n("45").build());
PutItemRequest request = PutItemRequest.builder()
.tableName("MyHomeThermostat")
.item(itemValues)
.build();
try {
ddb.putItem(request);
} catch (ResourceNotFoundException e) {
//...
} catch (DynamoDbException e) {
//...
}
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, like StsAssumeRoleCredentialsProvider
or StsGetSessionTokenCredentialsProvider
, 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 of CachedSupplier
and related stuff.