0
votes

We have an ERP application running on GCP . For downloading data spanning more than three months or so ,we're uploading a file on GCS. Now i want to create a signed url so that to give limited access to the end users . I have been trying this. But i get this error : Signature does not match. Please check your Google secret key. Can anyone tell how to go about this?

private static final int EXPIRATION_TIME = 5;
private static final String BASE_URL = "https://storage.googleapis.com";
private static final  String httpVerb = "GET";
/*
 * private static final String BUCKET = "my_bucket"; private static final String
 * FOLDER = "folder";
 */
private final AppIdentityService identityService = AppIdentityServiceFactory.getAppIdentityService();

public String getSignedUrl(String bucket, final String fileName, String contentTpe) throws Exception {
    final long expiration = expiration();
    final String unsigned = stringToSign(bucket, expiration, fileName, contentTpe);
    final String signature = sign(unsigned);

    return new StringBuilder(BASE_URL).append("/").append(bucket).append("/").append(fileName)
            .append("?GoogleAccessId=").append(clientId()).append("&Expires=").append(expiration)
            .append("&Signature=").append(URLEncoder.encode(signature, "UTF-8")).toString();
}

private static long expiration() {
    final long unitMil = 1000l;
    final Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, EXPIRATION_TIME);
    final long expiration = calendar.getTimeInMillis() / unitMil;
    return expiration;
}

private String stringToSign(String bucket, final long expiration, String filename, String contentType) {
    final String contentMD5 = "";
    final String canonicalizedExtensionHeaders = "";
    final String canonicalizedResource = "/" + bucket + "/" + filename;      
    final String stringToSign = httpVerb + "\n"+ contentMD5 + "\n" + contentType + "\n" + expiration + "\n"
            + canonicalizedExtensionHeaders + canonicalizedResource;
    return stringToSign;
}

protected String sign(final String stringToSign) throws UnsupportedEncodingException {
    final SigningResult signingResult = identityService.signForApp(stringToSign.getBytes());
    final String encodedSignature = new String(Base64.encodeBase64(signingResult.getSignature()), "UTF-8");
    return encodedSignature;
}

protected String clientId() {
    return identityService.getServiceAccountName();
}
1

1 Answers

0
votes

URL signing code is a bit tricky because by its nature it can be difficult to know what you've gotten wrong, other than just seeing that it's wrong. There are a few general tips that make it easier:

First, if possible, consider using URL signing functions in the google-cloud libraries. For example, the Java google-cloud library provides a Storage.signURL method, and you can use it like this:

URL signedUrl = storage.signUrl(
    BlobInfo.newBuilder(bucketName, blobName).build(),
    2, TimeUnit.DAYS);

Second, if you look at the error message, you'll notice that there's a <StringToSign> section. This section contains the exact string that GCS would calculate a signature for. Make sure that the string you're signing matches this string exactly. If it doesn't, that's your problem.

In your code's particular case, I didn't find the problem, but it might be that you're including a content-type line when signing the string, but GET requests don't provide a Content-Type header. It's just an idea, though, since I don't see your invocation of getSignedUrl.