AWS S3 Java SDK – 使用流下载加密的GetObject,来自销售伙伴(SP)API链接

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

AWS S3 Java SDK - Downloading an encrypted GetObject by Stream of a Selling Partner (SP) API url

问题

我最近开始使用SellingPartner(SP),但我对他们如何提供S3报告下载感到有些困惑。

当我从SP API获取报告文档时,我会得到以下返回(省略部分内容):

GetReportDocumentResponse 类:
{
  "payload": {
    "reportDocumentId": "amzn1.tortuga.3.OMITTED.OMITTED",
    "url": "https://tortuga-prod-na.s3-external-1.amazonaws.com/%2FOMITED/amzn1.tortuga.3.OMITTED.OMITTED?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20201025T163212Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=OMITED%2F20201025%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=OMITED",
    "encryptionDetails": {
      "standard": "AES",
      "initializationVector": "OMITED==",
      "key": "+OMITED="
    },
    "compressionAlgorithm": null
  },
  "errors": null
}

如果我直接将payload.url复制粘贴到浏览器中,会下载一个加密文档,看起来还不错(虽然我无法解密它,结尾有代码片段)。

我尝试使用AWS S3 Java SDK下载,但一直收到software.amazon.awssdk.services.s3.model.S3Exception: Access Denied错误。

我有以下代码片段:

public String getReportFile(String reportDocumentId) throws IOException {
    GetReportDocumentResponse response = getReport(reportDocumentId);
    ReportDocumentEncryptionDetails encryptionDetails = response.getPayload().getEncryptionDetails();
    
    GetObjectRequest request =
        GetObjectRequest.builder()
            .key(reportDocumentId)
            .bucket("tortuga-prod-na") //这里硬编码,是URL中的桶名,对吗?
            .sseCustomerAlgorithm(encryptionDetails.getStandard())
            .sseCustomerKey(encryptionDetails.getKey())
            // .sseCustomerKeyMD5() 我是否需要应用它?这是初始化向量字段吗?
            .build();
    
    // 我尝试过不带Credentials,以及使用个人账号的accessKey和secretKey,不确定对于URL是否应该使用其他凭证,如果URL在浏览器中正常工作,我应该使用哪些凭证?
    StaticCredentialsProvider credentialsProvider =
        StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey));
    BufferedReader br =
        new BufferedReader(
            new InputStreamReader(
                S3Client.builder()
                    .credentialsProvider(credentialsProvider)
                    .region(Region.US_EAST_1)
                    .build()
                    .getObject(request)));
}

我的最终目标是分块下载此文件(因为可能超过500MB),并每次处理几百行。如果文件已加密,是否可能?我希望下载后已解密,并能够分块处理。

我想知道如何使用S3Client发起与JSON中URL相同的请求。我们是否有办法只需将URL粘贴到S3Client中,包括加密设置并发出请求?

关于从浏览器下载的文件,我尝试使用以下代码解密:

byte[] bytes = FileUtils.readFileToByteArray(new File("encrypted_file"));

Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(<payload.encryptionDetails.key String value>), "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
System.out.println(new String(cipher.doFinal(bytes)));

但这会抛出异常:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

英文:

I started using SellingPartner (SP) recently and I am kind confused how they provide us S3 reports to download.

When I fetch a Report Document from SP API I get this return (omitted):

GetReportDocumentResponse class:
{
  &quot;payload&quot;: {
    &quot;reportDocumentId&quot;: &quot;amzn1.tortuga.3.OMITTED.OMITTED&quot;,
    &quot;url&quot;: &quot;https://tortuga-prod-na.s3-external-1.amazonaws.com/%2FOMITED/amzn1.tortuga.3.OMITTED.OMITTED?X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Date=20201025T163212Z&amp;X-Amz-SignedHeaders=host&amp;X-Amz-Expires=300&amp;X-Amz-Credential=OMITED%2F20201025%2Fus-east-1%2Fs3%2Faws4_request&amp;X-Amz-Signature=OMITED&quot;,
    &quot;encryptionDetails&quot;: {
      &quot;standard&quot;: &quot;AES&quot;,
      &quot;initializationVector&quot;: &quot;OMITED==&quot;,
      &quot;key&quot;: &quot;+OMITED=&quot;
    },
    &quot;compressionAlgorithm&quot;: null
  },
  &quot;errors&quot;: null
}

