Search code examples
javagoogle-cloud-storagespring-cloud-gcp

Spring GCP Storage - The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method


I'm uploading a file to Google Storage and try to generate a public link:

    Storage.BlobWriteOption precondition;
    Blob blob = storage.get(bucketName, name);
    if (blob == null) {
      precondition = Storage.BlobWriteOption.doesNotExist();
    } else {
      precondition = Storage.BlobWriteOption.generationMatch(blob.getGeneration());
    }

    Blob rs = storage.createFrom(BlobInfo.newBuilder(BlobId.of(bucket.getName(), name)).setContentType("application/octet-stream").build(), inputStream, precondition)

    BlobId blobId = rs.getBlobId();
    URL url =
        storage.signUrl(
            BlobInfo.newBuilder(blobId).build(),
            Duration.ofHours(1).toMillis() - 10000,
            TimeUnit.MILLISECONDS,
            Storage.SignUrlOption.httpMethod(HttpMethod.GET),
            Storage.SignUrlOption.withExtHeaders(Map.of("Content-Type", "application/octet-stream")));

I got a link like https://storage.googleapis.com/vocab-be-local/vocab.zip?GoogleAccessId=my-account@vocab-local.iam.gserviceaccount.com&Expires=1715751607&Signature=Vz2G8qr%2Fy%2BN8vR7yiPIQlnpzyaz2s5qGLcTL4nE7w3B6gXprtODGzz8Dk49v%2BMbrCg6IcHi1ZGN9ZNYVZyM7VW3RUZ6QOOIPbA62AEzM1T9UUYIaxGYkE7xGCFC3a6LBv%2FCnN6F28eau3wdGpldwC5iwccrwSYIpozK43Whklk%2B%2BHekjq%2BgnI%2F%2FnuqBWa1Z9P8RBejlz9ajnaxriaFOUyvlBLQBShnxM4q%2BdAJWeFlocMZIk7ZSiGuh1gTi2HTecxzod9bIRu%2FmhecpMlmPP2PvQSW0dqxMuonfxxxj3Eby7hRXOy6wNKSWU4DqZkUJm29AHOyO1vqgc2KaQevbWdw%3D%3D

When I use this link via Postman or curl, I got:

<?xml version='1.0' encoding='UTF-8'?>
<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>Access denied.</Message>
    <Details>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Details>
    <StringToSign>GET

application/octet-stream
1715751607
/vocab-be-local/vocab.zip</StringToSign>
</Error>

Tried with Webflux WebClient, still got same error:

WebClient.create()
    .get().uri(d.getImageUrl())
    .header("Content-Type", "application/octet-stream")
    .retrieve()
    .toEntity(byte[].class)
    .block()

I have read a lot of questions/answers related to this issue. They suggest that I need to add content-type when uploading/signing. I did it, but no luck. Please help!!

UPDATE:

Spring boot 3.2.0
Spring cloud 2023.0.1
Spring cloud GCP 5.1.2

My pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-storage</artifactId>
</dependency>

My application.yml:

spring:
  cloud:
    gcp:
      credentials:
        location: classpath:google/sa/vocab-storage-local.json
      storage:
        project-id: my-project

My service account JSON:

{
  "type": "service_account",
  "project_id": "my-project",
  "private_key_id": "***********",
  "private_key": "-----BEGIN PRIVATE KEY-----\n********\n-----END PRIVATE KEY-----\n",
  "client_email": "******@******.iam.gserviceaccount.com",
  "client_id": "*******************",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/********",
  "universe_domain": "googleapis.com"
}

Solution

  • Fixed:

      storage.signUrl(BlobInfo.newBuilder(blob.getBlobId())
    -> Add this line    .setContentType("application/octet-stream")
                        .build(),
                      32515291723000L - currentTimeMillis(),
                      TimeUnit.MILLISECONDS,
                      Storage.SignUrlOption.httpMethod(HttpMethod.GET),
    -> Fix this line  Storage.SignUrlOption.withContentType()))