Skip to content
This repository has been archived by the owner on Jul 6, 2023. It is now read-only.

feat(samples): add local generation for crypto keys #98

Merged
merged 4 commits into from Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 5 additions & 20 deletions samples/snippets/create_certificate.py
Expand Up @@ -15,7 +15,6 @@
# limitations under the License.

# [START privateca_create_certificate]
from google.cloud import kms
import google.cloud.security.privateca_v1 as privateca_v1
from google.protobuf import duration_pb2

Expand All @@ -26,13 +25,10 @@ def create_certificate(
ca_pool_name: str,
ca_name: str,
certificate_name: str,
kms_location: str,
key_ring_id: str,
key_id: str,
key_version_id: str,
common_name: str,
domain_name: str,
certificate_lifetime: int,
public_key_bytes: bytes,
) -> None:
"""
Create a Certificate which is issued by the Certificate Authority present in the CA Pool.
Expand All @@ -44,30 +40,19 @@ def create_certificate(
ca_pool_name: set a unique name for the CA pool.
ca_name: the name of the certificate authority which issues the certificate.
certificate_name: set a unique name for the certificate.
kms_location: Cloud KMS location.
key_ring_id: ID of the Cloud KMS key ring.
key_id: ID of the key to use.
key_version_id: verstion ID of the key to use.
common_name: a title for your certificate.
domain_name: fully qualified domain name for your certificate.
certificate_lifetime: the validity of the certificate in seconds.
public_key_bytes: public key used in signing the certificates.
"""

kmsClient = kms.KeyManagementServiceClient()
caServiceClient = privateca_v1.CertificateAuthorityServiceClient()

# 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. For more info, see: https://cloud.google.com/kms/docs/retrieve-public-key.
# Generating keys locally is also possible.
# The public key used to sign the certificate can be generated using any crypto library/framework.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it reasonable to expect that customers looking at this customer will already have a public key and/or know of a good way to generate one?

If not, or if there are best practices you'd like to show it may be worth pulling the key generation code up into the sample.

Showing that it's possible to use Cloud KMS to create a key also seems valuable to me - maybe you could keep the KMS sample link in the comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I returned comment about Cloud KMS way.

The reasons why we prefer to generate keys locally:

Keys stored in Cloud KMS are not directly exportable, so the only way a customer can use them is by calling the AsymmetricSign/Verify and Encrypt/Decrypt APIs on those keys
however, this suffers from two problems:

  • most applications that need to use the certificate/key don't support accessing the key like this, but instead require direct access to the key on the filesystem.
  • even if they did support that, this would likely introduce too much latency and limit the throughput, so there aren't many scenarios where this would be useful

Leaf certificate keys are almost always generated directly on the machine that will be using them, and never sent anywhere else.


key_version_name = kmsClient.crypto_key_version_path(
project_id, kms_location, key_ring_id, key_id, key_version_id
)
kms_public_key = kmsClient.get_public_key(name=key_version_name)

# Set the Public Key and its format as obtained from the Cloud KMS.
# Set the Public Key and its format.
public_key = privateca_v1.PublicKey(
key=str.encode(kms_public_key.pem),
key=public_key_bytes,
format_=privateca_v1.PublicKey.KeyFormat.PEM,
)

Expand Down
3 changes: 2 additions & 1 deletion samples/snippets/requirements-test.txt
@@ -1,2 +1,3 @@
pytest==6.2.4
google-auth==1.34.0
google-auth==1.34.0
cryptography==3.4.7
50 changes: 12 additions & 38 deletions samples/snippets/test_certificates.py
Expand Up @@ -17,8 +17,13 @@
import typing
import uuid

from cryptography.hazmat.backends.openssl.backend import backend
from cryptography.hazmat.primitives.asymmetric import rsa

from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.hazmat.primitives.serialization import PublicFormat

import google.auth
from google.cloud import kms

from create_certificate import create_certificate
from disable_certificate_authority import disable_certificate_authority
Expand All @@ -31,7 +36,6 @@
COMMON_NAME = "COMMON_NAME"
ORGANIZATION = "ORGANIZATION"
CERTIFICATE_LIFETIME = 1000000
KEY_VERSION = 1
DOMAIN_NAME = "domain.com"


Expand All @@ -42,62 +46,32 @@ def generate_name() -> str:
def test_create_and_revoke_certificate_authority(
certificate_authority, capsys: typing.Any
) -> None:
KEY_RING_ID = generate_name()
CRYPTO_KEY_ID = generate_name()
CERT_NAME = generate_name()

CA_POOL_NAME, CA_NAME = certificate_authority
enable_certificate_authority(PROJECT, LOCATION, CA_POOL_NAME, CA_NAME)

kms_client = kms.KeyManagementServiceClient()

kms_location_name = kms_client.common_location_path(PROJECT, LOCATION)

kms_client.create_key_ring(
request={
"parent": kms_location_name,
"key_ring_id": KEY_RING_ID,
"key_ring": {},
}
private_key = rsa.generate_private_key(
public_exponent=65537, key_size=2048, backend=backend
)

key_ring_path = kms_client.key_ring_path(PROJECT, LOCATION, KEY_RING_ID)

purpose = kms.CryptoKey.CryptoKeyPurpose.ASYMMETRIC_SIGN
algorithm = (
kms.CryptoKeyVersion.CryptoKeyVersionAlgorithm.RSA_SIGN_PKCS1_4096_SHA256
)
key = {
"purpose": purpose,
"version_template": {
"algorithm": algorithm,
},
}

kms_client.create_crypto_key(
request={
"parent": key_ring_path,
"crypto_key_id": CRYPTO_KEY_ID,
"crypto_key": key,
}
public_key_bytes = private_key.public_key().public_bytes(
Encoding.PEM, PublicFormat.SubjectPublicKeyInfo
)

# Wait while crypto key is generating
time.sleep(30)
time.sleep(5)

create_certificate(
PROJECT,
LOCATION,
CA_POOL_NAME,
CA_NAME,
CERT_NAME,
LOCATION,
KEY_RING_ID,
CRYPTO_KEY_ID,
KEY_VERSION,
COMMON_NAME,
DOMAIN_NAME,
CERTIFICATE_LIFETIME,
public_key_bytes,
)

revoke_certificate(
Expand Down