从AWS Credentials提供商获取安全令牌

时间:2020-10-08 07:13:36

标签: java amazon-web-services primary-key aws-iot x509securitytokenmanager

有人可以向我解释一下,我该如何执行此blog的第一步? 我在AWS文档中找不到它。

换句话说,我需要翻译一个命令:

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

到JAVA。我该怎么做?我需要AWS开发工具包(我更喜欢没有“自定义客户端发出HTTPS请求”的解决方案)

更新:

我尝试使用自定义客户端发出HTTPS请求,但是在将密钥导出到Java KeyStore时遇到了困难(但是curl命令对我来说很好用):

$ 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.

另一个更新(我已经尝试过的内容)

  1. 将myPrivateKey和deviceCertificate转换为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

  2. 从我的代码中使用此JKS:

     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);
     }
    
  3. 我遇到异常:

    javax.net.ssl.SSLHandshakeException:收到致命警报:bad_certificate

1 个答案:

答案 0 :(得分:2)

AWS开发工具包提供SdkHttpClient的几种实现,可用于与您的Amazon Services进行同步或异步交互。

例如,您可以使用ApacheHttpClient类。

所有这些HTTP客户端都是使用BuilderApacheHttpClient.Builder代表ApacheHttpClient创建和配置的。

ApacheHttpClient.Builder提供的方法可让您为客户端,远程对等或相互身份验证配置安全的HTTP连接。

如果必须对客户端进行身份验证,则必须提供用于此目的的证书和私钥,与您的--cert的{​​{1}}和--key参数相对应调用。

通常,此证书和私钥存储在一个受密码保护的curl中,通常以PKCS#12格式(KeyStore.p12文件)存储。

可以通过两种方式使.pfx可以访问此信息。

首先,通过设置一系列ApacheHttpClient.Builder属性:

System

注意: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"); 只是标准JSSE属性staticjavax.net.ssl.keyStorejavax.net.ssl.keyStorePassword的常量。

第二,通过为javax.net.ssl.keyStoreType的{​​{1}}方法提供TlsKeyManagersProvider实现。例如:

tlsKeyManagersProvider

实际上,实际上,上述ApacheHttpClient.Builder基于属性的配置已由SystemPropertyTlsKeyManagersProvider(另一种Path clientKeyStore = ... TlsKeyManagersProvider keyManagersProvider = FileStoreTlsKeyManagersProvider.create(clientKeyStore, "pkcs12", "password"); 实现)使用。

如果您需要对服务器进行身份验证,则还有两个选择。

首先,再次通过设置几个System属性:

TlsKeyManagersProvider

如您所见,为简单起见,这次我们使用的是另一种SystemPath 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"); 。您可以使用以下方法从AWS服务器证书PEM文件(与KeyStore命令中的jks相关联的文件)构建这样的KeyStore

--cacert

在相互认证的情况下,尽管可以重复使用相同的curl,但最佳实践是维护两个,一个使用客户端私钥和证书,另一个使用您将信任的服务器证书(信任库)。

或者,您也可以通过定义需要使用的TrustManager来配置服务器端身份验证。

对于此任务,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); } 提供了方法KeyStore。此方法需要实现TlsTrustManagersProvider接口。

此接口定义单个方法ApacheHttpClient.Builder,该方法返回必须用于检查SSL通信中的远程对等方的tlsTrustManagersProvider数组。

不幸的是,AWS开发工具包未提供此接口的实现,您需要实现自己的接口(如果需要更多信息,请告诉我)。

在初始化和配置后,您可以使用trustManagers或{{1}将此TrustManager或其SdkHttpClient提供给自定义服务客户端,例如IotClient }}方法。

如果您只需要像使用SdkHttpClient.Builder命令一样测试TLS连接,则可以尝试执行以下操作:

httpClient

请在AWS Java SDK中查看this test,这也会有所帮助。

最后,您还可以在项目中使用async HTTP clients。在这些客户端中配置安全HTTP通信的方式与以上各段所述的方式非常相似。

您可以在AWS Java SDK v2 GitHub repository中找到所有这些资源。

您可以将整个SDK导入项目中(我假设您正在使用Maven):

httpClientBuilder

尽管,对于测试Apache HTTP客户端,我认为以下依赖项是唯一必要的:

curl

尽管我已尝试将答案集中在AWS开发工具包提供的代码上,据我所知,这是必要的,但要获得这些临时凭证,也可以使用允许与AWS安全连接的任何机制,例如Apache HttpClient,例如您的示例中的OkHttp等。

这些临时凭证可用于签署任何AWS Request并根据假定的IAM角色在AWS服务上执行操作。例如,按照您指示的blog中的示例,您可以在DynamoDB表中插入一个项目:

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();

关于您在上面的评论中如何续签获得的令牌的问题,我必须认识到我无法给您 answer

我认为,恐怕无法刷新上述调用返回的临时凭证,至少AWS开发工具包没有为此提供任何机制:该凭证提供者是专门为物联网设计的非常特殊的用例,在您引用的博客和official AWS documentation中指明。

AWS开发工具包提供了<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>aws-sdk-java</artifactId> <version>2.15.7</version> </dependency> 个不同的<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>apache-client</artifactId> <version>2.15.7</version> </dependency> ,它们支持令牌续订,例如StsAssumeRoleCredentialsProviderStsGetSessionTokenCredentialsProvider,但是此用例没有特定的提供程序。

如果有帮助,可以查看基类StsCredentialsProvider的源代码,特别是其构造函数中与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) { //... } 的设置和相关内容有关的代码。

相关问题