AWS IoT dispose d'un mécanisme permettant d'émettre des informations d'identification de sécurité temporaires à l'aide des certificats clients individuels de Thing pour accéder aux ressources AWS telles que S3. Il n'y avait pas d'exemple Java en tant qu'implémentation côté périphérique, alors je l'ai essayé. \ # En premier lieu, je ne pense pas qu'il existe de nombreux exemples d'implémentation d'applications qui s'exécutent sur des appareils IoT en Java ...
Autorisation d'appels directs Le mécanisme de base est résumé dans l'article suivant. [AWS IoT] Accéder à S3 / DynamoDB à l'aide d'un certificat client distinct pour Thing
Le code Java utilisé pour la vérification dans cet article se trouve ci-dessous. https://github.com/tmiki/aws-iot-authorizing-direct-calls-sample
--Créer divers fichiers KeyStore --Modifier le code source Java
Tout d'abord, préparez le certificat de la station AWS Root CA, le certificat client et la clé privée en vous basant sur l'article suivant.
[AWS IoT] Accéder à S3 / DynamoDB à l'aide d'un certificat client distinct pour Thing
Téléchargez le certificat de la station racine CA à partir de l'URL ci-dessous et créez un KeyStore. https://www.amazontrust.com/repository/AmazonRootCA1.pem
Le nom du fichier KeyStore à créer est "AWSRootCaStore.bin".
$ keytool -import -trustcacerts -alias AmazonRootCA1 -file ./AmazonRootCA1.pem -keystore AWSRootCaStore.bin -storepass password_amazon_root_ca
Owner: CN=Amazon Root CA 1, O=Amazon, C=US
Issuer: CN=Amazon Root CA 1, O=Amazon, C=US
Serial number: 66c9fcf99bf8c0a39e2f0788a43e696365bca
Valid from: Tue May 26 09:00:00 JST 2015 until: Sun Jan 17 09:00:00 JST 2038
Certificate fingerprints:
SHA1: 8D:A7:F9:65:EC:5E:FC:37:91:0F:1C:6E:59:FD:C1:CC:6A:6E:DE:16
SHA256: 8E:CD:E6:88:4F:3D:87:B1:12:5B:A3:1A:C3:FC:B1:3D:70:16:DE:7F:57:CC:90:4F:E1:CB:97:C6:AE:98:19:6E
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 3
Extensions:
#1: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
CA:true
PathLen:2147483647
]
#2: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
Key_CertSign
Crl_Sign
]
#3: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 84 18 CC 85 34 EC BC 0C 94 94 2E 08 59 9C C7 B2 ....4.......Y...
0010: 10 4E 0A 08 .N..
]
]
Trust this certificate? [no]: yes
Certificate was added to keystore
$ ls -l AWSRootCaStore.bin
-rw-r--r-- 1 mta 197121 1146 Aug 25 12:09 AWSRootCaStore.bin
Ensuite, créez un fichier KeyStore pour le certificat client. La commande keytool ne semble pas avoir de fonction pour importer directement le certificat / clé privée, donc créez-le une fois au format PKCS12 en utilisant OpenSSL. Il vous sera demandé le mot de passe pour exporter, alors entrez-le correctement.
$ openssl pkcs12 -export -in c231554173-certificate.pem.crt -inkey c231554173-private.pem.key -out ClientCertKey.p12
Enter Export Password:
Verifying - Enter Export Password:
$ ls -l ClientCertKey.p12
-rw-r--r-- 1 vagrant vagrant 2461 Aug 25 15:54 ClientCertKey.p12
Créez un fichier KeyStore pour le certificat client / la clé privée en fonction du fichier créé précédemment. Bien que cela n'ait pas été complètement vérifié, Java semble être capable de gérer non seulement le format JKS mais aussi le format PKCS12 en tant que KeyStore. Peut-être que ce travail est inutile.
$ keytool -importkeystore \
-srckeystore ClientCertKey.p12 -srcstoretype pkcs12 \
-destkeystore client_cert_key_store.bin -deststorepass "password_client_cert_key_store"
Importing keystore ClientCertKey.p12 to client_cert_key_store.bin...
Enter source keystore password: previously_input_password
Entry for alias 1 successfully imported.
Import command completed: 1 entries successfully imported, 0 entries failed or cancelled
Tout d'abord, veuillez cloner le dépôt Git suivant de manière appropriée. https://github.com/tmiki/aws-iot-authorizing-direct-calls-sample
Stockez le fichier KeyStore suivant dans le répertoire directement sous l'exemple de code. En tant que KeyStore pour le certificat client / clé privée, client_cert_key_store.bin et ClientCertKey.p12 sont pratiquement équivalents. Java semble être capable de gérer l'un ou l'autre format, je l'ai donc implémenté pour qu'il puisse gérer les deux. Dans cet article, nous utiliserons ClientCertKey.p12 pour la vérification.
Une constante est définie au début de la classe principale InvestigateAwsIotADC, changez-la donc en fonction de votre environnement.
public class InvestigateAwsIotADC {
// Constant values of the Amazon Root CA.
static String ROOT_CA_FILE_PATH = "AWSRootCaStore.bin";
static String ROOT_CA_FILE_PASSWORD = "password_amazon_root_ca";
// Constant values of a client certificate.
static String CLIENT_CERT_KEY_STORE_PKCS12_FILE_PATH = "ClientCertKey.p12";
static String CLIENT_CERT_KEY_STORE_PKCS12_PASSWORD = "password_client_cert_key_store";
// Replace the domain name with yours.
static String AWS_IOT_ROLE_ALIAS_ENDPOINT = "https://****YourEndpointDomain****/role-aliases/****YourRoleAlias****/credentials";
static String AWS_IOT_THING_NAME = "****YourThingName***";
static String AWS_IOT_ADC_HEADER_NAME = "x-amzn-iot-thingname";
// Replace the AWS resources configurations with yours.
static Regions AWS_REGION = Regions.AP_NORTHEAST_1;
static String AWS_S3_BUCKET_NAME = "****YourBucketName****";
static String AWS_S3_FILE_NAME = "****YourFile****";
Excusez-moi, lancez "Investigate AwsIot ADC" depuis Eclipse. Je voulais créer un fichier Jar avec Gradle, mais je ne pouvais pas inclure les bibliothèques dépendantes ... Nous mettrons à jour ici dès qu'il sera confirmé.
L'exemple de code effectue les opérations suivantes:
--Créez un objet client HTTP et
Si l'exécution réussit, ce qui suit sera affiché sur la console. Un AVERTISSEMENT peut être émis, mais ignorez-le ici.
Résultat d'exécution
Started.
Http request: GET https://abcdefgh123456.credentials.iot.ap-northeast-1.amazonaws.com/role-aliases/InvestigateIotRoleAlias/credentials HTTP/1.1
Http Status Code: 200
Http Response Header: content-type: application/json
Http Response Header: content-length: 1189
Http Response Header: date: Sun, 25 Aug 2019 15:02:34 GMT
Http Response Header: x-amzn-RequestId: 0d33da6d-58d0-f450-aa66-f8278cf6834e
Http Response Body: {"credentials":{"accessKeyId":"ASIA................","secretAccessKey":"NPf6................................","expiration":"2019-08-25T16:02:34Z"}}
WARNING: Your profile name includes a 'profile ' prefix. This is considered part of the profile name in the Java SDK, so you will need to include this prefix in your profile name when you reference this profile from your Java code. [Mon Aug 26 00:02:35 JST 2019]
WARNING: Your profile name includes a 'profile ' prefix. This is considered part of the profile name in the Java SDK, so you will need to include this prefix in your profile name when you reference this profile from your Java code. [Mon Aug 26 00:02:35 JST 2019]
The content of the S3 file: retrieved
Finished.
Tout d'abord, créez un objet SSLContext à l'aide de SSLContextBuilder d'Apache HC, mais spécifiez le KeyStore à ce stade. Dans la méthode loadTrustMaterial (), spécifiez le KeyStore qui stocke le certificat de station RootCA. Dans la méthode loadKeyMaterial (), spécifiez le KeyStore qui spécifie le certificat client et la clé privée. À ce stade, il est nécessaire de spécifier le mot de passe pour l'ensemble du KeyStore et le mot de passe de la clé privée, mais comme il n'y a pas de tel concept lors de l'utilisation du format PKCS12, il semble que les deux spécifient le même mot de passe. \ # Les spécifications ici n'ont pas encore été étudiées et ne sont pas claires.
Créez un SSLConnectionSocketFactory en spécifiant l'objet SSLContext généré. Lors de la création de l'objet CloseableHttpClient, spécifiez cet objet SSLConnectionSocketFactory et générez-le.
createHttpClient()
public static CloseableHttpClient createHttpClient() {
// The "custom()" method returns "SSLContextBuilder".
// Its parameters are a little complicated.
//
// https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/ssl/SSLContexts.html#custom()
// https://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/ssl/SSLContextBuilder.html
SSLContext sslcontext = null;
try {
sslcontext = SSLContexts.custom()//
.loadKeyMaterial(new File(CLIENT_CERT_KEY_STORE_PKCS12_FILE_PATH), CLIENT_CERT_KEY_STORE_PKCS12_PASSWORD.toCharArray(), CLIENT_CERT_KEY_STORE_PKCS12_PASSWORD.toCharArray())
.loadTrustMaterial(new File(ROOT_CA_FILE_PATH), ROOT_CA_FILE_PASSWORD.toCharArray())
.build();
} catch (Exception e) {
System.out.println("An Exception has been thrown while creating an SSLContext.");
e.printStackTrace();
}
// https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLConnectionSocketFactory.html
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
// https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/HttpClients.html
return HttpClients.custom().setSSLSocketFactory(sslsf).build();
}
Émettez des requêtes HTTP de la manière standard Apache HC. À ce stade, vous devez spécifier le nom de la chose dans l'en-tête "x-amzn-iot-thingname".
Si la requête HTTP aboutit, les informations d'identification de sécurité temporaires seront renvoyées au format JSON.
invokeHttpRequest()
public static String invokeHttpRequest(CloseableHttpClient httpclient) {
// Add a header specify an AWS IoT thing name described in the AWS IOT document below.
// https://docs.aws.amazon.com/iot/latest/developerguide/authorizing-direct-aws.html
HttpUriRequest request = RequestBuilder.get() //
.setUri(AWS_IOT_ROLE_ALIAS_ENDPOINT)
.setHeader(AWS_IOT_ADC_HEADER_NAME, AWS_IOT_THING_NAME)
.build();
System.out.println("Http request: "+ request.getRequestLine());
try {
CloseableHttpResponse response = httpclient.execute(request);
// Prints an HTTP Status Code, HTTP Headers and an HTTP Response Body.
System.out.println("Http Status Code: " + response.getStatusLine().getStatusCode());
List<Header> headers = Arrays.asList(response.getAllHeaders());
headers.forEach((header) -> {System.out.println("Http Response Header: " + header);});
String response_body = IOUtils.toString(response.getEntity().getContent(), "UTF-8");
System.out.println("Http Response Body: " + response_body);
response.close();
return (response_body);
} catch (IOException e) {
System.out.println("An Exception has been thrown while invoking an HTTP request.");
e.printStackTrace();
}
return null;
}
Analysez le corps de la réponse HTTP renvoyé en tant que JSON et récupérez les valeurs de AccessKeyId / SecretAccessKey / SessionToken respectivement. Ceci est emballé dans BasicSessionCredentials et renvoyé.
BasicSessionCredentials (AWS SDK for Java - 1.11.618) https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/BasicSessionCredentials.html
parseCredentialsFromHttpResponse()
public static BasicSessionCredentials parseCredentialsFromHttpResponse(String http_response) {
JSONObject response_json = new JSONObject(http_response);
String access_key_id = response_json.getJSONObject("credentials").getString("accessKeyId");
String secret_access_key = response_json.getJSONObject("credentials").getString("secretAccessKey");
String session_token = response_json.getJSONObject("credentials").getString("sessionToken");
return new BasicSessionCredentials(access_key_id, secret_access_key, session_token);
}
Utilisez BasicSessionCredentials pour obtenir le fichier sur S3 et afficher son contenu sur la console.
getS3Object()
public static String getS3Object(BasicSessionCredentials temporary_credentials) {
// See also the following document.
// https://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingTempSessionTokenJava.html
AmazonS3 s3Client = AmazonS3ClientBuilder.standard() //
.withCredentials(new AWSStaticCredentialsProvider(temporary_credentials))
.withRegion(AWS_REGION)
.build();
S3Object s3object = s3Client.getObject(AWS_S3_BUCKET_NAME, AWS_S3_FILE_NAME);
try {
String s3object_content = IOUtils.toString(s3object.getObjectContent(), "UTF-8");
return s3object_content;
} catch (IOException e) {
System.out.println("An Exception has been thrown while retreiving an S3 object.");
e.printStackTrace();
}
return null;
}
Travailler avec des certificats clients en Java a été plus difficile que prévu. Apache HC fait du bon travail à certains endroits, mais c'est quand même assez compliqué.
Je n'ai pas entièrement saisi les spécifications Java KeyStore, mais il semble qu'il puisse gérer non seulement le format JKS mais également le format PKCS12. C'est là que je voudrais enquêter séparément.
Recommended Posts