Skip to content

Commit 3cd5367

Browse files
authored
Merge pull request #105 from Venafi/pm_updates
Service generated CSR enhancement for VaaS
2 parents 3bc52fb + 15084bc commit 3cd5367

9 files changed

Lines changed: 567 additions & 110 deletions

File tree

tests/test_pm.py

Lines changed: 86 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919

2020
from test_env import (TPP_TOKEN_URL, CLOUD_APIKEY, CLOUD_URL, TPP_PM_ROOT, CLOUD_ENTRUST_CA_NAME,
2121
CLOUD_DIGICERT_CA_NAME, TPP_CA_NAME, TPP_USER, TPP_PASSWORD, CLOUD_TEAM)
22-
from test_utils import timestamp
23-
from vcert import TPPTokenConnection, CloudConnection, Authentication, SCOPE_PM, logger, VenafiError
22+
from test_utils import get_tpp_policy_name, get_vaas_zone
23+
from vcert import TPPTokenConnection, CloudConnection, Authentication, SCOPE_PM, logger, VenafiError, KeyType
2424
from vcert.parser import json_parser, yaml_parser
2525
from vcert.parser.utils import parse_policy_spec
2626
from vcert.policy import (Policy, Subject, KeyPair, SubjectAltNames, Defaults, DefaultSubject, DefaultKeyPair,
2727
PolicySpecification)
2828
from vcert.policy.pm_cloud import (CA_TYPE_DIGICERT, CA_TYPE_ENTRUST, validate_policy_spec as validate_ps_vaas,
29-
get_ca_info, default_error_msg)
29+
get_ca_info, default_error_msg, ipv4, ipv6)
3030
from vcert.policy.pm_tpp import (is_service_generated_csr, validate_policy_spec as validate_ps_tpp, no_match_error_msg,
3131
too_many_error_msg, unsupported_error_msg, supported_key_types,
3232
supported_rsa_key_sizes, supported_elliptic_curves)
@@ -50,7 +50,7 @@ def test_json_parsing(self):
5050
self._assert_policy_spec(ps)
5151

5252
def test_json_serialization(self):
53-
ps = PolicySpecification(policy=_get_policy_obj(), defaults=_get_defaults_obj())
53+
ps = PolicySpecification(policy=get_policy_obj(), defaults=get_defaults_obj())
5454
json_parser.serialize(ps, 'test_json_serialization.json')
5555

5656
def test_yaml_11_parsing(self):
@@ -61,7 +61,7 @@ def test_yaml_12_parsing(self):
6161
self._assert_policy_spec(ps)
6262

6363
def test_yaml_serialization(self):
64-
ps = PolicySpecification(policy=_get_policy_obj(), defaults=_get_defaults_obj())
64+
ps = PolicySpecification(policy=get_policy_obj(), defaults=get_defaults_obj())
6565
yaml_parser.serialize(ps, 'test_yaml_serialization.yaml')
6666

6767
def _assert_policy_spec(self, ps):
@@ -108,18 +108,18 @@ def test_create_policy_yaml(self):
108108
pass
109109

110110
def test_create_policy_full(self):
111-
policy = _get_policy_obj(ca_type=CA_TYPE_TPP)
111+
policy = get_policy_obj(ca_type=CA_TYPE_TPP)
112112
policy.key_pair.rsa_key_sizes = [2048]
113-
self._create_policy_tpp(policy=policy, defaults=_get_defaults_obj())
113+
self._create_policy_tpp(policy=policy, defaults=get_defaults_obj())
114114

115115
def test_create_policy_empty(self):
116116
self._create_policy_tpp()
117117

118118
def test_create_policy_no_policy(self):
119-
self._create_policy_tpp(defaults=_get_defaults_obj())
119+
self._create_policy_tpp(defaults=get_defaults_obj())
120120

121121
def test_create_policy_no_defaults(self):
122-
policy = _get_policy_obj(ca_type=CA_TYPE_TPP)
122+
policy = get_policy_obj(ca_type=CA_TYPE_TPP)
123123
policy.key_pair.rsa_key_sizes = [2048]
124124
self._create_policy_tpp(policy=policy)
125125

@@ -149,7 +149,7 @@ def test_create_and_get_policy_with_contacts(self):
149149
self.assertEqual(1, len(result.users))
150150

151151
def _create_policy_tpp(self, policy_spec=None, policy=None, defaults=None):
152-
zone = f"{TPP_PM_ROOT}\\{_get_tpp_policy_name()}"
152+
zone = f"{TPP_PM_ROOT}\\{get_tpp_policy_name()}"
153153
create_policy(self.tpp_conn, zone, policy_spec, policy, defaults)
154154

