生成SSE-C的预签名URL时出错

时间:2019-03-20 12:47:12

标签: amazon-web-services amazon-s3 aws-sdk

我已经使用客户密钥(SSE-C)为AWS S3服务器端加密创建了客户密钥。

我可以使用键上传对象。但是,当我使用AWS Java SDK生成一个预签名URL时,该URL已成功创建,但是当我单击该URL时,出现了以下错误。

  

  SignatureDoesNotMatch      我们计算出的请求签名与您提供的签名不匹配。检查您的密钥和签名方法。

import java.io.IOException;
import java.net.URI;
import java.net.URL;

import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.HttpMethod;
import com.amazonaws.SdkClientException;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.Headers;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
import com.amazonaws.services.s3.model.SSEAlgorithm;
import com.amazonaws.services.s3.model.SSECustomerKey;
import com.amazonaws.util.Base64;
import com.amazonaws.util.Md5Utils;

public class GeneratePresignedURL {

	public GeneratePresignedURL() throws IOException {
		String bucketName = "abctest";
		String keyName = "testnew.mp4";

		try {
			SSECustomerKey SSE_KEY = new SSECustomerKey("KLgsVafKowMCfKDsbIh597CmMUSoPBn6QJ8OIGxAMBw=");

			ClientConfiguration cnf = new ClientConfiguration();
			cnf.withSignerOverride("AWSS3V4SignerType");
			AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withClientConfiguration(cnf)
					.withCredentials(new ProfileCredentialsProvider()).build();

			// Set the presigned URL to expire after one hour.
			java.util.Date expiration = new java.util.Date();
			long expTimeMillis = expiration.getTime();
			expTimeMillis += 1000 * 60 * 60;
			expiration.setTime(expTimeMillis);

			// Generate the presigned URL.
			System.out.println("Generating pre-signed URL.");
			GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName,
					keyName).withMethod(HttpMethod.PUT).withExpiration(expiration).withSSECustomerKey(SSE_KEY);

			generatePresignedUrlRequest.setSSECustomerKeyAlgorithm(SSEAlgorithm.AES256);

			URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

			System.out.println("Pre-Signed URL: " + url.toExternalForm());
		} catch (AmazonServiceException e) {
			// The call was transmitted successfully, but Amazon S3 couldn't process
			// it, so it returned an error response.
			e.printStackTrace();
		} catch (SdkClientException e) {
			// Amazon S3 couldn't be contacted for a response, or the client
			// couldn't parse the response from Amazon S3.
			e.printStackTrace();
		}
	}

我尝试遵循https://aws.amazon.com/blogs/developer/generating-amazon-s3-pre-signed-urls-with-sse-c-part-5-finale/

中的代码

我在使用SSE S3的预签名URL时没有问题,我仅遇到SSE-C的问题

我尝试设置默认加密和其他配置,但没有帮助。任何指针都会有很大帮助。

谢谢, AK

2 个答案:

答案 0 :(得分:0)

您不能通过浏览器直接调用预签名的URL

您需要在请求中传递标头

  1. x-amz-server-side-encryption-customer-algorithm

  2. x-amz-server-side-encryption-customer-key

  3. x-amz服务器端加密-客户密钥MD5

请检查文档以获取更多信息 https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html

  

使用预先签名的URL上载新对象,检索现有对象或仅检索对象元数据时,必须在客户端应用程序中提供所有加密标头。

请检查通过Unirest库调用签名URL的示例代码 https://github.com/pavanpawar4591/s3signurlwith-sse-c

public static void getPreSignedURL() throws URISyntaxException {
        java.util.Date expiration = new java.util.Date();
        long expTimeMillis = expiration.getTime();
        expTimeMillis += 1000 * 60 * 60;
        expiration.setTime(expTimeMillis);

        System.out.println("Generating pre-signed URL.");
        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, keyName)
                .withMethod(HttpMethod.GET).withExpiration(expiration).withSSECustomerKey(SSE_KEY);

        // generatePresignedUrlRequest.setContentType("video/mp4");
        generatePresignedUrlRequest.setSSECustomerKeyAlgorithm(SSEAlgorithm.AES256);

        URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

        System.out.println("Pre-Signed URL: " + url.toURI() + " With key: " + SSE_KEY);

        System.out.println("------------------------------");
        //https://aws.amazon.com/blogs/developer/generating-amazon-s3-pre-signed-urls-with-sse-c-part-4/ 
        // refer to above doc
        try {
            HttpResponse<String> response = Unirest.get(url.toURI().toString())
                    .header(Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM, SSEAlgorithm.AES256.getAlgorithm())
                    .header(Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, Base64.encodeAsString(SECRET_KEY.getEncoded()))
                    .header(Headers.SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5,
                            Md5Utils.md5AsBase64(SECRET_KEY.getEncoded()))
                    .header("cache-control", "no-cache").header("postman-token", "d3453c38-1b59-a12e-fd97-dbe2150eadf5")
                    .asString();

            System.out.println(response.getStatus());
            System.out.println(response.getStatusText());
            System.out.println(response.getBody());
        } catch (UnirestException e) {

            e.printStackTrace();
        }

    }

答案 1 :(得分:0)

感谢@pavan

任何需要使用SSE-C的客户端,该客户端都必须能够发送“以下”标头。

enter image description here

使用预签名URL检索现有对象或仅检索对象元数据时,我们需要在客户端应用程序中提供所有加密标头。

  • 对于S3托管或KMS托管的服务器端加密,我们可以生成一个预签名的URL,并将其直接粘贴到浏览器或播放器中。

  • 但是,这对于SSE-C对象不是正确的,因为除了 预先签署的URL,还需要包括HTTP标头 特定于SSE-C对象。因此,您可以使用预签名的URL 只能以编程方式用于SSE-C对象。

上传:

我选择了S3托管密钥,而不是客户提供的客户端密钥。

        FileInputStream fin = new FileInputStream(uploadFileName);

        byte fileContent[] = new byte[(int) uploadFileName.length()];

        // Reads up to certain bytes of data from this input stream into an array of
        // bytes.
        fin.read(fileContent);
        // create string from byte array

        // Specify server-side encryption.
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(fileContent.length);
        objectMetadata.setSSEAlgorithm(ObjectMetadata.AES_256_SERVER_SIDE_ENCRYPTION);
        PutObjectRequest putRequest = new PutObjectRequest(bucketName, keyName,
                new ByteArrayInputStream(fileContent), objectMetadata);

        // Upload the object and check its encryption status.
        PutObjectResult putResult = s3Client.putObject(putRequest);
        System.out.println("Object \"" + keyName + "\" uploaded with SSE.");

获取预签名网址:

    java.util.Date expiration = new java.util.Date();
    long expTimeMillis = expiration.getTime();
    expTimeMillis += 1000 * 60 * 60;
    expiration.setTime(expTimeMillis);

    System.out.println("Generating pre-signed URL.");
    GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, keyName)
            .withMethod(HttpMethod.GET).withExpiration(expiration);

    URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
    System.out.println("Pre-Signed URL: " + url.toURI());

参考: https://docs.aws.amazon.com/AmazonS3/latest/dev/ServerSideEncryptionCustomerKeys.html