Skip to content

Instantly share code, notes, and snippets.

@andreban
Last active September 4, 2017 10:41
Show Gist options
  • Save andreban/e0e0a47c7baf467580dbf1d8f4377618 to your computer and use it in GitHub Desktop.
Save andreban/e0e0a47c7baf467580dbf1d8f4377618 to your computer and use it in GitHub Desktop.
Signing an AMP update-cache URL in Java
/**
* Copyright 2015-2016, Google, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.amp.ssr;
import org.bouncycastle.jce.provider.JCERSAPublicKey;
import org.bouncycastle.openssl.PEMReader;
import java.io.StringReader;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.Base64;
/**
* This sample uses the BouncyCastle library (https://www.bouncycastle.org/java.html) to generate and verify the
* signatures
*/
public class UpdateCache {
static {
java.security.Security.addProvider(
new org.bouncycastle.jce.provider.BouncyCastleProvider()
);
}
private static final String PRIVATE_KEY =
"-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIEowIBAAKCAQEAz28TyldF6N73GAcoDzJwkn5G1Ar6jGcls98L+Exyq3Yt9Gcq\n" +
"ZPRkslNjK9/HsgvepLp+Le4LdhJqnmdTsTz9xiDk2tAEXvBWo3kfEXMUY5UVVWzM\n" +
"3cQNfL4vEpHx3UneHsOeu9qr7L/r6sQdhWbwVKM+8q4oWgx1MjP3ZBeQBuThzAGJ\n" +
"F25egnOPYGI1aCLU2AXGeFoDUBsWbR/BE02EL06vZ0jdhowocJbUtnu8hnn6lzSp\n" +
"nRZD7ZmRbCY9MPYwWG++DgCOYYB12xSrAdX2B3+0yYcCyL/44whrJ51eEa1rcnvR\n" +
"Z3k5ASYxgy8rWyGy1EGOtx3ihMPZoL32ZK4eTwIDAQABAoIBADdCnSATXeFqvKLf\n" +
"DTH3OvwbvdyxTAvpUqb3baXpH8dDxXxNm4KnhMfo5QuEIxvwg5CYyhKMTutWfqbS\n" +
"Wf5mhCirDKyqK+/q0Pndw4QPh0f8KGHIiU8u8nTf6YRl8uwDyLOjY38iAxkgC8ZQ\n" +
"Fpk6Uh4KGzPlZ4SNuw7zvx0gFdNv3VmSKEtvLoXUIAGZ34Jc9oSoADjn0SS1BjrB\n" +
"SxPckPHz6336fhHvSRBdRn42lZ+P1i0oF56oUsirHGEdLe7yokUny23BH0IjOeT7\n" +
"TlNnJi19FQmsOV1/guoy4UPnN+ioYzz8PsyoHQ1ZZrPRRfWqN8rXtwGtUY9CZ6fo\n" +
"DGxXLkECgYEA6QONoQhH/T8lovTlG8k/eZxdts0qfm52WBoKSr9HPgfuFbLGHMLN\n" +
"e57q3JzF0Kvpusv6nKGAU2aMN5fbQsW9Tn4Y29NyKDn9528OlNCrdGHRfn3cuuNx\n" +
"Ya3OpHttH49a0PsJ5FjAfbYgZkOC+jdCNYA8YaaXHEqnO5PX2PWi9u8CgYEA4+WJ\n" +
"XD1RBTV7f5D7yT/skQHvg/JyZVz0zc/UDal4eMM7RtivNdPAZAIRsBzrsVE1iJSp\n" +
"fgIcjauUPAQovVTLVRPF6t8u+PhrlD13Ty0rtwaKPHjlfTqWn6DxSllOd5u6qu11\n" +
"tlJ+szT+BZbb1ca/BOaVWBgwvlRyaVFkzD1MTqECgYEAz6KR7v/xHlLJ1+b/zX3M\n" +
"aTKUM5OpHRaTsDgsGaVO95CbcIxoizJvZowa3tF4WFUIpqKjRDWJo0fLBLL1+A86\n" +
"fJpSznghzoS2oydMSAGV9tQ/mMbRDVwlKZR2gg1WFPxqQbShgPAxPxQu1NGuAgeB\n" +
"oSkPJTkMiHJhiO5aNgPU3tMCgYBQLYRbCFOF4qAVbFu5HR1MkwLCSD0I/mA+PH0d\n" +
"ZV82FXG4ZimWZW5NgRDaBgLB+pE8ARl0dKe5DAF17/ypR7jVsMQz8ttiynuGxu0/\n" +
"1OOJW+qshdgva7psZMIfZzYdDJPq6Oo9/94FjIJGUyH6nxa7Uah/OtuDStCJzxII\n" +
"bRR4oQKBgHDm98hlGTGvIIkK3ZU3Tcxjg0OxtlmsDsA2DQqjTxgWrk7+x9sX7sos\n" +
"kt/entRQs/FwiuXfvzfVc2RnqJ0P0F4zevbR+55rax3nv1fWnUhTm698/lh3Kfpp\n" +
"C8iytkhnemDhNLoBYVaO1tPRSXkyelek+8s3HeKe86Qvc1tbhNV9\n" +
"-----END RSA PRIVATE KEY-----";
private static final String PUBLIC =
"-----BEGIN PUBLIC KEY-----\n" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz28TyldF6N73GAcoDzJw\n" +
"kn5G1Ar6jGcls98L+Exyq3Yt9GcqZPRkslNjK9/HsgvepLp+Le4LdhJqnmdTsTz9\n" +
"xiDk2tAEXvBWo3kfEXMUY5UVVWzM3cQNfL4vEpHx3UneHsOeu9qr7L/r6sQdhWbw\n" +
"VKM+8q4oWgx1MjP3ZBeQBuThzAGJF25egnOPYGI1aCLU2AXGeFoDUBsWbR/BE02E\n" +
"L06vZ0jdhowocJbUtnu8hnn6lzSpnRZD7ZmRbCY9MPYwWG++DgCOYYB12xSrAdX2\n" +
"B3+0yYcCyL/44whrJ51eEa1rcnvRZ3k5ASYxgy8rWyGy1EGOtx3ihMPZoL32ZK4e\n" +
"TwIDAQAB\n" +
"-----END PUBLIC KEY-----";
public static PublicKey getPublicKeyFromString(String key) throws Exception {
PEMReader pemReader = new PEMReader(new StringReader(key));
return (JCERSAPublicKey)pemReader.readObject();
}
public static PrivateKey getPrivateKeyFromString(String key) throws Exception {
PEMReader pemReader = new PEMReader(new StringReader(key));
KeyPair keyPair = (KeyPair)pemReader.readObject();
return keyPair.getPrivate();
}
public static byte[] generateSignature(PrivateKey privateKey, String input) throws Exception{
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(privateKey);
privateSignature.update(input.getBytes("utf-8"));
return privateSignature.sign();
}
public static boolean verifySignature(PublicKey publicKey, String input, byte[] signature) throws Exception {
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(publicKey);
sig.update(input.getBytes("utf-8"));
return sig.verify(signature);
}
private static final String AMP_CACHE_DOMAIN = "https://limitless--tundra--65881-herokuapp-com.cdn.ampproject.org";
private static final String URL_TEMPLATE =
"/update-cache/c/s/limitless-tundra-65881.herokuapp.com/amp-access/sample/0?amp_action=flush&amp_ts=%d";
private static final String SIGNATURE_PARAM = "&amp_url_signature=";
private static final int ONE_SECOND_IN_MILLIS = 1000;
public static void main(String[] args) throws Exception {
// the value of `amp_ts` should be the current time in seconds, so we need to divide by 1000
long ampTimestamp = System.currentTimeMillis() / ONE_SECOND_IN_MILLIS;
// This is the part of the url that needs to be signed.
String signaturePath = String.format(URL_TEMPLATE, ampTimestamp);
// Sign the input
PrivateKey privateKey = getPrivateKeyFromString(PRIVATE_KEY);
byte[] signature = generateSignature(privateKey, signaturePath);
// Use `Base64.getUrlEncoder()` to get Base64url encoder.
String base64UrlEncodedSig = Base64.getUrlEncoder().encodeToString(signature);
System.out.println(AMP_CACHE_DOMAIN + signaturePath + SIGNATURE_PARAM + base64UrlEncodedSig);
// Check if Signature matches
PublicKey publicKey = getPublicKeyFromString(PUBLIC);
System.out.println("Signature Matches: " + verifySignature(publicKey, signaturePath, signature));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment