无法找到有效的证书路径以获取带有AWS安全令牌的请求目标。

huangapple go评论65阅读模式
英文:

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  {
    // ...
    // 代码部分省略
    // ...
}
  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

我不明白为什么在成功获取临时凭证后会出现这个异常。

英文:

I'm trying to implement the solution presented in the following AWS article:


So I did next steps:

  1. Create local 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. Create local truststore:

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

  3. My code:

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. So, I get temporary credentials successfully, but when I use them:
AWSSessionCredentialsProviderImpl credentialsProvider = new AWSSessionCredentialsProviderImpl();
credentialsProvider.init("someid");
s3Client = AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(credentialsProvider)
.build();
s3Client.putObject(request); 

I get the following exception:

> Caused by:
> sun.security.provider.certpath.SunCertPathBuilderException:
> unable to find valid certification path to requested target

I don't understand why I get this exception if I can get temporary credentials successfully.

答案1

得分: 4

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

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

我认为解决这个问题的最佳方法是将您已经拥有的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();

// 重新使用配置的HTTP客户端,根据您的需求进行修改
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的新环境。

英文:

The problem could be related with many things.

Most likely, your Java program will not be able to establish a trust relationship with the remote peer, probably because the AWS CA is not one of the preconfigured JVM trusted CAs.

I think the best approach you can take to solve the problem is to pass the SdkHttpClient that you already have to the S3 client as well.

Please, be aware that in your sample code you are using AmazonS3ClientBuilder, a AWS Java SDK version 1 class, meanwhile the rest of the code is using AWS SDK v2.

Maybe you can update your code to the latest version of the S3Client and try something like this:

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

Please, be sure that your trust store contains the actual SSL certificate. You have the root CA certificate of AWS, but maybe not the corresponding to the actual service.

If necessary, you can obtain the service SSL certificate with something like this:

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

Please, change the command according to your region. You need to extract the PEM content from the response.

As indicated in the comments to the answer, another alternative could be unset the System properties established when you obtain your credentials before the invocation of the S3Client:

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

It will provide the AWS SDK with a fresh environment for invoking S3.

huangapple
  • 本文由 发表于 2020年10月14日 13:35:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/64347224.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定