问题描述
我尝试实施这篇文章:https://aws.amazon.com/blogs/security/how-to-eliminate-the-need-for-hardcoded-aws-credentials-in-devices-by-using-the-aws-iot-credentials-provider/
所以我做了下一步:
-
创建本地密钥库:
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
-
创建本地信任库:
keytool -keystore my_ca.jks -alias myalias -import -file AmazonRootCA1.pem
-
我的代码:
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客户端。
请注意,在示例代码中,您正在使用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。