If I copy/paste the payload.url directly in my browser, it downloads an encrypted document, which looks fine (I couldn't decrypt it though, snippet in the end).

I am trying to download using the AWS S3 Java SDK and I keep getting software.amazon.awssdk.services.s3.model.S3Exception: Access Denied

I have this snippet:

public String getReportFile(String reportDocumentId) throws IOException {
    GetReportDocumentResponse response = getReport(reportDocumentId);
    ReportDocumentEncryptionDetails encryptionDetails = response.getPayload().getEncryptionDetails();
    
    GetObjectRequest request =
        GetObjectRequest.builder()
            .key(reportDocumentId)
            .bucket(&quot;tortuga-prod-na&quot;) //hardcoding here, thats the bucket on the URL, right?
            .sseCustomerAlgorithm(encryptionDetails.getStandard())
            .sseCustomerKey(encryptionDetails.getKey())
            // .sseCustomerKeyMD5() should I apply it? Is that the Initialization Vector field?
            .build();
    
//I tried both without Credentials, and using accessKey and secretKey from my personal account, not sure if should be another one related to the URL, what should I use for credentials if the URL works fine in my browser?
    StaticCredentialsProvider credentialsProvider =
        StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey));
    BufferedReader br =
        new BufferedReader(
            new InputStreamReader(
                S3Client.builder()
                    .credentialsProvider(credentialsProvider)
                    .region(Region.US_EAST_1)
                    .build()
                    .getObject(request)));
    

My end goal is to download this file in chunks (as it may have over 500mb) and process a few hundred lines at a time. Would that be possible if it's encrypted? I would like to download it already decrypted and be able to process it in chunks.

I wonder how to make the same request using S3Client like the URL coming from the JSON. Do we have a way to just paste a URL on S3Client, include the encryption settings and make a call?

About the downloaded file from the browser, I tried to decrypt it doing this:

byte[] bytes = FileUtils.readFileToByteArray(new File(&quot;encrypted_file&quot;));

Cipher cipher = Cipher.getInstance(&quot;AES/ECB/PKCS5PADDING&quot;);
SecretKeySpec secretKey = new SecretKeySpec(Base64.getDecoder().decode(&lt;payload.encryptionDetails.key String value&gt;), &quot;AES&quot;);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
System.out.println(new String(cipher.doFinal(bytes)));

which throws exception:
Exception in thread &quot;main&quot; javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

Thanks in advance.

答案1

得分: 1

> 如果我直接在浏览器中复制/粘贴 payload.url,它会下载一个加密文档,看起来很正常(尽管我无法解密它,结尾有代码片段)。

这意味着该对象未使用 SSE-C,否则您将无法下载它。看起来内容在客户端(S3 API 外部)进行了加密,然后加密内容被上传为普通对象。所以请在您的代码中检查内容上传的地方,查找加密参数。

如果您能够直接从浏览器下载对象,那么就像下载普通对象一样下载内容。

注意:请正确使用 AWS S3 客户端端到端加密

> 关于从浏览器下载的文件,我尝试通过以下方式解密它:

无论您从哪里获取了这段代码,请不要使用它。仅使用 AES/ECB/PKCS5PADDING 模式是不安全的。

使用 IV(初始化向量)意味着使用了不同的加密操作模式。您必须从代码或加密内容的服务中找出它是什么模式。

private static final String SYMMETRIC_KEY_ALG = "AES";
// 找出正确的值,可能是 AES/CBC/PKCS5Padding
private static final String SYMMETRIC_CIPHER_NAME = "???";

IvParameterSpec ivParamSpec = new IvParameterSpec(encryptionParams.getIv());
SecretKey symmetricKey = new SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);

Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, symmetricKey, ivParamSpec);

byte[] decrypted = cipher.doFinal(encryptionParams.getCiphertext());

然而,您真的需要找出内容是如何加密的。可能使用了 aes-gcm 模式,部分密文可能是认证哈希。因此,在这里您不应该做假设,而是找出真实的数据。

英文:

> If I copy/paste the payload.url directly in my browser, it downloads an encrypted document, which looks fine (I couldn't decrypt it though, snippet in the end).

It means the object is not using SSE-C, otherwise you wouldn't be able to download it. Seems the content is encrypted in the client side (outside s3 api) and the encrypted content is uploaded as a normal object. So please check in your code, where the content is uploaded, for encryption parameters .

If you are able to directly download the object from the browser, then just download the content as a normal object.

Note: proper use of the AWS S3 Client Side Encryption

> About the downloaded file from the browser, I tried to decrypt it doing this

wherever you got this code, please do not use it. Just using the AES/ECB/PKCS5PADDING mode is not safe.

Using the IV (initializationVector) implies using different encryption mode of operation. You have to find out which is it is from the code or service which encrypts the content.

private static final String SYMMETRIC_KEY_ALG = &quot;AES&quot;;
// find out the correct value, could be AES/CBC/PKCS5Padding
private static final String SYMMETRIC_CIPHER_NAME = &quot;???&quot;;

IvParameterSpec ivParamSpec = new IvParameterSpec(encryptionParams.getIv());
SecretKey symmetricKey = new SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);

Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, symmetricKey, ivParamSpec);

byte[] decrypted = cipher.doFinal(encryptionParams.getCiphertext());

However - you really need to find out how the content is encrypted. It could be possible the aes-gcm mode is used and then part of the ciphertext can be an authentication hash. So here you should not make assumptions and find the real data.

huangapple
  • 本文由 发表于 2020年10月26日 09:52:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/64530584.html
匿名

发表评论

匿名网友

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

确定