Last active
September 4, 2017 10:41
-
-
Save andreban/e0e0a47c7baf467580dbf1d8f4377618 to your computer and use it in GitHub Desktop.
Signing an AMP update-cache URL in Java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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&_ts=%d"; | |
private static final String SIGNATURE_PARAM = "&_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