Convert a clientsecret into a private key

2020-06-29 05:01发布

问题:

I'm working with Google Cloud Storage in AppEngine and I'm attempting to use a POST form to upload a file to GCS. The problem I'm having is with the steps needed to sign the policy document. I can easily fetch the client_secret, which is a String from the client_secrets.json that the API Console gave me. however, in order to create a signature, I need to convert that string into a PrivateKey object. Here's what my code looks like:

//create the policy document and encode it
String policyDocument = ...  //omitted for brevity
String encodedPolicy = Base64.encodeString(policyDocument);

//sign using SHA256 with RSA
String secretKey = ... //I fetch this from client_secrets.json
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(secretKey); //THIS IS THE PROBLEM!  
sig.update(encodedPolicy.getBytes("UTF-8"));        
String signature = new String(Base64.encode(sig.sign()));

//put the values in the request attributes so we can fetch them from a JSP
req.setAttribute("policy", encodedPolicy);
req.setAttribute("signature", signature);

As noted above, my problem is in the line

sig.initSign(secretKey); //THIS IS THE PROBLEM!  

secretKey is a String. Signature.initSign() expects a PrivateKey, or one of its descendant objects. How do I convert the string in the client_secrets.json into a PrivateKey (or derived) object that I can pass Signature.initSign?

Any help would be greatly appreciated. Thanks

OK, here's where I am right now. I tried the suggestions below, and all of the documentation is urging me to use the client_secret in the client_secrets.json file downloaded from the Google API console, not the service account. And besides, I'm trying to construct an example of a user's upload, not a service account.

I found the following code on another page:

public static String signPolicyDocument(String policyDocument, String secret) {     
try {
        Mac mac = Mac.getInstance("HmacSHA256");
        byte[] secretBytes = secret.getBytes();
        SecretKeySpec signingKey = new SecretKeySpec(secretBytes, "HmacSHA256");
        mac.init(signingKey);
        byte[] signedSecretBytes = mac.doFinal(policyDocument.getBytes());          
        return new String(Base64.encode(signedSecretBytes));
    } catch (InvalidKeyException e) {
        throw new RuntimeException(e);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }

And it gets me all the way through the process...until I submit the resulting form. Then I get the following response:

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

What signing method is it looking for?

回答1:

To upload a file to GCS from Appengine you can use the blobstore Api. Follow the steps described in Using Blobstore with GCS. The advantage is that you don't have to worry about keys and the code is much simpler.



回答2:

Here's what I think you need to do:

KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
sig.initSign(privateKey);

The keyBytes variable should contain a byte[] array with your service account key file in it.



回答3:

The final answer to this problem is like the conclusion of Wargames. As WOPR said, "A strange game...the only way to win is not to play." Avoid signing and policy document and all that crap and use the blobstore.

(See this: https://developers.google.com/appengine/docs/java/blobstore/overview#using-blobstore-with-gcs)

It's very easy to implement; when you create your temporary blobstore upload URL like so:

    //open the blobstore service and create the upload url
    BlobstoreService bs = BlobstoreServiceFactory.getBlobstoreService();        
    String uploadUrl = bs.createUploadUrl("/display",
            UploadOptions.Builder.withGoogleStorageBucketName(bucket));

The downside to this approach is the object name will be a string of characters you don't recognize. You can open the blobstore viewer and see your object by file name in the blobstore, but in GCS its object name will be gobbledygook. (A hash, maybe? A randomly assigned ID)?