GAE应用程序需要创建过期签名的URL到Google云端存储 [英] GAE Application needing to create Expiring Signed URL's to Google Cloud Storage

查看:73
本文介绍了GAE应用程序需要创建过期签名的URL到Google云端存储的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我成功地创建了一个独立的Java应用程序,用于创建即将签名的URL到位于Google云端存储中的资产。但是,我一直未能弄清楚如何通过AppEngine创建到期签名的URL到这些相同的资产。



如何创建到Google云存储资产的过期签名URL我可以返回到客户端应用程序?



这是我的Java应用程序:

  import java.io.FileInputStream; 
import java.io.IOException;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.UnrecoverableKeyException;
import java.util.Calendar;

import org.apache.commons.codec.binary.Base64;

public class GCSSignedURL {

public static void main(String [] args)throws Exception {

最终字符串googleAccessId =XXXXXXXXXXXX @ developer。 gserviceaccount.com;
final String keyFile =D:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXX-privatekey.p12;
final String keyPassword =notasecret;
日历日历= Calendar.getInstance();
calendar.add(Calendar.MINUTE,30);
String httpVerb =GET;
String contentMD5 =;
String contentType =;
long expiration = calendar.getTimeInMillis();
String canonicalizedExtensionHeaders =;
String canonicalizedResource =/myproj/foo.txt;

String stringToSign =
httpVerb +\\\
+
contentMD5 +\\\
+
contentType +\\\
+
expiration +\\\
+
canonicalizedExtensionHeaders +
canonicalizedResource;

PrivateKey pkcsKey = loadKeyFromPkcs12(keyFile,keyPassword.toCharArray());
字符串签名= signData(pkcsKey,stringToSign);
String baseURL =https://storage.cloud.google.com/myproj/foo.txt;
字符串urlEncodedSignature = URLEncoder.encode(签名,UTF-8);
String url = baseURL +?GoogleAccessId =+ googleAccessId +& Expires =+ expiration +& Signature =+ urlEncodedSignature;

System.out.println(url);
}

私有静态PrivateKey loadKeyFromPkcs12(字符串文件名,字符[]密码)
抛出异常{
FileInputStream fis = new FileInputStream(filename);
KeyStore ks = KeyStore.getInstance(PKCS12);
尝试{
ks.load(fis,password); (e.getCause()!= null
&&& e.getCause()instanceof UnrecoverableKeyException){
System.err。
} catch(IOException e){
if println(不正确的密码);
}
throw e;
}
return(PrivateKey)ks.getKey(privatekey,password);

$ b private static String signData(PrivateKey key,String data)
throws Exception {
Signature signer = Signature.getInstance(SHA256withRSA);
signer.initSign(key);
signer.update(data.getBytes(UTF-8));
byte [] rawSignature = signer.sign();
String encodedSignature = new String(Base64.encodeBase64(rawSignature,
false),UTF-8);
返回encodedSignature;
}

}

这是我尝试过的:

  public String signUrl(Long _songId,String _format)throws ResourceNotFoundException 
{
final String googleAccessId = XXXXXXXXXXXXXXXXXX@developer.gserviceaccount.com;

AppIdentityService服务= AppIdentityServiceFactory.getAppIdentityService();

日历日历= Calendar.getInstance();
calendar.add(Calendar.MINUTE,5);
String httpVerb =GET;
String contentMD5 =;
String contentType =;
long expiration = calendar.getTimeInMillis();
String canonicalizedExtensionHeaders =;
String canonicalizedResource =/myproj/foo.txt;
string stringToSign =
httpVerb +\\\
+
contentMD5 +\\\
+
contentType +\\\
+
expiration + \\\
+
canonicalizedExtensionHeaders +
canonicalizedResource;

SigningResult key = service.signForApp(stringToSign.getBytes());
String baseURL =https://storage.cloud.google.com/myproj/foo.txt;
String encodedUrl = baseURL +?GoogleAccessId =+ googleAccessId +& Expires =+ expiration
+& Signature =+ key.getKeyName();

return encodedUrl;
}

结果是一个到期的URL,但要求我使用我的Google电子邮件/密码,因此签名无法正常工作。



我能够使用Fabio的建议最终生成一个编码的URL,但是,现在我得到了:

 <?xml version =1.0encoding =UTF-8?> 
- <错误><代码> SignatureDoesNotMatch< / Code><消息>我们计算出
的请求签名与您提供的签名不匹配。检查您的Google密钥并签署
方法。< / Message>
< StringToSign>
GET 1374729586 /[my_bucket]/[my_folder]/file.png</StringToSign>
< / Error>

我用来生成网址的代码是:

