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

Unable to find valid certification path to requested target with AWS security token

我正在尝试实施以下 AWS 文章中介绍的解决方案:


所以我做了后续步骤:

  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 class,同时其余代码使用的是 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 SDK提供一个全新的调用S3的环境。