From ea7843d2ca66f83ac5fb49b46102c69ff3762cbb Mon Sep 17 00:00:00 2001
From: Sita Lakshmi Sangameswaran <sitalakshmi@google.com>
Date: Tue, 3 Aug 2021 21:15:05 +0530
Subject: [PATCH] fix: changed the crypto public key provider to Bouncy Castle
 (#223)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* fix: changed the crypto public key provider to Bouncy Castle

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/master/packages/owl-bot/README.md

* fix: changed the crypto public key provider to Bouncy Castle

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/master/packages/owl-bot/README.md

* refactor: tweaked key creation acc to review comments

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/master/packages/owl-bot/README.md

* docs: lint changes acc to review comments

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/master/packages/owl-bot/README.md

Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
---
 .../java/privateca/CreateCertificate.java     |  42 +----
 .../src/test/java/privateca/SnippetsIT.java   | 151 +++++++-----------
 privateca/pom.xml                             |   6 +-
 3 files changed, 70 insertions(+), 129 deletions(-)

diff --git a/privateca/cloud-client/src/main/java/privateca/CreateCertificate.java b/privateca/cloud-client/src/main/java/privateca/CreateCertificate.java
index 8be4f175bc4..dd716f48439 100644
--- a/privateca/cloud-client/src/main/java/privateca/CreateCertificate.java
+++ b/privateca/cloud-client/src/main/java/privateca/CreateCertificate.java
@@ -18,8 +18,6 @@
 // [START privateca_create_certificate]
 
 import com.google.api.core.ApiFuture;
-import com.google.cloud.kms.v1.CryptoKeyVersionName;
-import com.google.cloud.kms.v1.KeyManagementServiceClient;
 import com.google.cloud.security.privateca.v1.CaPoolName;
 import com.google.cloud.security.privateca.v1.Certificate;
 import com.google.cloud.security.privateca.v1.CertificateAuthorityServiceClient;
@@ -46,24 +44,14 @@ public static void main(String[] args)
       throws InterruptedException, ExecutionException, IOException {
     // TODO(developer): Replace these variables before running the sample.
 
-    // To sign and issue a certificate, a public key is essential. Here, we are making use
-    // of Cloud KMS to retrieve an already created public key. Specify the following details to
-    // retrieve the key. For more info, see: https://cloud.google.com/kms/docs/retrieve-public-key
-    String project = "your-project-id";
-    String kmsLocation = "kms-location";
-    String keyRingId = "your-ring-id";
-    String keyId = "your-key-id";
-    String keyVersionId = "your-version-id";
-
-    // Retrieve the public key from Cloud KMS.
-    ByteString publicKeyBytes =
-        retrievePublicKey(project, kmsLocation, keyRingId, keyId, keyVersionId);
-
+    // publicKeyBytes: Public key used in signing the certificates.
     // location: For a list of locations, see:
     // https://cloud.google.com/certificate-authority-service/docs/locations
     // caPoolName: Set a unique name for the CA pool.
     // certificateAuthorityName: The name of the certificate authority which issues the certificate.
     // certificateName: Set a unique name for the certificate.
+    String project = "your-project-id";
+    ByteString publicKeyBytes = ByteString.copyFrom(new byte[] {});
     String location = "ca-location";
     String caPoolName = "ca-pool-name";
     String certificateAuthorityName = "certificate-authority-name";
@@ -74,7 +62,8 @@ public static void main(String[] args)
   }
 
   // Create a Certificate which is issued by the Certificate Authority present in the CA Pool.
-  // The key used to sign the certificate is created by the Cloud KMS.
+  // The public key used to sign the certificate can be generated using any crypto
+  // library/framework.
   public static void createCertificate(
       String project,
       String location,
@@ -99,7 +88,7 @@ public static void createCertificate(
       String domainName = "dnsname.com";
       long certificateLifetime = 1000L;
 
-      // Set the Public Key and its format as obtained from the Cloud KMS.
+      // Set the Public Key and its format.
       PublicKey publicKey =
           PublicKey.newBuilder().setKey(publicKeyBytes).setFormat(KeyFormat.PEM).build();
 
@@ -163,24 +152,5 @@ public static void createCertificate(
       System.out.println(response.getPemCertificateChainList());
     }
   }
-
-  // Get the public Key used for signing the certificate from Cloud KMS.
-  public static ByteString retrievePublicKey(
-      String project, String kmsLocation, String keyRingId, String keyId, String keyVersionId)
-      throws IOException {
-    // Initialize client that will be used to send requests. This client only needs to be created
-    // once, and can be reused for multiple requests. After completing all of your requests, call
-    // the `client.close()` method on the client to safely
-    // clean up any remaining background resources.
-    try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
-
-      CryptoKeyVersionName keyVersionName =
-          CryptoKeyVersionName.of(project, kmsLocation, keyRingId, keyId, keyVersionId);
-      com.google.cloud.kms.v1.PublicKey publicKey = client.getPublicKey(keyVersionName);
-
-      ByteString publicKeyBytes = publicKey.getPemBytes();
-      return publicKeyBytes;
-    }
-  }
 }
 // [END privateca_create_certificate]
