INVALID_ARGUMENT response when signingBlob using IamCredentialsClient

Viewed 17

I'm following this google guide on how to sign urls manually since I need to create a signed URL in other to let other user upload files without having to authenticate manually and while I manage to follow all steps required and showed in the python example, my java code always receives an io.grpc.StatusRuntimeException: INVALID_ARGUMENT: Request contains an invalid argument.

I'm getting crazy trying to find what is the issue and I'm not sure if I'm doing it correctly.

The dependency I'm using to signBlob is the google-cloud-iamcredentials. Is there something I'm missing out?

Here you can check the code

try (IamCredentialsClient iamCredentialsClient = IamCredentialsClient.create()){
        final LocalDateTime currentTime = LocalDateTime.now();
        final String completeTimeStamp = currentTime.format(DateTimeFormatter.ofPattern("yyyyMMdd HHmmss"))
                .replace(" ", "T") + 'Z';
        final String pathToResource = "/" + filename;
        final String credentialScope = currentTime.format(DateTimeFormatter.BASIC_ISO_DATE) + "/eu/storage/goog4_request";
        String credential = clientEmail + '/' + credentialScope;
        credential = credential.replace("@", "%40");
        credential = credential.replace("/", "%2F");
        final String canonicalHeader = "host:" + bucketName + ".storage.googleapis.com\n";
        final String signedHeader = "host";
        final String canonicalQueryString =
                "X-Goog-Algorithm=GOOG4-RSA-SHA256&" +
                        "X-Goog-Credential=" + credential + '&' +
                        "X-Goog-Date=" + completeTimeStamp + '&' +
                        "X-Goog-Expires=" + expiration + '&' +
                        "X-Goog-SignedHeaders=" + signedHeader;
        final String canonicalRequest =
                "PUT" + '\n' +
                pathToResource + '\n' +
                canonicalQueryString + '\n' +
                canonicalHeader + '\n' +
                signedHeader + '\n' +
                "UNSIGNED-PAYLOAD";
        final String hashedCanonicalRequest =
                DigestUtils.sha256Hex(canonicalRequest.getBytes(StandardCharsets.UTF_8));
        final String stringToSign = "GOOG4-RSA-SHA256" + '\n' +
                completeTimeStamp + '\n' +
                credentialScope + '\n' +
                hashedCanonicalRequest;
        final String signature = iamCredentialsClient.signBlobCallable()
                .call(
                        SignBlobRequest.newBuilder()
                                .setName(ServiceAccountName.of(projectId, clientEmail).toString())
                                .addAllDelegates(new ArrayList<>())
                                .setPayload(ByteString.copyFrom(stringToSign.getBytes(StandardCharsets.UTF_8)))
                                .build()
                ).toString();
        return String.format(CLOUD_URL, pathToResource,
                canonicalQueryString, "&X-Goog-Signature=".concat(signature));
1 Answers

I just found my issue after a long time debugging and an even longer time having a break from this problem.

Turns out I was creating the ServiceAccountName.of wrong, according to this resource https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob.

You can see under the parameter section this comment

Required. The resource name of the service account for which the credentials are requested, in the following format: projects/-/serviceAccounts/{ACCOUNT_EMAIL_OR_UNIQUEID}. The - wildcard character is required; replacing it with a project ID is invalid.

Meaning my code was not constructing this field in the correct format, I changed ServiceAccountName.of(projectId, clientEmail).toString() to ServiceAccountName.of("-", clientEmail).toString() and it worked!

I leave this as the answer for my question in case anyone in the future encounters with this exact issue.

Related