155155

@@ -171,27 +171,81 @@ def test_create_policy_yaml(self):
171171
pass
172172

173173
def test_create_policy_full(self):
174-
self._create_policy_cloud(policy=_get_policy_obj(), defaults=_get_defaults_obj())
174+
self._create_policy_cloud(policy=get_policy_obj(), defaults=get_defaults_obj())
175175

176176
def test_create_policy_empty(self):
177177
self._create_policy_cloud()
178178

179179
def test_create_policy_no_policy(self):
180-
self._create_policy_cloud(defaults=_get_defaults_obj())
180+
self._create_policy_cloud(defaults=get_defaults_obj())
181181

182182
def test_create_policy_no_defaults(self):
183-
self._create_policy_cloud(policy=_get_policy_obj())
183+
self._create_policy_cloud(policy=get_policy_obj())
184184

185185
def test_create_policy_entrust(self):
186-
self._create_policy_cloud(policy=_get_policy_obj(ca_type=CA_TYPE_ENTRUST), defaults=_get_defaults_obj())
186+
self._create_policy_cloud(policy=get_policy_obj(ca_type=CA_TYPE_ENTRUST), defaults=get_defaults_obj())
187187

188188
def test_create_policy_digicert(self):
189-
self._create_policy_cloud(policy=_get_policy_obj(ca_type=CA_TYPE_DIGICERT), defaults=_get_defaults_obj())
189+
self._create_policy_cloud(policy=get_policy_obj(ca_type=CA_TYPE_DIGICERT), defaults=get_defaults_obj())
190190

191191
def test_validate_domains(self):
192-
policy = self._create_policy_cloud(policy=_get_policy_obj())
192+
policy = self._create_policy_cloud(policy=get_policy_obj())
193193
self.assertListEqual(policy.policy.domains, POLICY_DOMAINS)
194194

195+
def test_csr_attributes_service(self):
196+
cit = self._create_csr_attributes_policy(service_generated_csr=True)
197+
198+
self.assertFalse(cit.csr_upload_allowed, "csrUploadAllowed attribute is not False")
199+
self.assertTrue(cit.key_generated_by_venafi_allowed, "keyGeneratedByVenafiAllowed is not True")
200+
201+
def test_csr_attributes_local(self):
202+
cit = self._create_csr_attributes_policy(service_generated_csr=False)
203+
204+
self.assertTrue(cit.csr_upload_allowed, "csrUploadAllowed attribute is not True")
205+
self.assertFalse(cit.key_generated_by_venafi_allowed, "keyGeneratedByVenafiAllowed is not False")
206+
207+
def test_csr_attributes_not_specified(self):
208+
cit = self._create_csr_attributes_policy()
209+
210+
self.assertTrue(cit.csr_upload_allowed, "csrUploadAllowed attribute is not True")
211+
self.assertTrue(cit.key_generated_by_venafi_allowed, "keyGeneratedByVenafiAllowed is not True")
212+
213+
def test_ec_key_pair(self):
214+
policy = get_policy_obj()
215+
kp = KeyPair(
216+
key_types=['EC'],
217+
rsa_key_sizes=[2048, 4096],
218+
elliptic_curves=['P521', 'P384'],
219+
reuse_allowed=False)
220+
policy.key_pair = kp
221+
222+
defaults = get_defaults_obj()
223+
defaults.key_pair = DefaultKeyPair(
224+
key_type='EC',
225+
rsa_key_size=2048,
226+
elliptic_curve='P521')
227+
228+
ps = self._create_policy_cloud(policy=policy, defaults=defaults)
229+
self.assertEqual(ps.policy.key_pair.key_types[0].upper(), KeyType.ECDSA.upper(), "Policy Key Type is not EC")
230+
self.assertTrue(len(ps.policy.key_pair.elliptic_curves) == 2,
231+
f"Expected 2 accepted Elliptic Curves. Got {len(ps.policy.key_pair.elliptic_curves)}")
232+
self.assertIn('P521', ['P521', 'P384'], "[P521] is not in the allowed Elliptic Curves list")
233+
self.assertIn('P384', ['P521', 'P384'], "[P384] is not in the allowed Elliptic Curves list")
234+
235+
def test_create_policy_uri_ip_email(self):
236+
policy = get_policy_obj()
237+
policy.subject_alt_names.email_allowed = True
238+
policy.subject_alt_names.uri_allowed = True
239+
policy.subject_alt_names.ip_allowed = True
240+
uri_protocols = ["https", "ldaps", "spiffe"]
241+
policy.subject_alt_names.uri_protocols = uri_protocols
242+
ps = self._create_policy_cloud(policy=policy, defaults=get_defaults_obj())
243+
self.assertListEqual(ps.policy.subject_alt_names.ip_constraints, [ipv4, ipv6])
244+
self.assertListEqual(ps.policy.subject_alt_names.uri_protocols, uri_protocols)
245+
self.assertTrue(ps.policy.subject_alt_names.email_allowed)
246+
self.assertTrue(ps.policy.subject_alt_names.uri_allowed)
247+
self.assertTrue(ps.policy.subject_alt_names.ip_allowed)
248+
195249
def test_create_policy_with_no_users(self):
196250
zone = self._get_random_zone()
197251
connector = self.cloud_conn
@@ -277,13 +331,23 @@ def test_create_policy_with_team(self):
277331
self.assertEqual(CLOUD_TEAM, result.users[0])
278332

