Skip to content

Commit

Permalink
KMS changes (GoogleCloudPlatform#1218)
Browse files Browse the repository at this point in the history
* use byte[] instead of String in KMS asymmetric
* added import comments
  • Loading branch information
daniel-sanche authored Oct 18, 2018
1 parent 5dbf7be commit 24d202f
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 45 deletions.
80 changes: 54 additions & 26 deletions kms/src/main/java/com/example/Asymmetric.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import com.google.api.services.cloudkms.v1.model.ListKeyRingsResponse;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.MessageDigest;
Expand All @@ -43,7 +42,6 @@
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.BadPaddingException;
Expand All @@ -57,7 +55,18 @@
public class Asymmetric {

// [START kms_get_asymmetric_public]
/** Retrieves the public key from a saved asymmetric key pair on Cloud KMS */
/**
* Retrieves the public key from a saved asymmetric key pair on Cloud KMS
*
* Requires:
* java.io.StringReader
* java.security.KeyFactory
* java.security.PublicKey
* java.security.Security
* java.security.spec.X509EncodedKeySpec
* org.bouncycastle.jce.provider.BouncyCastleProvider
* org.bouncycastle.util.io.pem.PemReader
*/
public static PublicKey getAsymmetricPublicKey(CloudKMS client, String keyPath)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException,
NoSuchProviderException {
Expand Down Expand Up @@ -86,26 +95,32 @@ public static PublicKey getAsymmetricPublicKey(CloudKMS client, String keyPath)
* Decrypt a given ciphertext using an 'RSA_DECRYPT_OAEP_2048_SHA256' private key
* stored on Cloud KMS
*/
public static String decryptRSA(String ciphertext, CloudKMS client, String keyPath)
public static byte[] decryptRSA(byte[] ciphertext, CloudKMS client, String keyPath)
throws IOException {
AsymmetricDecryptRequest request = new AsymmetricDecryptRequest().setCiphertext(ciphertext);
AsymmetricDecryptRequest request = new AsymmetricDecryptRequest().encodeCiphertext(ciphertext);
AsymmetricDecryptResponse response = client.projects()
.locations()
.keyRings()
.cryptoKeys()
.cryptoKeyVersions()
.asymmetricDecrypt(keyPath, request)
.execute();
return new String(response.decodePlaintext());
return response.decodePlaintext();
}
// [END kms_decrypt_rsa]

// [START kms_encrypt_rsa]
/**
* Encrypt message locally using an 'RSA_DECRYPT_OAEP_2048_SHA256' public key
* retrieved from Cloud KMS
* Encrypt data locally using an 'RSA_DECRYPT_OAEP_2048_SHA256' public key
* retrieved from Cloud KMS
*
* Requires:
* java.security.PublicKey
* java.security.Security
* javax.crypto.Cipher
* org.bouncycastle.jce.provider.BouncyCastleProvider
*/
public static String encryptRSA(String message, CloudKMS client, String keyPath)
public static byte[] encryptRSA(byte[] plaintext, CloudKMS client, String keyPath)
throws IOException, IllegalBlockSizeException, NoSuchPaddingException,
InvalidKeySpecException, NoSuchProviderException, BadPaddingException,
NoSuchAlgorithmException, InvalidKeyException {
Expand All @@ -114,20 +129,23 @@ public static String encryptRSA(String message, CloudKMS client, String keyPath)

Cipher cipher = Cipher.getInstance("RSA/NONE/OAEPWITHSHA256ANDMGF1PADDING", "BC");
cipher.init(Cipher.ENCRYPT_MODE, rsaKey);
byte[] ciphertext = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(ciphertext);
return cipher.doFinal(plaintext);
}
// [END kms_encrypt_rsa]

// [START kms_sign_asymmetric]
/** Create a signature for a message using a private key stored on Cloud KMS */
public static String signAsymmetric(String message, CloudKMS client, String keyPath)
/** Create a signature for a message using a private key stored on Cloud KMS
*
* Requires:
* java.security.MessageDigest
* java.util.Base64
*/
public static byte[] signAsymmetric(byte[] message, CloudKMS client, String keyPath)
throws IOException, NoSuchAlgorithmException {
byte[] msgBytes = message.getBytes(StandardCharsets.UTF_8);
Digest digest = new Digest();
// Note: some key algorithms will require a different hash function
// For example, EC_SIGN_P384_SHA384 requires SHA-384
digest.encodeSha256(MessageDigest.getInstance("SHA-256").digest(msgBytes));
digest.encodeSha256(MessageDigest.getInstance("SHA-256").digest(message));

AsymmetricSignRequest signRequest = new AsymmetricSignRequest();
signRequest.setDigest(digest);
Expand All @@ -139,16 +157,22 @@ public static String signAsymmetric(String message, CloudKMS client, String keyP
.cryptoKeyVersions()
.asymmetricSign(keyPath, signRequest)
.execute();
return response.getSignature();
return Base64.getMimeDecoder().decode(response.getSignature());
}
// [END kms_sign_asymmetric]

// [START kms_verify_signature_rsa]
/**
* Verify the validity of an 'RSA_SIGN_PSS_2048_SHA256' signature for the
* specified plaintext message
* specified message
*
* Requires:
* java.security.PublicKey
* java.security.Security
* java.security.Signature
* org.bouncycastle.jce.provider.BouncyCastleProvider
*/
public static boolean verifySignatureRSA(String signature, String message, CloudKMS client,
public static boolean verifySignatureRSA(byte[] signature, byte[] message, CloudKMS client,
String keyPath) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException,
SignatureException, NoSuchProviderException, InvalidKeyException {
Security.addProvider(new BouncyCastleProvider());
Expand All @@ -157,18 +181,23 @@ public static boolean verifySignatureRSA(String signature, String message, Cloud
Signature rsaVerify = Signature.getInstance("SHA256withRSA/PSS");

rsaVerify.initVerify(rsaKey);
rsaVerify.update(message.getBytes(StandardCharsets.UTF_8));
byte[] sigBytes = Base64.getMimeDecoder().decode(signature);
return rsaVerify.verify(sigBytes);
rsaVerify.update(message);
return rsaVerify.verify(signature);
}
// [END kms_verify_signature_rsa]

// [START kms_verify_signature_ec]
/**
* Verify the validity of an 'EC_SIGN_P256_SHA256' signature for the
* specified plaintext message
* specified message
*
* Requires:
* java.security.PublicKey
* java.security.Security
* java.security.Signature
* org.bouncycastle.jce.provider.BouncyCastleProvider
*/
public static boolean verifySignatureEC(String signature, String message, CloudKMS client,
public static boolean verifySignatureEC(byte[] signature, byte[] message, CloudKMS client,
String keyPath) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException,
SignatureException, NoSuchProviderException, InvalidKeyException {
Security.addProvider(new BouncyCastleProvider());
Expand All @@ -177,9 +206,8 @@ public static boolean verifySignatureEC(String signature, String message, CloudK
Signature ecVerify = Signature.getInstance("SHA256withECDSA", "BC");

ecVerify.initVerify(ecKey);
ecVerify.update(message.getBytes(StandardCharsets.UTF_8));
byte[] sigBytes = Base64.getMimeDecoder().decode(signature);
return ecVerify.verify(sigBytes);
ecVerify.update(message);
return ecVerify.verify(signature);
}
// [END kms_verify_signature_ec]

Expand Down
46 changes: 27 additions & 19 deletions kms/src/test/java/com/example/AsymmetricIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,13 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Base64;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -56,6 +59,7 @@ public class AsymmetricIT {
private static final String parent = "projects/" + projectId + "/locations/global";
private static final String keyRing = "kms-asymmetric-sample";
private static final String message = "test message 123";
private static final byte[] message_bytes = message.getBytes(StandardCharsets.UTF_8);

private static final String rsaDecryptId = "rsa-decrypt";
private static final String rsaSignId = "rsa-sign";
Expand Down Expand Up @@ -136,35 +140,39 @@ public void testGetPublicKey() throws Exception {

@Test
public void testRSAEncryptDecrypt() throws Exception {
String ciphertext = Asymmetric.encryptRSA(message, client, rsaDecrypt);
assertEquals("incorrect RSA ciphertext length.", 344, ciphertext.length());
assertEquals("incorrect ciphertext final character.", '=', ciphertext.charAt(343));
byte[] ciphertext = Asymmetric.encryptRSA(message_bytes, client, rsaDecrypt);
assertEquals("incorrect RSA ciphertext length.", 256, ciphertext.length);

String plaintext = Asymmetric.decryptRSA(ciphertext, client, rsaDecrypt);
byte[] plainbytes = Asymmetric.decryptRSA(ciphertext, client, rsaDecrypt);
assertTrue("decryption failed.", Arrays.equals(message_bytes, plainbytes));
String plaintext = new String(plainbytes);
assertEquals("decryption failed.", message, plaintext);
}

@Test
public void testRSASignVerify() throws Exception {
String sig = Asymmetric.signAsymmetric(message, client, rsaSign);
assertEquals("invalid ciphertext length", 344, sig.length());
assertEquals("incorrect ciphertext final character.", '=', sig.charAt(343));

boolean success = Asymmetric.verifySignatureRSA(sig, message, client, rsaSign);
assertTrue("RSA verification failed.", success);
boolean shouldFail = Asymmetric.verifySignatureRSA(sig, message + ".", client, rsaSign);
assertFalse("RSA verification failed.", shouldFail);
byte[] sig = Asymmetric.signAsymmetric(message_bytes, client, rsaSign);
assertEquals("invalid ciphertext length", 256, sig.length);

boolean success = Asymmetric.verifySignatureRSA(sig, message_bytes, client, rsaSign);
assertTrue("RSA verification failed. Valid message not accepted", success);
String changed = message + ".";
byte[] changedBytes = changed.getBytes(StandardCharsets.UTF_8);
boolean shouldFail = Asymmetric.verifySignatureRSA(sig, changedBytes, client, rsaSign);
assertFalse("RSA verification failed. Invalid message accepted", shouldFail);
}

@Test
public void testECSignVerify() throws Exception {
String sig = Asymmetric.signAsymmetric(message, client, ecSign);
assertTrue("invalid ciphertext length", sig.length() > 50 && sig.length() < 300);

boolean success = Asymmetric.verifySignatureEC(sig, message, client, ecSign);
assertTrue("RSA verification failed.", success);
boolean shouldFail = Asymmetric.verifySignatureEC(sig, message + ".", client, ecSign);
assertFalse("RSA verification failed.", shouldFail);
byte[] sig = Asymmetric.signAsymmetric(message_bytes, client, ecSign);
assertTrue("invalid ciphertext length", sig.length > 50 && sig.length < 300);

boolean success = Asymmetric.verifySignatureEC(sig, message_bytes, client, ecSign);
assertTrue("EC verification failed. Valid message not accepted", success);
String changed = message + ".";
byte[] changedBytes = changed.getBytes(StandardCharsets.UTF_8);
boolean shouldFail = Asymmetric.verifySignatureEC(sig, changedBytes, client, ecSign);
assertFalse("EC verification failed. Invalid message accepted", shouldFail);
}

}

0 comments on commit 24d202f

Please sign in to comment.