无法使用AWS安全令牌找到到所请求目标的有效认证路径

问题描述

我尝试实施这篇文章https://aws.amazon.com/blogs/security/how-to-eliminate-the-need-for-hardcoded-aws-credentials-in-devices-by-using-the-aws-iot-credentials-provider/

所以我做了下一步:

  1. 创建本地密钥库:

    keystore winpty openssl pkcs12 -export -in eeb81a0eb6-certificate.pem.crt -inkey eeb81a0eb6-private.pem.key -name myname -out my.p12 -password pass:mypass

    keytool -importkeystore -destkeystore mykeystore.jks -srckeystore my.p12 -srcstoretype PKCS12 -deststorepass mypass -srcstorepass mypass

  2. 创建本地信任库:

    keytool -keystore my_ca.jks -alias myalias -import -file AmazonRootCA1.pem

  3. 我的代码

    public class AWSSessionCredentialsProviderImpl implements AWSSessionCredentialsProvider  {
    private static final Logger LOGGER = LogManager.getLogger(AWSSessionCredentialsProviderImpl.class.getName());
    
    private final Gson gson = new Gson();
    
    private SdkHttpClient client;
    private HttpExecuteRequest request; 
    private String awsAccessKeyId;
    private String awsSecretAccessKeyId;
    private String awsSessionToken;
    
    public void init(String clientId) throws IOException,URISyntaxException {
        System.setProperty("javax.net.ssl.trustStore",Configuration.KEYSTOREPATH_CA.toAbsolutePath().toString());
        System.setProperty("javax.net.ssl.trustStoreType","jks");
        
        try {
            System.setProperty("javax.net.ssl.trustStorePassword",new String(Files.readAllBytes(Configuration.KEYSTOREPATH_CA_PASS)));
        } catch (IOException e) {
            throw new IOException("Read password of trust store is Failed",e);
        }
        
        
        System.setProperty("javax.net.ssl.keyStore",Configuration.KEYSTOREPATH.toAbsolutePath().toString());
        System.setProperty("javax.net.ssl.keyStoreType","jks");
        
        try {
            System.setProperty("javax.net.ssl.keyStorePassword",new String(Files.readAllBytes(Configuration.KEYSTOREPATH_PASS)));
        } catch (IOException e) {
            throw new IOException("Read password of key store is Failed",e);
        }

        client = ApacheHttpClient.builder().build();

        SdkHttpRequest httpRequest;
        try {
            httpRequest = SdkHttpFullRequest.builder()
                    .method(SdkHttpMethod.GET)
                    .uri(new URI(Configuration.CLIENT_ENDPOINT))
                    .putHeader("x-amzn-iot-thingname",clientId)
                    .build();
        } catch (URISyntaxException e) {
            throw new URISyntaxException(Configuration.CLIENT_ENDPOINT,"Building URI from client endpoint is Failed");
        }

        request = HttpExecuteRequest.builder()
                .request(httpRequest)
                .build();
        try {
            setCredentials();
        } catch (IOException e) {
            throw new IOException("Set temporary credentials is Failed",e);
        }
    }
    
    @Override
    public void refresh() {
        try {
            setCredentials();
        } catch (IOException e) {
            LOGGER.error("Refresh session credentials is Failed",e);
        }
    }
    
    @Override
    public AWSSessionCredentials getCredentials() {
        return new BasicSessionCredentials(awsAccessKeyId,awsSecretAccessKeyId,awsSessionToken);
    }
    
    private void setCredentials() throws IOException {
        HttpExecuteResponse response = client.prepareRequest(request).call();
        String credStr = IoUtils.toUtf8String(response.responseBody().get());
        
        CredentialsJson credJson = gson.fromJson(credStr,CredentialsJson.class);
        awsAccessKeyId = credJson.credentials.accessKeyId;
        awsSecretAccessKeyId = credJson.credentials.secretAccessKey;
        awsSessionToken = credJson.credentials.sessionToken;
    }
}

  1. 因此,我成功获得了临时凭据,但是当我使用它们时:
AWSSessionCredentialsProviderImpl credentialsProvider = new AWSSessionCredentialsProviderImpl();
credentialsProvider.init("someid");

s3Client = AmazonS3ClientBuilder.standard()
                .withRegion(region)
                .withCredentials(credentialsProvider)
                .build();

s3Client.putObject(request); 

我得到异常: Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

我不明白,如果我可以成功获得临时凭据,为什么会收到此异常。

解决方法

问题可能与很多事情有关。

您的Java程序很可能无法与远程对等方建立信任关系,这可能是因为AWS CA不是预先配置的JVM信任的CA之一。

我认为您可以采用的最好方法是将已经拥有的SdkHttpClient传递给S3客户端。

请注意,在示例代码中,您正在使用AmazonS3ClientBuilder(AWS Java SDK版本1类),而其余代码在使用AWS SDK v2。

也许您可以将代码更新为latest version of the S3Client,然后尝试执行以下操作:

System.setProperty("javax.net.ssl.trustStore",Configuration.KEYSTOREPATH_CA.toAbsolutePath().toString());
System.setProperty("javax.net.ssl.trustStoreType","jks");

try {
    System.setProperty("javax.net.ssl.trustStorePassword",new String(Files.readAllBytes(Configuration.KEYSTOREPATH_CA_PASS)));
} catch (IOException e) {
    throw new IOException("Read password of trust store is failed",e);
}


System.setProperty("javax.net.ssl.keyStore",Configuration.KEYSTOREPATH.toAbsolutePath().toString());
System.setProperty("javax.net.ssl.keyStoreType","jks");

try {
    System.setProperty("javax.net.ssl.keyStorePassword",new String(Files.readAllBytes(Configuration.KEYSTOREPATH_PASS)));
} catch (IOException e) {
    throw new IOException("Read password of key store is failed",e);
}

SdkHttpClient client = ApacheHttpClient.builder().build();

// The idea is reuse the configured HTTP client,modify it as per your needs
AWSSessionCredentialsProviderImpl credentialsProvider = new AWSSessionCredentialsProviderImpl(client);
credentialsProvider.init("someid");

S3Client s3 = S3Client.builder()
  .httpClient(client)
  .region(region)
  .credentialsProvider(credentialsProvider)
  .build();

请确保您的信任库包含实际的SSL证书。您具有AWS的根CA证书,但可能没有与实际服务相对应的证书。

如有必要,您可以通过以下方式获取服务SSL证书:

openssl s_client -connect s3.us-west-1.amazonaws.com:443

请根据您所在地区更改命令。您需要从响应中提取PEM内容。

如答案注释所示,另一种方法可能是取消设置在调用System之前获得凭据时建立的S3Client属性:

System.clearProperty("javax.net.ssl.trustStore");
System.clearProperty("javax.net.ssl.trustStorePassword");
System.clearProperty("javax.net.ssl.trustStoreType");

它将为AWS开发工具包提供全新的环境来调用S3。