279333
def _create_policy_cloud(self, policy_spec=None, policy=None, defaults=None):
280-
zone = self._get_random_zone()
334+
zone = get_vaas_zone()
281335
response = create_policy(self.cloud_conn, zone, policy_spec, policy, defaults)
282336
return response
283337

284-
@staticmethod
285-
def _get_random_zone():
286-
return _get_zone()
338+
def _create_csr_attributes_policy(self, service_generated_csr=None):
339+
"""
340+
341+
:param bool service_generated_csr:
342+
:rtype: common.Policy
343+
"""
344+
policy = get_policy_obj()
345+
policy.key_pair.service_generated = service_generated_csr
346+
zone = get_vaas_zone()
347+
create_policy(connector=self.cloud_conn, zone=zone, policy_spec=None, policy=policy)
348+
cit = self.cloud_conn._get_template_by_id(zone)
349+
350+
return cit
287351

288352

289353
class TestLocalMethods(unittest.TestCase):
@@ -646,7 +710,7 @@ def create_policy(connector, zone, policy_spec=None, policy=None, defaults=None)
646710
POLICY_DOMAINS = ['vfidev.com', 'vfidev.net', 'venafi.example']
647711

648712

649-
def _get_policy_obj(ca_type=None):
713+
def get_policy_obj(ca_type=None):
650714
policy = Policy(
651715
subject=Subject(
652716
orgs=['OSS Venafi, Inc.'],
@@ -683,7 +747,7 @@ def _get_policy_obj(ca_type=None):
683747
return policy
684748

685749

686-
def _get_defaults_obj():
750+
def get_defaults_obj():
687751
defaults = Defaults(
688752
d_subject=DefaultSubject(
689753
org='OSS Venafi, Inc.',
@@ -697,24 +761,3 @@ def _get_defaults_obj():
697761
elliptic_curve='P521'),
698762
auto_installed=False)
699763
return defaults
700-
701-
702-
def _get_app_name():
703-
name = 'vcert-python-app-{}'
704-
return name
705-
706-
707-
def _get_cit_name():
708-
cit_name = 'vcert-python-cit-{}'
709-
return cit_name
710-
711-
712-
def _get_zone():
713-
time = timestamp()
714-
zone = f"{_get_app_name().format(time)}\\{_get_cit_name().format(time)}"
715-
return zone
716-
717-
718-
def _get_tpp_policy_name():
719-
time = timestamp()
720-
return f"{_get_app_name().format(time)}"

tests/test_utils.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,27 @@ def timestamp():
3939
return datetime.today().strftime('%Y.%m.%d-%Hh%Mm%Ss')
4040

4141

42+
def _get_app_name():
43+
name = 'vcert-python-app-{}'
44+
return name
45+
46+
47+
def _get_cit_name():
48+
cit_name = 'vcert-python-cit-{}'
49+
return cit_name
50+
51+
52+
def get_vaas_zone():
53+
t = timestamp()
54+
zone = f"{_get_app_name().format(t)}\\{_get_cit_name().format(t)}"
55+
return zone
56+
57+
58+
def get_tpp_policy_name():
59+
t = timestamp()
60+
return f"{_get_app_name().format(t)}"
61+
62+
4263
def simple_enroll(conn, zone):
4364
req = CertificateRequest(common_name=f"{random_word(12)}.venafi.example.com")
4465
conn.request_cert(req, zone)

tests/test_vaas.py

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,15 @@
2121

2222
from cryptography import x509
2323
from cryptography.hazmat.backends import default_backend
24-
from cryptography.hazmat.primitives import hashes
24+
from cryptography.hazmat.primitives import hashes, serialization
25+
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
2526
from cryptography.x509.oid import NameOID
2627

28+
from policy import KeyPair, DefaultKeyPair, PolicySpecification
2729
from test_env import CLOUD_ZONE, CLOUD_APIKEY, CLOUD_URL, RANDOM_DOMAIN
28-
from test_utils import random_word, enroll, renew, renew_by_thumbprint, renew_without_key_reuse, simple_enroll
30+
from test_pm import get_policy_obj, get_defaults_obj
31+
from test_utils import random_word, enroll, renew, renew_by_thumbprint, renew_without_key_reuse, simple_enroll, \
32+
get_vaas_zone
2933
from vcert import CloudConnection, KeyType, CertificateRequest, CustomField, logger, CSR_ORIGIN_SERVICE
3034

3135
log = logger.get_child("test-vaas")
@@ -165,3 +169,49 @@ def test_cloud_enroll_service_generated_csr(self):
165169

166170
output = cert_object.as_pkcs12('FooBarPass123')
167171
log.info(f"PKCS12 created successfully for certificate with CN: {cn}")
172+
173+
def test_enroll_ec_key_certificate(self):
174+
policy = get_policy_obj()
175+
kp = KeyPair(
176+
key_types=['EC'],
177+
elliptic_curves=['P521', 'P384'],
178+
reuse_allowed=False)
179+
policy.key_pair = kp
180+
181+
defaults = get_defaults_obj()
182+
defaults.key_pair = DefaultKeyPair(
183+
key_type='EC',
184+
elliptic_curve='P521')
185+
186+
policy_spec = PolicySpecification()
187+
policy_spec.policy = policy
188+
policy_spec.defaults = defaults
189+
190+
zone = get_vaas_zone()
191+
192+
self.cloud_conn.set_policy(zone, policy_spec)
193+
password = 'FooBarPass123'
194+
195+
request = CertificateRequest(
196+
common_name=f"{random_word(10)}.venafi.example",
197+
key_type=KeyType(
198+
key_type="ec",
199+
option="P384"
200+
),
201+
csr_origin=CSR_ORIGIN_SERVICE,
202+
key_password=password
203+
)
204+
205+
self.cloud_conn.request_cert(request, zone)
206+
cert = self.cloud_conn.retrieve_cert(request)
207+
208+
p_key = None
209+
try:
210+
p_key = serialization.load_pem_private_key(data=cert.key.encode(), password=password.encode(),
211+
backend=default_backend())
212+
except Exception as e:
213+
log.error(msg=f"Error parsing Private Key: {e.message}")
214+
215+
if p_key:
216+
self.assertIsInstance(p_key, EllipticCurvePrivateKey, "returned private key is not of type Elliptic Curve")
217+
self.assertEqual(p_key.curve.key_size, 384, f"Private Key expected curve: 384. Got: {p_key.curve.key_size}")

vcert/common.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ def __init__(self, policy_id=None, company_id=None, name=None, system_generated=
179179
subject_ou_regexes=None, subject_st_regexes=None, subject_l_regexes=None, subject_c_regexes=None,
180180
san_regexes=None, key_types=None, key_reuse=None, cert_authority=None, cert_authority_account_id=None,
181181
cert_authority_product_option_id=None, priority=None, modification_date=None, status=None, reason=None,
182-
validity_period=None, recommended_settings=None):
182+
validity_period=None, recommended_settings=None, csr_upload_allowed=None,
183+
key_generated_by_venafi_allowed=None):
183184
"""
184185
:param str policy_id:
185186
:param str company_id:
@@ -204,6 +205,8 @@ def __init__(self, policy_id=None, company_id=None, name=None, system_generated=
204205
:param str reason:
205206
:param str validity_period:
206207
:param vaas_utils.RecommendedSettings recommended_settings:
208+
:param bool csr_upload_allowed:
209+
:param bool key_generated_by_venafi_allowed:
207210
"""
208211
self.id = policy_id
209212
self.company_id = company_id
@@ -229,6 +232,11 @@ def __init__(self, policy_id=None, company_id=None, name=None, system_generated=
229232
self.reason = reason
230233
self.validity_period = validity_period
231234
self.recommended_settings = recommended_settings
235+
self.csr_upload_allowed = csr_upload_allowed
236+
self.key_generated_by_venafi_allowed = key_generated_by_venafi_allowed
237+
self.email_regexes = None # type: list[str]
238+
self.ip_constraints_regexes = None # type: list[str]
239+
self.uri_regexes = None # type: list[str]
232240

233241
def __repr__(self):
234242
return "Policy:\n" + "\n".join([f" {k}: {v}" for k, v in (

0 commit comments

Comments
 (0)