  AppIdentityService服务= AppIdentityServiceFactory.getAppIdentityService(); 
最终字符串googleAccessId = service.getServiceAccountName();
String url = songUrl(_songId,_format);
日历日历= Calendar.getInstance();
calendar.add(Calendar.MINUTE,10);
String httpVerb =GET;
String contentMD5 =;
String contentType =;
long expiration = calendar.getTimeInMillis()/ 1000L;
String canonicalizedExtensionHeaders =;
String canonicalizedResource =/[my_bucket]/[my_folder]/file.png;
string stringToSign =
httpVerb +\\\
+
contentMD5 +\\\
+
contentType +\\\
+
expiration + \\\
+
canonicalizedExtensionHeaders +
canonicalizedResource;

尝试
{
String baseURL = http:// [my_bucket] .commondatastorage.googleapis.com / [my_folder] /file.png;
SigningResult signingResult = service.signForApp(stringToSign.getBytes());
String encodedSignature = new String(Base64.encodeBase64(signingResult.getSignature(),false),UTF-8);
String encodedUrl = baseURL +?GoogleAccessId =+ googleAccessId +& Expires =+ expiration
+& Signature =+ encodedSignature;

return encodedUrl;

catch(UnsupportedEncodingException e)
{
抛出新的ResourceNotFoundException(无法编码URL。不支持的编码异常。,e);
}


解决方案

>

用于 googleAccessId 使用:

 字符串googleAccessId = service.getServiceAccountName(); 

以及签名使用:

  SigningResult signingResult = service 
.signForApp(stringToSign.getBytes());
String encodedSignature = new String(Base64.encodeBase64(
signingResult.getSignature(),false),UTF-8);

这对我来说很有用。
见下面的示例签名类:

  public class GcsAppIdentityServiceUrlSigner {

private static final int EXPIRATION_TIME = 5;
private static final String BASE_URL =https://storage.googleapis.com;
private static final String BUCKET =my_bucket;
private static final String FOLDER =folder;


private final AppIdentityService identityService = AppIdentityServiceFactory.getAppIdentityService();

public String getSignedUrl(final String httpVerb,final String fileName)throws Exception {
final long expiration = expiration();
final String unsigned = stringToSign(expiration,fileName,httpVerb);
final字符串签名=符号(无符号);

返回新的StringBuilder(BASE_URL).append(/)
.append(BUCKET)
.append(/)
.append(FOLDER)
.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;
最终日历日历= Calendar.getInstance();
calendar.add(Calendar.MINUTE,EXPIRATION_TIME);
final long expiration = calendar.getTimeInMillis()/ unitMil;
回报到期;
}

private String stringToSign(final long expiration,String filename,String httpVerb){
final String contentType =;
final String contentMD5 =;
final String canonicalizedExtensionHeaders =;
final String canonicalizedResource =/+ BUCKET +/+ FOLDER +/+ filename;
final String stringToSign = httpVerb +\\\
+ contentMD5 +\\\
+ contentType +\\\

+ expiration +\\\
+ canonicalizedExtensionHeaders + canonicalizedResource;
返回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(),false),UTF-8);
返回encodedSignature;


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


I was successfully able to create a stand-alone Java Application that creates Expiring Signed URL's to assets sitting in Google Cloud Storage. However, I have been unsuccessful in figuring out how to create Expiring Signed URL's to these same assets through AppEngine.

How can I create a Expiring Signed URL to Google Cloud Storage Assets that I can return to client applications?

Here is my Java Application that works:

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.UnrecoverableKeyException;
import java.util.Calendar;

import org.apache.commons.codec.binary.Base64;

public class GCSSignedURL {

public static void main(String[] args) throws Exception {

    final String googleAccessId = "XXXXXXXXXXXX@developer.gserviceaccount.com";
    final String keyFile = "D:\\XXXXXXXXXXXXXXXXXXXXXXXXXXXXX-privatekey.p12";
    final  String keyPassword = "notasecret";
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, 30);
    String httpVerb = "GET";
    String contentMD5 = "";         
    String contentType = "";
    long expiration = calendar.getTimeInMillis();
    String canonicalizedExtensionHeaders = ""; 
    String canonicalizedResource = "/myproj/foo.txt";

    String stringToSign = 
            httpVerb + "\n" + 
            contentMD5 + "\n" + 
            contentType + "\n" + 
            expiration + "\n" + 
            canonicalizedExtensionHeaders + 
            canonicalizedResource;

    PrivateKey pkcsKey = loadKeyFromPkcs12(keyFile, keyPassword.toCharArray());
    String signature = signData(pkcsKey, stringToSign);     
    String baseURL = "https://storage.cloud.google.com/myproj/foo.txt";     
    String urlEncodedSignature = URLEncoder.encode(signature, "UTF-8");
    String url = baseURL + "?GoogleAccessId=" + googleAccessId + "&Expires=" + expiration + "&Signature=" + urlEncodedSignature;

    System.out.println(url);
}

private static PrivateKey loadKeyFromPkcs12(String filename, char[] password)
        throws Exception {
    FileInputStream fis = new FileInputStream(filename);
    KeyStore ks = KeyStore.getInstance("PKCS12");
    try {
        ks.load(fis, password);
    } catch (IOException e) {
        if (e.getCause() != null
                && e.getCause() instanceof UnrecoverableKeyException) {
            System.err.println("Incorrect password");
        }
        throw e;
    }
    return (PrivateKey) ks.getKey("privatekey", password);
}

private static String signData(PrivateKey key, String data)
        throws Exception {
    Signature signer = Signature.getInstance("SHA256withRSA");
    signer.initSign(key);
    signer.update(data.getBytes("UTF-8"));
    byte[] rawSignature = signer.sign();
    String encodedSignature = new String(Base64.encodeBase64(rawSignature,
            false), "UTF-8");
    return encodedSignature;
}

}

Here is what I have tried:

public String signUrl(Long _songId, String _format) throws ResourceNotFoundException
{
    final String googleAccessId = "XXXXXXXXXXXXXXXXXX@developer.gserviceaccount.com";

    AppIdentityService service = AppIdentityServiceFactory.getAppIdentityService();

    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, 5);       
    String httpVerb = "GET";
    String contentMD5 = "";
    String contentType = "";
    long expiration = calendar.getTimeInMillis();       
    String canonicalizedExtensionHeaders = "";      
    String canonicalizedResource = "/myproj/foo.txt";
    String stringToSign = 
            httpVerb + "\n" + 
            contentMD5 + "\n" + 
            contentType + "\n" + 
            expiration + "\n" + 
            canonicalizedExtensionHeaders + 
            canonicalizedResource;  