diff --git a/privateca/cloud-client/src/test/java/privateca/SnippetsIT.java b/privateca/cloud-client/src/test/java/privateca/SnippetsIT.java
index 6764556402a..f9903a9bf17 100644
--- a/privateca/cloud-client/src/test/java/privateca/SnippetsIT.java
+++ b/privateca/cloud-client/src/test/java/privateca/SnippetsIT.java
@@ -18,17 +18,6 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import com.google.cloud.kms.v1.CreateKeyRingRequest;
-import com.google.cloud.kms.v1.CryptoKey;
-import com.google.cloud.kms.v1.CryptoKey.CryptoKeyPurpose;
-import com.google.cloud.kms.v1.CryptoKeyVersion;
-import com.google.cloud.kms.v1.CryptoKeyVersion.CryptoKeyVersionAlgorithm;
-import com.google.cloud.kms.v1.CryptoKeyVersionName;
-import com.google.cloud.kms.v1.CryptoKeyVersionTemplate;
-import com.google.cloud.kms.v1.KeyManagementServiceClient;
-import com.google.cloud.kms.v1.KeyRing;
-import com.google.cloud.kms.v1.KeyRingName;
-import com.google.cloud.kms.v1.LocationName;
 import com.google.cloud.security.privateca.v1.CaPoolName;
 import com.google.cloud.security.privateca.v1.Certificate;
 import com.google.cloud.security.privateca.v1.CertificateAuthority;
@@ -38,11 +27,22 @@
 import com.google.protobuf.ByteString;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.io.PrintStream;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
 import java.util.UUID;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemWriter;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Assert;
