Не удается найти действительный путь сертификации к запрошенной цели с помощью токена безопасности AWS

#java #amazon-web-services #keystore #credentials #truststore

#java #amazon-веб-сервисы #хранилище ключей #учетные данные #truststore

Вопрос:

Я пытаюсь реализовать решение, представленное в следующей статье 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); 
  

Я получаю следующее исключение:

Вызвано:
sun.security.provider.certpath.Исключение SunCertPathBuilderException: не удается найти действительный путь сертификации к запрошенной цели

Я не понимаю, почему я получаю это исключение, если я могу успешно получить временные учетные данные.

Ответ №1:

Проблема может быть связана со многими вещами.

Скорее всего, ваша Java-программа не сможет установить доверительные отношения с удаленным одноранговым узлом, вероятно, потому, что центр сертификации AWS не является одним из предварительно настроенных доверенных центров сертификации JVM.

Я думаю, что лучший подход, который вы можете предпринять для решения проблемы, — это передать SdkHttpClient то, что у вас уже есть, клиенту S3.

Пожалуйста, имейте в виду, что в вашем примере кода, который вы используете AmazonS3ClientBuilder , класс AWS Java SDK версии 1, в то время как остальной код использует AWS SDK v2.

Возможно, вы можете обновить свой код до последней версии 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, но, возможно, он не соответствует фактическому сервису.

При необходимости вы можете получить сертификат 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.

Комментарии:

1. Я проверил. Происходит то же исключение. Я успешно получаю временные учетные данные, но не удается поместить объект в S3.

2. Я вижу грубее. Пожалуйста, просто для того, чтобы разобраться в проблеме, можете ли вы отменить System установленные вами свойства System.clearProperty("javax.net.ssl.trustStore"); System.clearProperty("javax.net.ssl.trustStorePassword"); System.clearProperty("javax.net.ssl.trustStoreType"); перед S3Client вызовом и создать S3Client без передачи HTTP-клиента в httpClient методе? Извините, если я не очень хорошо объяснил себя.

3. Это работает! Я не понимаю. Итак, для получения токена безопасности нам нужно это хранилище доверия, а для другой команды aws мы хотим использовать команду по умолчанию (jdk1.8.0_111 jre lib security cacerts)? Это очень странно

4. Я попытался добавить AmazonRootCA1.pem в jdk1.8.0_111 jre lib security cacerts, но это не помогло.

5. Извините за поздний ответ. Да, я думаю, проблема заключается в том, что ваше хранилище доверия не содержит фактического SSL-сертификата. У вас есть сертификат корневого центра сертификации AWS, но не соответствующий фактическому сервису. Возможно, вы можете попытаться загрузить этот конкретный сертификат и включить его в свое хранилище доверия. Вы можете получить сертификат с помощью чего-то вроде этого: openssl s_client -connect s3.us-west-1.amazonaws.com:443 . Измените команду в соответствии с вашим регионом. Вам необходимо извлечь содержимое pem из ответа. Пожалуйста, вы можете попробовать?