    SigningResult key = service.signForApp(stringToSign.getBytes());
    String baseURL = "https://storage.cloud.google.com/myproj/foo.txt";
    String encodedUrl = baseURL + "?GoogleAccessId=" + googleAccessId + "&Expires=" + expiration
            + "&Signature=" + key.getKeyName();

    return encodedUrl;
}

The result is an expiring URL but requires me to authenticate with my google email / password so the signing isn't working properly.

I was able to finally generate an encoded URL using Fabio's suggestion, however, I now get:

<?xml version="1.0" encoding="UTF-8"?>
-<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated 
does not match the signature you provided. Check your Google secret key and signing 
method.</Message>
<StringToSign>
GET 1374729586 /[my_bucket]/[my_folder]/file.png</StringToSign>    
</Error>

The code I am using to generate the URL is:

AppIdentityService service = AppIdentityServiceFactory.getAppIdentityService();
    final String googleAccessId = service.getServiceAccountName();
    String url = songUrl(_songId, _format);  
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, 10);
    String httpVerb = "GET";
    String contentMD5 = ""; 
    String contentType = ""; 
    long expiration = calendar.getTimeInMillis()/1000L;
    String canonicalizedExtensionHeaders = "";      
    String canonicalizedResource = "/[my_bucket]/[my_folder]/file.png";
    String stringToSign = 
            httpVerb + "\n" + 
            contentMD5 + "\n" + 
            contentType + "\n" + 
            expiration + "\n" + 
            canonicalizedExtensionHeaders + 
            canonicalizedResource;

    try 
    {
        String baseURL = http://[my_bucket].commondatastorage.googleapis.com/[my_folder]/file.png;  
        SigningResult signingResult = service.signForApp(stringToSign.getBytes());
        String encodedSignature = new String(Base64.encodeBase64(signingResult.getSignature(), false), "UTF-8");
        String encodedUrl = baseURL + "?GoogleAccessId=" + googleAccessId + "&Expires=" + expiration
                + "&Signature=" + encodedSignature;

        return encodedUrl;
    } 
    catch (UnsupportedEncodingException e) 
    {
        throw new ResourceNotFoundException("Unable to encode URL.  Unsupported encoding exception.", e);
    }       

解决方案

Two things:

For googleAccessId use:

String googleAccessId = service.getServiceAccountName();

And for Signature use:

SigningResult signingResult = service
            .signForApp(stringToSign.getBytes());
String encodedSignature = new String(Base64.encodeBase64(
            signingResult.getSignature(), false), "UTF-8");

That's what worked for me. See below a sample signer class:

public class GcsAppIdentityServiceUrlSigner  {

    private static final int EXPIRATION_TIME = 5;
    private static final String BASE_URL = "https://storage.googleapis.com";
    private static final String BUCKET = "my_bucket";
    private static final String FOLDER = "folder";


    private final AppIdentityService identityService = AppIdentityServiceFactory.getAppIdentityService();

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

        return new StringBuilder(BASE_URL).append("/")
                .append(BUCKET)
                .append("/")
                .append(FOLDER)
                .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(final long expiration, String filename, String httpVerb) {
        final String contentType = "";
        final String contentMD5 = "";
        final String canonicalizedExtensionHeaders = "";
        final String canonicalizedResource = "/" + BUCKET + "/" + FOLDER + "/" + 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(), false), "UTF-8");
        return encodedSignature;
    }

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

这篇关于GAE应用程序需要创建过期签名的URL到Google云端存储的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