@@ -57,15 +57,12 @@ public class SnippetsIT {
 
   private static final String PROJECT_ID = System.getenv("GOOGLE_CLOUD_PROJECT");
   private static String LOCATION;
-  private static String KMS_LOCATION;
   private static String CA_POOL_NAME;
   private static String CA_POOL_NAME_DELETE;
   private static String CA_NAME;
   private static String CA_NAME_DELETE;
   private static String CERTIFICATE_NAME;
-  private static String KEY_RING_ID;
-  private static String KEY_ID;
-  private static String VERSION_ID;
+  private static int KEY_SIZE;
 
   private ByteArrayOutputStream stdOut;
 
@@ -77,24 +74,24 @@ public static void reqEnvVar(String envVarName) {
   }
 
   @BeforeClass
-  public static void setUp() throws InterruptedException, ExecutionException, IOException {
+  public static void setUp()
+      throws IOException, ExecutionException, NoSuchProviderException, NoSuchAlgorithmException,
+          InterruptedException {
     reqEnvVar("GOOGLE_APPLICATION_CREDENTIALS");
     reqEnvVar("GOOGLE_CLOUD_PROJECT");
 
     LOCATION = "asia-south1";
-    KMS_LOCATION = "global";
     CA_POOL_NAME = "ca-pool-" + UUID.randomUUID().toString();
     CA_POOL_NAME_DELETE = "ca-pool-" + UUID.randomUUID().toString();
     CA_NAME = "ca-name-" + UUID.randomUUID().toString();
     CA_NAME_DELETE = "ca-name-" + UUID.randomUUID().toString();
     CERTIFICATE_NAME = "certificate-name-" + UUID.randomUUID().toString();
-    KEY_RING_ID = "key-ring-id-" + UUID.randomUUID().toString();
-    KEY_ID = "key-id-" + UUID.randomUUID().toString();
-    VERSION_ID = "1";
+    KEY_SIZE = 2048; // Default key size
 
     // Create CA Pool.
     privateca.CreateCaPool.createCaPool(PROJECT_ID, LOCATION, CA_POOL_NAME);
     privateca.CreateCaPool.createCaPool(PROJECT_ID, LOCATION, CA_POOL_NAME_DELETE);
+    sleep(5);
 
     // Create and Enable Certificate Authorities.
     privateca.CreateCertificateAuthority.createCertificateAuthority(
@@ -105,17 +102,27 @@ public static void setUp() throws InterruptedException, ExecutionException, IOEx
     privateca.EnableCertificateAuthority.enableCertificateAuthority(
         PROJECT_ID, LOCATION, CA_POOL_NAME, CA_NAME);
 
-    // Create Asymmetric Sign Key used to sign certificate, with Cloud KMS.
-    createKeyRing();
-    sleep(5);
-    createAsymmetricSignKey();
+    // Create an asymmetric key pair using Bouncy Castle crypto framework.
+    KeyPair asymmetricKeyPair = createAsymmetricKeyPair();
+
+    // Cast the keys to their respective components.
+    RSAPublicKey publicKey = (RSAPublicKey) asymmetricKeyPair.getPublic();
+    RSAPrivateKey privateKey = (RSAPrivateKey) asymmetricKeyPair.getPrivate();
+
+    // Construct the PemObject for public and private keys.
+    PemObject publicKeyPemObject = new PemObject("PUBLIC KEY", publicKey.getEncoded());
+    PemObject privateKeyPemObject = new PemObject("PRIVATE KEY", privateKey.getEncoded());
+
+    // Only the public key will be used to create the certificate.
+    ByteString publicKeyByteString = convertToPemEncodedByteString(publicKeyPemObject);
 
-    // Retrieve public key from Cloud KMS and Create Certificate.
-    ByteString publicKey =
-        privateca.CreateCertificate.retrievePublicKey(
-            PROJECT_ID, KMS_LOCATION, KEY_RING_ID, KEY_ID, VERSION_ID);
+    // TODO (Developers): Save the private key by writing it to a file and
+    // TODO (cont): use it to verify the issued certificate.
+    ByteString privateKeyByteString = convertToPemEncodedByteString(privateKeyPemObject);
+
+    // Create certificate with the above generated public key.
     privateca.CreateCertificate.createCertificate(
-        PROJECT_ID, LOCATION, CA_POOL_NAME, CA_NAME, CERTIFICATE_NAME, publicKey);
+        PROJECT_ID, LOCATION, CA_POOL_NAME, CA_NAME, CERTIFICATE_NAME, publicKeyByteString);
     sleep(5);
   }
 
@@ -125,9 +132,6 @@ public static void cleanUp() throws InterruptedException, ExecutionException, IO
     ByteArrayOutputStream stdOut = new ByteArrayOutputStream();
     System.setOut(new PrintStream(stdOut));
 
-    // De-provision public key.
-    cleanupCertificateSignKey();
-
     // Delete CA and CA pool.
     privateca.DeleteCertificateAuthority.deleteCertificateAuthority(
         PROJECT_ID, LOCATION, CA_POOL_NAME, CA_NAME);
@@ -138,71 +142,34 @@ public static void cleanUp() throws InterruptedException, ExecutionException, IO
     System.setOut(null);
   }
 
-  // Create a new key ring.
-  public static void createKeyRing() throws IOException {
-    // Initialize client that will be used to send requests. This client only
-    // needs to be created once, and can be reused for multiple requests. After
-    // completing all of your requests, call the "close" method on the client to
-    // safely clean up any remaining background resources.
-    try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
-      // Build the parent name from the project and location.
-      LocationName locationName = LocationName.of(PROJECT_ID, KMS_LOCATION);
-
-      // Build the key ring to create.
-      KeyRing keyRing = KeyRing.newBuilder().setName(locationName.toString()).build();
-
-      // Create the key ring.
-      KeyRing createdKeyRing =
-          client.createKeyRing(
-              CreateKeyRingRequest.newBuilder()
-                  .setParent(locationName.toString())
-                  .setKeyRing(keyRing)
-                  .setKeyRingId(KEY_RING_ID)
-                  .build());
-      System.out.printf("Created key ring: %s%n", createdKeyRing.getName());
-    }
+  // Wait for the specified amount of time.
+  public static void sleep(int seconds) throws InterruptedException {
+    TimeUnit.SECONDS.sleep(seconds);
   }
 
-  // Create a new asymmetric key for the purpose of signing and verifying data.
-  public static void createAsymmetricSignKey() throws IOException {
-    // Initialize client that will be used to send requests. This client only
-    // needs to be created once, and can be reused for multiple requests. After
-    // completing all of your requests, call the "close" method on the client to
-    // safely clean up any remaining background resources.
-    try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
-      // Build the parent name from the project, location, and key ring.
-      KeyRingName keyRingName = KeyRingName.of(PROJECT_ID, KMS_LOCATION, KEY_RING_ID);
-
-      // Build the asymmetric key to create.
-      CryptoKey key =
-          CryptoKey.newBuilder()
-              .setPurpose(CryptoKeyPurpose.ASYMMETRIC_SIGN)
-              .setVersionTemplate(
-                  CryptoKeyVersionTemplate.newBuilder()
-                      .setAlgorithm(CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_2048_SHA256))
-              .build();
-
-      // Create the key.
-      CryptoKey createdKey = client.createCryptoKey(keyRingName, KEY_ID, key);
-      System.out.printf("Created asymmetric key: %s%n", createdKey.getName());
-    }
-  }
+  // Create an asymmetric key pair to be used in certificate signing.
+  public static KeyPair createAsymmetricKeyPair()
+      throws NoSuchAlgorithmException, NoSuchProviderException {
+    Security.addProvider(new BouncyCastleProvider());
 
-  public static void cleanupCertificateSignKey() throws IOException, InterruptedException {
-    try (KeyManagementServiceClient client = KeyManagementServiceClient.create()) {
-      CryptoKeyVersionName cryptoKeyVersionName =
-          CryptoKeyVersionName.of(PROJECT_ID, KMS_LOCATION, KEY_RING_ID, KEY_ID, VERSION_ID);
-      // Destroy the crypto key version.
-      CryptoKeyVersion cryptoKeyVersion = client.destroyCryptoKeyVersion(cryptoKeyVersionName);
-      sleep(5);
-      // If the response has destroy time, then the version is successfully destroyed.
-      Assert.assertTrue(cryptoKeyVersion.hasDestroyTime());
-    }
+    // Generate the key pair with RSA algorithm using Bouncy Castle (BC).
+    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
+    generator.initialize(KEY_SIZE);
+    KeyPair keyPair = generator.generateKeyPair();
+
+    return keyPair;
   }
 
-  // Wait for the specified amount of time.
-  public static void sleep(int seconds) throws InterruptedException {
-    TimeUnit.SECONDS.sleep(seconds);
+  // Convert the encoded PemObject to ByteString.
+  public static ByteString convertToPemEncodedByteString(PemObject pemEncodedKey)
+      throws IOException {
+    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+    PemWriter pemWriter = new PemWriter(new OutputStreamWriter(byteArrayOutputStream));
+    pemWriter.writeObject(pemEncodedKey);
+    pemWriter.close();
+    ByteString keyByteString = ByteString.copyFrom(byteArrayOutputStream.toByteArray());
+
+    return keyByteString;
   }
 
   @Before
diff --git a/privateca/pom.xml b/privateca/pom.xml
index 62464ee33e4..45035eaae95 100644
--- a/privateca/pom.xml
+++ b/privateca/pom.xml
@@ -54,7 +54,11 @@
       <artifactId>google-cloud-security-private-ca</artifactId>
       <version>1.1.0</version>
     </dependency>
-
+    <dependency>
+      <groupId>org.bouncycastle</groupId>
+      <artifactId>bcpkix-jdk15on</artifactId>
+      <version>1.58</version>
+    </dependency>
     <dependency>
       <groupId>com.google.cloud</groupId>
       <artifactId>google-cloud-kms</artifactId>