java - 无法使用 AWS 安全 token 找到请求目标的有效证书路径

标签 java amazon-web-services keystore credentials truststore

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


所以我做了接下来的步骤:

  1. 创建本地 keystore :

    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;
    }
}

  • 因此,我成功获得了临时凭据,但是当我使用它们时:
  • 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 客户端。

    请注意,在您的示例代码中,您使用的是 AWS Java SDK 版本 1 类 AmazonS3ClientBuilder,同时其余代码使用的是 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 内容。

    如答案评论中所示,另一种替代方法可以取消设置在调用 S3Client 之前获取凭据时建立的 System 属性:

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

    它将为 AWS SDK 提供调用 S3 的全新环境。

    关于java - 无法使用 AWS 安全 token 找到请求目标的有效证书路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64347224/

    相关文章:

    java - MongoRepository findOne 使用 "id"而不是 "_id"

    java - Android:相互调用方法并显示消息

    amazon-web-services - AWS Cloudformation - Elasticsearch 访问控制策略服务错误 InvalidTypeException

    java - 使用其他 PC 时出现 SSL 验证程序异常

    java - Android KeyStore问题,另一个设备相同的应用程序

    java - 如何每秒编辑一次Jlabel?

    java - 从 JApplet 关闭 JFrame

    amazon-web-services - 如何使用来自 ACM 的 AWS 证书使用 Amazon EC2 从端口 80 重定向到 443?

    amazon-web-services - 使用 Lex 和 Alexa 的区别

    java - JMeter - 根据应用环境设置javax.net.ssl.keyStore和javax.net.ssl.keyStorePassword