Skip to content

Commit 8dd8f69

Browse files
authored
feat(samples): add subordinate CA samples
1 parent 1ea16b7 commit 8dd8f69

File tree

4 files changed

+367
-0
lines changed

4 files changed

+367
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# [START privateca_activate_subordinateca]
18+
import google.cloud.security.privateca_v1 as privateca_v1
19+
20+
21+
def activate_subordinate_ca(
22+
project_id: str,
23+
location: str,
24+
ca_pool_name: str,
25+
subordinate_ca_name: str,
26+
pem_ca_certificate: str,
27+
ca_name: str,
28+
) -> None:
29+
"""
30+
Activate a subordinate Certificate Authority (CA).
31+
*Prerequisite*: Get the Certificate Signing Resource (CSR) of the subordinate CA signed by another CA. Pass in the signed
32+
certificate and (issuer CA's name or the issuer CA's Certificate chain).
33+
*Post*: After activating the subordinate CA, it should be enabled before issuing certificates.
34+
Args:
35+
project_id: project ID or project number of the Cloud project you want to use.
36+
location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations.
37+
ca_pool_name: set it to the CA Pool under which the CA should be created.
38+
pem_ca_certificate: the signed certificate, obtained by signing the CSR.
39+
subordinate_ca_name: the CA to be activated.
40+
ca_name: The name of the certificate authority which signed the CSR.
41+
If an external CA (CA not present in Google Cloud) was used for signing,
42+
then use the CA's issuerCertificateChain.
43+
"""
44+
45+
ca_service_client = privateca_v1.CertificateAuthorityServiceClient()
46+
47+
subordinate_ca_path = ca_service_client.certificate_authority_path(
48+
project_id, location, ca_pool_name, subordinate_ca_name
49+
)
50+
ca_path = ca_service_client.certificate_authority_path(
51+
project_id, location, ca_pool_name, ca_name
52+
)
53+
54+
# Set CA subordinate config.
55+
subordinate_config = privateca_v1.SubordinateConfig(
56+
# Follow one of the below methods:
57+
# Method 1: If issuer CA is in Google Cloud, set the Certificate Authority Name.
58+
certificate_authority=ca_path,
59+
# Method 2: If issuer CA is external to Google Cloud, set the issuer's certificate chain.
60+
# The certificate chain of the CA (which signed the CSR) from leaf to root.
61+
# pem_issuer_chain=privateca_v1.SubordinateConfig.SubordinateConfigChain(
62+
# pem_certificates=issuer_certificate_chain,
63+
# )
64+
)
65+
66+
# Construct the "Activate CA Request".
67+
request = privateca_v1.ActivateCertificateAuthorityRequest(
68+
name=subordinate_ca_path,
69+
# The signed certificate.
70+
pem_ca_certificate=pem_ca_certificate,
71+
subordinate_config=subordinate_config,
72+
)
73+
74+
# Activate the CA
75+
operation = ca_service_client.activate_certificate_authority(request=request)
76+
result = operation.result()
77+
78+
print("Operation result:", result)
79+
80+
# The current state will be STAGED.
81+
# The Subordinate CA has to be ENABLED before issuing certificates.
82+
print(
83+
f"Current state: {ca_service_client.get_certificate_authority(name=subordinate_ca_path).state}"
84+
)
85+
86+
87+
# [END privateca_activate_subordinateca]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# [START privateca_create_certificate_csr]
18+
import google.cloud.security.privateca_v1 as privateca_v1
19+
from google.protobuf import duration_pb2
20+
21+
22+
def create_certificate_csr(
23+
project_id: str,
24+
location: str,
25+
ca_pool_name: str,
26+
ca_name: str,
27+
certificate_name: str,
28+
certificate_lifetime: int,
29+
pem_csr: str,
30+
) -> None:
31+
"""
32+
Create a Certificate which is issued by the specified Certificate Authority (CA).
33+
The certificate details and the public key is provided as a Certificate Signing Request (CSR).
34+
Args:
35+
project_id: project ID or project number of the Cloud project you want to use.
36+
location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations.
37+
ca_pool_name: set a unique name for the CA pool.
38+
ca_name: the name of the certificate authority to sign the CSR.
39+
certificate_name: set a unique name for the certificate.
40+
certificate_lifetime: the validity of the certificate in seconds.
41+
pem_csr: set the Certificate Issuing Request in the pem encoded format.
42+
"""
43+
44+
ca_service_client = privateca_v1.CertificateAuthorityServiceClient()
45+
46+
# The public key used to sign the certificate can be generated using any crypto library/framework.
47+
# Also you can use Cloud KMS to retrieve an already created public key.
48+
# For more info, see: https://cloud.google.com/kms/docs/retrieve-public-key.
49+
50+
# Create certificate with CSR.
51+
# The pem_csr contains the public key and the domain details required.
52+
certificate = privateca_v1.Certificate(
53+
pem_csr=pem_csr, lifetime=duration_pb2.Duration(seconds=certificate_lifetime),
54+
)
55+
56+
# Create the Certificate Request.
57+
# Set the CA which is responsible for creating the certificate with the provided CSR.
58+
request = privateca_v1.CreateCertificateRequest(
59+
parent=ca_service_client.ca_pool_path(project_id, location, ca_pool_name),
60+
certificate_id=certificate_name,
61+
certificate=certificate,
62+
issuing_certificate_authority_id=ca_name,
63+
)
64+
response = ca_service_client.create_certificate(request=request)
65+
66+
print(f"Certificate created successfully: {response.name}")
67+
68+
# Get the signed certificate and the issuer chain list.
69+
print(f"Signed certificate: {response.pem_certificate}")
70+
print(f"Issuer chain list: {response.pem_certificate_chain}")
71+
72+
73+
# [END privateca_create_certificate_csr]
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# [START privateca_create_subordinateca]
18+
import google.cloud.security.privateca_v1 as privateca_v1
19+
from google.protobuf import duration_pb2
20+
21+
22+
def create_subordinate_ca(
23+
project_id: str,
24+
location: str,
25+
ca_pool_name: str,
26+
subordinate_ca_name: str,
27+
common_name: str,
28+
organization: str,
29+
domain: str,
30+
ca_duration: int,
31+
) -> None:
32+
"""
33+
Create Certificate Authority (CA) which is the subordinate CA in the given CA Pool.
34+
Args:
35+
project_id: project ID or project number of the Cloud project you want to use.
36+
location: location you want to use. For a list of locations, see: https://cloud.google.com/certificate-authority-service/docs/locations.
37+
ca_pool_name: set it to the CA Pool under which the CA should be created.
38+
subordinate_ca_name: unique name for the Subordinate CA.
39+
common_name: a title for your certificate authority.
40+
organization: the name of your company for your certificate authority.
41+
domain: the name of your company for your certificate authority.
42+
ca_duration: the validity of the certificate authority in seconds.
43+
"""
44+
45+
ca_service_client = privateca_v1.CertificateAuthorityServiceClient()
46+
47+
# Set the type of Algorithm
48+
key_version_spec = privateca_v1.CertificateAuthority.KeyVersionSpec(
49+
algorithm=privateca_v1.CertificateAuthority.SignHashAlgorithm.RSA_PKCS1_4096_SHA256
50+
)
51+
52+
# Set CA subject config.
53+
subject_config = privateca_v1.CertificateConfig.SubjectConfig(
54+
subject=privateca_v1.Subject(
55+
common_name=common_name, organization=organization
56+
),
57+
# Set the fully qualified domain name.
58+
subject_alt_name=privateca_v1.SubjectAltNames(dns_names=[domain]),
59+
)
60+
61+
# Set the key usage options for X.509 fields.
62+
x509_parameters = privateca_v1.X509Parameters(
63+
key_usage=privateca_v1.KeyUsage(
64+
base_key_usage=privateca_v1.KeyUsage.KeyUsageOptions(
65+
crl_sign=True, cert_sign=True,
66+
)
67+
),
68+
ca_options=privateca_v1.X509Parameters.CaOptions(is_ca=True,),
69+
)
70+
71+
# Set certificate authority settings.
72+
certificate_authority = privateca_v1.CertificateAuthority(
73+
type_=privateca_v1.CertificateAuthority.Type.SUBORDINATE,
74+
key_spec=key_version_spec,
75+
config=privateca_v1.CertificateConfig(
76+
subject_config=subject_config, x509_config=x509_parameters,
77+
),
78+
# Set the CA validity duration.
79+
lifetime=duration_pb2.Duration(seconds=ca_duration),
80+
)
81+
82+
ca_pool_path = ca_service_client.ca_pool_path(project_id, location, ca_pool_name)
83+
84+
# Create the CertificateAuthorityRequest.
85+
request = privateca_v1.CreateCertificateAuthorityRequest(
86+
parent=ca_pool_path,
87+
certificate_authority_id=subordinate_ca_name,
88+
certificate_authority=certificate_authority,
89+
)
90+
91+
operation = ca_service_client.create_certificate_authority(request=request)
92+
result = operation.result()
93+
94+
print(f"Operation result: {result}")
95+
96+
97+
# [END privateca_create_subordinateca]
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
import re
17+
import typing
18+
import uuid
19+
20+
import google.auth
21+
import google.cloud.security.privateca_v1 as privateca_v1
22+
23+
from activate_subordinate_ca import activate_subordinate_ca
24+
from create_certificate_csr import create_certificate_csr
25+
from create_subordinate_ca import create_subordinate_ca
26+
from revoke_certificate import revoke_certificate
27+
28+
PROJECT = google.auth.default()[1]
29+
LOCATION = "europe-west1"
30+
COMMON_NAME = "COMMON_NAME"
31+
ORGANIZATION = "ORGANIZATION"
32+
CA_DURATION = CERTIFICATE_LIFETIME = 1000000
33+
DOMAIN_NAME = "domain.com"
34+
35+
36+
def generate_name() -> str:
37+
return "test-" + uuid.uuid4().hex[:10]
38+
39+
40+
def test_subordinate_certificate_authority(
41+
certificate_authority, capsys: typing.Any
42+
) -> None:
43+
CSR_CERT_NAME = generate_name()
44+
SUBORDINATE_CA_NAME = generate_name()
45+
46+
CA_POOL_NAME, ROOT_CA_NAME = certificate_authority
47+
48+
# 1. Create a Subordinate Certificate Authority.
49+
create_subordinate_ca(
50+
PROJECT,
51+
LOCATION,
52+
CA_POOL_NAME,
53+
SUBORDINATE_CA_NAME,
54+
COMMON_NAME,
55+
ORGANIZATION,
56+
DOMAIN_NAME,
57+
CA_DURATION,
58+
)
59+
60+
# 2. Fetch CSR of the given CA.
61+
ca_service_client = privateca_v1.CertificateAuthorityServiceClient()
62+
63+
ca_path = ca_service_client.certificate_authority_path(
64+
PROJECT, LOCATION, CA_POOL_NAME, SUBORDINATE_CA_NAME
65+
)
66+
response = ca_service_client.fetch_certificate_authority_csr(name=ca_path)
67+
pem_csr = response.pem_csr
68+
69+
# 3. Sign the CSR and create a certificate.
70+
create_certificate_csr(
71+
PROJECT,
72+
LOCATION,
73+
CA_POOL_NAME,
74+
ROOT_CA_NAME,
75+
CSR_CERT_NAME,
76+
CERTIFICATE_LIFETIME,
77+
pem_csr,
78+
)
79+
80+
# 4. Get certificate PEM format
81+
certificate_name = ca_service_client.certificate_path(
82+
PROJECT, LOCATION, CA_POOL_NAME, CSR_CERT_NAME
83+
)
84+
pem_certificate = ca_service_client.get_certificate(
85+
name=certificate_name
86+
).pem_certificate
87+
88+
# 5. Activate Subordinate CA
89+
activate_subordinate_ca(
90+
PROJECT,
91+
LOCATION,
92+
CA_POOL_NAME,
93+
SUBORDINATE_CA_NAME,
94+
pem_certificate,
95+
ROOT_CA_NAME,
96+
)
97+
98+
revoke_certificate(
99+
PROJECT, LOCATION, CA_POOL_NAME, CSR_CERT_NAME,
100+
)
101+
102+
out, _ = capsys.readouterr()
103+
104+
assert re.search(
105+
f'Operation result: name: "projects/{PROJECT}/locations/{LOCATION}/caPools/{CA_POOL_NAME}/certificateAuthorities/{SUBORDINATE_CA_NAME}"',
106+
out,
107+
)
108+
109+
assert "Certificate created successfully" in out
110+
assert f"Current state: {privateca_v1.CertificateAuthority.State.STAGED}" in out

0 commit comments

Comments
 (0)