1
votes

I am struggling to create signed URL to upload file to Google Storage. First, I am getting private key and clientId using JSON key:

AuthCredentials.ServiceAccountAuthCredentials serviceAccountAuthCredentials = AuthCredentials.ServiceAccountAuthCredentials.createForJson(json_resource.getInputStream());
PrivateKey key = serviceAccountAuthCredentials.credentials().getPrivateKey();
Signature signer = Signature.getInstance("SHA256withRSA");
signer.initSign(key);

After that, creating string to sign:

String upload_uri = "PUT\n\n" + expiration +
                "\n/" + bucketName + "/" + folderPath + "/" + fileName;

Then signing string:

signer.update(stringToSign.getBytes("UTF-8"));
byte[] rawSignature = signer.sign();
String signature = new String(Base64.encodeBase64(rawSignature, false), "UTF-8");

And then composing URL using signed string:

final String clientId = serviceAccountAuthCredentials.account();
String url = "http://storage.googleapis.com/" + 
             bucketName + "/" + folderPath + "/" + fileName +
             "?GoogleAccessId=" + clientId +
             "&Expires=" + expiration +
             "&Signature=" + URLEncoder.encode(signature, "UTF-8");

Using this URL I am getting error:

SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.GET 1485340222074 /bucketName/fileName

2
Could you compare your stringToSign with a canonical example like gsutil on the same URL? For starters, I think you are missing a \n after PUT (there should be one for the method, the md5, and the content type). - Travis Hobrla
Thanks for a suggestion, @TravisHobrla. I tried it - didn't work. I also tried to add contentType - didn't work either. - nkat
Make sure your StringToSign follows the correct format, that your expiration time is properly calculated in Unix Epoch, and that you are signing it as per the guide. Also make sure the client that uses the URL also supplies the correct headers, specifically Content Type that you have set. - Jordan

2 Answers

2
votes

Hello please check the below working code from my side. I feel you are missing some parameters as get or token:`

    Calendar calendar = new GregorianCalendar();
            Long lExpireDate = Long.valueOf(calendar.getTime().getTime() + 7*86400000);//7 days validity
            String strExpireDate = fromDateLongtoStringISO8601(lExpireDate);

            String HTTP_Verb = "GET";
            String Content_MD5 = "";
            String Content_Type = "";//"image/jpeg";
            String Expiration = String.valueOf(lExpireDate/1000);
            String Canonicalized_Resource = "/"+bucketName+"/"+objectName;
            String StringToSign = HTTP_Verb + "\n" +
                    Content_MD5 + "\n" +
                    Content_Type + "\n" +
                    Expiration + "\n" +
                    Canonicalized_Resource;


            byte[] blob = StringToSign.getBytes();
            String BASE_URL = "https://storage.googleapis.com"+Canonicalized_Resource;

            ArrayList<String> scopes = new ArrayList<>();
            scopes.add(SQLAdminScopes.CLOUD_PLATFORM);
            appIdentity = AppIdentityServiceFactory.getAppIdentityService();
            AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);

            AppIdentityService.SigningResult result = appIdentity.signForApp(blob);
            byte[] signatureByte = result.getSignature();
            String urlEncoded = com.google.api.client.util.Base64.encodeBase64String(signatureByte);
            String strURLBase64Encoded = URLEncoder.encode(urlEncoded);

            String GOOGLE_ACCESS_STORAGE_ID = appIdentity.getServiceAccountName();
            //GOOGLE_ACCESS_STORAGE_ID contains the email form of the client ID.

            String concatenatedURL = BASE_URL + "?GoogleAccessId=" + GOOGLE_ACCESS_STORAGE_ID
                    + "&Expires=" + strExpireDate
                    + "&access_token=" + URLEncoder.encode(accessToken.getAccessToken())
                    + "&Signature=" + strURLBase64Encoded;
            entity.setProperty(Media.URLD, concatenatedURL);
0
votes

I found that I had to set the content type in StringToSign to make PUT work. Prior to setting the content type I was receiving the same error message that you got.