diff options
author | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2019-04-29 15:30:41 +0200 |
---|---|---|
committer | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2019-04-29 15:30:41 +0200 |
commit | e74ed697f8e4e60e9b86f9472c2161a32d74658f (patch) | |
tree | 6b348e1423ec6fa3d1aa9b05345867b948d40529 /hosted-api | |
parent | a2f7ac956ef2785bb4980872e9dbdc7ef61bdd10 (diff) |
Add crypto utilities for HTTP request signatures
Diffstat (limited to 'hosted-api')
-rw-r--r-- | hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java b/hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java new file mode 100644 index 00000000000..17fd7012880 --- /dev/null +++ b/hosted-api/src/main/java/ai/vespa/hosted/api/Signatures.java @@ -0,0 +1,123 @@ +package ai.vespa.hosted.api; + +import javax.crypto.Cipher; +import java.io.InputStream; +import java.net.URI; +import java.security.DigestInputStream; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.concurrent.Callable; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class Signatures { + + /** Reads the PEM formatted X509 encoded RSA public key from the given key data. */ + public static PublicKey parsePublicPemX509RsaKey(String publicKey) { + try { + byte[] encodedKey = readKey(publicKey, "-----BEGIN PUBLIC KEY-----\n", "-----END PUBLIC KEY-----"); + return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(encodedKey)); + } + catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + catch (InvalidKeySpecException e) { + throw new IllegalArgumentException(e); + } + } + + /** Reads the PEM formatted PKCS8 encoded RSA private key from the given key data. */ + public static PrivateKey parsePrivatePemPkcs8RsaKey(String privateKey) { + try { + byte[] encodedKey = readKey(privateKey, "-----BEGIN PRIVATE KEY-----\n", "-----END PRIVATE KEY-----"); + return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(encodedKey)); + } + catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + catch (InvalidKeySpecException e) { + throw new IllegalArgumentException(e); + } + } + + /** Returns the data encrypted with the given key. */ + public static byte[] encrypted(byte[] data, Key key) { + try { + Cipher rsaEncrypter = Cipher.getInstance("RSA"); + rsaEncrypter.init(Cipher.ENCRYPT_MODE, key); + return rsaEncrypter.doFinal(data); + } + catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + catch (GeneralSecurityException e) { + throw new IllegalArgumentException(e); + } + } + + /** Returns the data decrypted with the given key. */ + public static byte[] decrypted(byte[] data, Key key) { + try { + Cipher rsaDecrypter = Cipher.getInstance("RSA"); + rsaDecrypter.init(Cipher.DECRYPT_MODE, key); + return rsaDecrypter.doFinal(data); + } + catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + catch (GeneralSecurityException e) { + throw new IllegalArgumentException(e); + } + } + + /** Returns the SHA-256 hash of the content in the implied input stream, consuming it in the process. */ + public static byte[] sha256Digest(Callable<InputStream> in) { + try (DigestInputStream digester = sha256Digester(in.call())) { + byte[] buffer = new byte[1 << 10]; + while (digester.read(buffer) != -1); // Consume the stream to compute the digest. + + return digester.getMessageDigest().digest(); + } + catch (IllegalStateException e) { + throw e; + } + catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + /** Wraps the given input stream in a digester which computes a SHA 256 hash of the contents read through it. */ + public static DigestInputStream sha256Digester(InputStream in) { + try { + return new DigestInputStream(in, MessageDigest.getInstance("SHA-256")); + } + catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + } + + /** Returns a canonical representation of the given request data. */ + public static byte[] canonicalMessageOf(String method, URI requestUri, String timestamp, String hash) { + return (method + "\n" + requestUri.normalize() + "\n" + timestamp + "\n" + hash).getBytes(UTF_8); + } + + /** Reads and Base64-decodes the key data from the given PEM formatted key data. */ + private static byte[] readKey(String pemKey, String start, String end) { + int keyStart = pemKey.indexOf(start) + start.length(); + int keyEnd = pemKey.indexOf(end); + if (keyEnd < keyStart) + throw new IllegalArgumentException("No key found on the form:\n" + start + "<key data>\n" + end); + + return Base64.getMimeDecoder().decode(pemKey.substring(keyStart, keyEnd).getBytes()); + } + +} |