Skip to content

Commit 269c279

Browse files
committed
[client] Implement the X509V3ExtensionsType fields on X509Certificate
1 parent f49241e commit 269c279

File tree

6 files changed

+313
-0
lines changed

6 files changed

+313
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# coding: utf-8
2+
import os
3+
4+
from pycti import OpenCTIApiClient
5+
6+
# Variables
7+
api_url = os.getenv("OPENCTI_API_URL", "http://opencti:4000")
8+
api_token = os.getenv("OPENCTI_API_TOKEN", "bfa014e0-e02e-4aa6-a42b-603b19dcf159")
9+
10+
# OpenCTI initialization
11+
opencti_api_client = OpenCTIApiClient(api_url, api_token)
12+
13+
observable_certificate = opencti_api_client.stix_cyber_observable.create(
14+
observableData={
15+
"type": "x509-certificate",
16+
"hashes": {
17+
"SHA-1": "3ba7e9f806eb30d2f4e3f905e53f07e9acf08e1e",
18+
"SHA-256": "73b8ed5becf1ba6493d2e2215a42dfdc7877e91e311ff5e59fb43d094871e699",
19+
"MD5": "956f4b8a30ec423d4bbec9ec60df71df",
20+
},
21+
"serial_number": "3311565258528077731295218946714536456",
22+
"signature_algorithm": "SHA256-RSA",
23+
"issuer": "C=US, O=DigiCert Inc, CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1",
24+
"validity_not_before": "2025-01-02T00:00:00Z",
25+
"validity_not_after": "2026-01-21T23:59:59Z",
26+
"subject": "C=US, ST=California, L=San Francisco, O=Cloudflare\\, Inc., CN=cloudflare-dns.com",
27+
"subject_public_key_algorithm": "ECDSA",
28+
"authority_key_identifier": "748580c066c7df37decfbd2937aa031dbeedcd17",
29+
"basic_constraints": '{"is_ca":null,"max_path_len":null}',
30+
"certificate_policies": "[CertificatePolicy(cps=['http://www.digicert.com/CPS'], id='2.23.140.1.2.2', user_notice=Unset())]",
31+
"crl_distribution_points": "['http://crl3.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl', 'http://crl4.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl']",
32+
"extended_key_usage": '{"client_auth":true,"server_auth":true}',
33+
"key_usage": '{"certificate_sign":null,"content_commitment":null,"crl_sign":null,"data_encipherment":null,"decipher_only":null,"digital_signature":true,"encipher_only":null,"key_agreement":true,"key_encipherment":null,"value":17}',
34+
}
35+
)
36+
37+
print(observable_certificate)

pycti/entities/opencti_stix_core_object.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,22 @@ def __init__(self, opencti, file):
579579
algorithm
580580
hash
581581
}
582+
basic_constraints
583+
name_constraints
584+
policy_constraints
585+
key_usage
586+
extended_key_usage
587+
subject_key_identifier
588+
authority_key_identifier
589+
subject_alternative_name
590+
issuer_alternative_name
591+
subject_directory_attributes
592+
crl_distribution_points
593+
inhibit_any_policy
594+
private_key_usage_period_not_before
595+
private_key_usage_period_not_after
596+
certificate_policies
597+
policy_mappings
582598
}
583599
... on IPv4Addr {
584600
value
@@ -1290,6 +1306,22 @@ def __init__(self, opencti, file):
12901306
algorithm
12911307
hash
12921308
}
1309+
basic_constraints
1310+
name_constraints
1311+
policy_constraints
1312+
key_usage
1313+
extended_key_usage
1314+
subject_key_identifier
1315+
authority_key_identifier
1316+
subject_alternative_name
1317+
issuer_alternative_name
1318+
subject_directory_attributes
1319+
crl_distribution_points
1320+
inhibit_any_policy
1321+
private_key_usage_period_not_before
1322+
private_key_usage_period_not_after
1323+
certificate_policies
1324+
policy_mappings
12931325
}
12941326
... on IPv4Addr {
12951327
value

pycti/entities/opencti_stix_cyber_observable.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,86 @@ def create(self, **kwargs):
716716
if "subject_public_key_exponent" in observable_data
717717
else None
718718
),
719+
"basic_constraints": (
720+
observable_data["basic_constraints"]
721+
if "basic_constraints" in observable_data
722+
else None
723+
),
724+
"name_constraints": (
725+
observable_data["name_constraints"]
726+
if "name_constraints" in observable_data
727+
else None
728+
),
729+
"policy_constraints": (
730+
observable_data["policy_constraints"]
731+
if "policy_constraints" in observable_data
732+
else None
733+
),
734+
"key_usage": (
735+
observable_data["key_usage"]
736+
if "key_usage" in observable_data
737+
else None
738+
),
739+
"extended_key_usage": (
740+
observable_data["extended_key_usage"]
741+
if "extended_key_usage" in observable_data
742+
else None
743+
),
744+
"subject_key_identifier": (
745+
observable_data["subject_key_identifier"]
746+
if "subject_key_identifier" in observable_data
747+
else None
748+
),
749+
"authority_key_identifier": (
750+
observable_data["authority_key_identifier"]
751+
if "authority_key_identifier" in observable_data
752+
else None
753+
),
754+
"subject_alternative_name": (
755+
observable_data["subject_alternative_name"]
756+
if "subject_alternative_name" in observable_data
757+
else None
758+
),
759+
"issuer_alternative_name": (
760+
observable_data["issuer_alternative_name"]
761+
if "issuer_alternative_name" in observable_data
762+
else None
763+
),
764+
"subject_directory_attributes": (
765+
observable_data["subject_directory_attributes"]
766+
if "subject_directory_attributes" in observable_data
767+
else None
768+
),
769+
"crl_distribution_points": (
770+
observable_data["crl_distribution_points"]
771+
if "crl_distribution_points" in observable_data
772+
else None
773+
),
774+
"inhibit_any_policy": (
775+
observable_data["inhibit_any_policy"]
776+
if "inhibit_any_policy" in observable_data
777+
else None
778+
),
779+
"private_key_usage_period_not_before": (
780+
observable_data["private_key_usage_period_not_before"]
781+
if "private_key_usage_period_not_before" in observable_data
782+
else None
783+
),
784+
"private_key_usage_period_not_after": (
785+
observable_data["private_key_usage_period_not_after"]
786+
if "private_key_usage_period_not_after" in observable_data
787+
else None
788+
),
789+
"certificate_policies": (
790+
observable_data["certificate_policies"]
791+
if "certificate_policies" in observable_data
792+
else None
793+
),
794+
"policy_mappings": (
795+
observable_data["policy_mappings"]
796+
if "policy_mappings" in observable_data
797+
else None
798+
),
719799
}
720800
elif type == "SSH-Key" or type.lower() == "ssh-key":
721801
input_variables["SSHKey"] = {

pycti/entities/stix_cyber_observable/opencti_stix_cyber_observable_properties.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,22 @@
173173
algorithm
174174
hash
175175
}
176+
basic_constraints
177+
name_constraints
178+
policy_constraints
179+
key_usage
180+
extended_key_usage
181+
subject_key_identifier
182+
authority_key_identifier
183+
subject_alternative_name
184+
issuer_alternative_name
185+
subject_directory_attributes
186+
crl_distribution_points
187+
inhibit_any_policy
188+
private_key_usage_period_not_before
189+
private_key_usage_period_not_after
190+
certificate_policies
191+
policy_mappings
176192
}
177193
... on SSHKey {
178194
key_type
@@ -488,6 +504,22 @@
488504
algorithm
489505
hash
490506
}
507+
basic_constraints
508+
name_constraints
509+
policy_constraints
510+
key_usage
511+
extended_key_usage
512+
subject_key_identifier
513+
authority_key_identifier
514+
subject_alternative_name
515+
issuer_alternative_name
516+
subject_directory_attributes
517+
crl_distribution_points
518+
inhibit_any_policy
519+
private_key_usage_period_not_before
520+
private_key_usage_period_not_after
521+
certificate_policies
522+
policy_mappings
491523
}
492524
... on SSHKey {
493525
key_type

tests/02-integration/entities/test_observables.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# coding: utf-8
2+
import datetime
3+
import json
24

35

46
def test_promote_observable_to_indicator_deprecated(api_client):
@@ -11,3 +13,90 @@ def test_promote_observable_to_indicator_deprecated(api_client):
1113
)
1214
assert observable is not None, "Returned observable is NoneType"
1315
assert observable.get("id") == obs1.get("id")
16+
17+
18+
def test_certificate_creation_mapping(api_client):
19+
with open("tests/data/certificate.json", "r") as content_file:
20+
content = content_file.read()
21+
22+
imported_malware_bundle, _ = api_client.stix2.import_bundle_from_json(
23+
json_data=content
24+
)
25+
assert imported_malware_bundle is not None
26+
27+
bundle = json.loads(content)
28+
stix_certificate = bundle["objects"][0]
29+
certificate = api_client.stix_cyber_observable.read(
30+
id=imported_malware_bundle[0]["id"]
31+
)
32+
33+
assert certificate["entity_type"] == "X509-Certificate"
34+
35+
assert certificate["is_self_signed"] == stix_certificate["is_self_signed"]
36+
assert certificate["serial_number"] == stix_certificate["serial_number"]
37+
assert certificate["signature_algorithm"] == stix_certificate["signature_algorithm"]
38+
assert certificate["issuer"] == stix_certificate["issuer"]
39+
assert certificate["subject"] == stix_certificate["subject"]
40+
assert (
41+
certificate["subject_public_key_algorithm"]
42+
== stix_certificate["subject_public_key_algorithm"]
43+
)
44+
assert (
45+
certificate["subject_public_key_modulus"]
46+
== stix_certificate["subject_public_key_modulus"]
47+
)
48+
assert (
49+
certificate["subject_public_key_exponent"]
50+
== stix_certificate["subject_public_key_exponent"]
51+
)
52+
assert datetime.datetime.fromisoformat(
53+
certificate["validity_not_before"]
54+
) == datetime.datetime.fromisoformat(stix_certificate["validity_not_before"])
55+
assert datetime.datetime.fromisoformat(
56+
certificate["validity_not_after"]
57+
) == datetime.datetime.fromisoformat(stix_certificate["validity_not_after"])
58+
assert {
59+
item["algorithm"]: item["hash"] for item in certificate["hashes"]
60+
} == stix_certificate["hashes"]
61+
assert certificate["basic_constraints"] == stix_certificate["basic_constraints"]
62+
assert certificate["name_constraints"] == stix_certificate["name_constraints"]
63+
assert certificate["policy_constraints"] == stix_certificate["policy_constraints"]
64+
assert certificate["key_usage"] == stix_certificate["key_usage"]
65+
assert certificate["extended_key_usage"] == stix_certificate["extended_key_usage"]
66+
assert (
67+
certificate["subject_key_identifier"]
68+
== stix_certificate["subject_key_identifier"]
69+
)
70+
assert (
71+
certificate["authority_key_identifier"]
72+
== stix_certificate["authority_key_identifier"]
73+
)
74+
assert (
75+
certificate["subject_alternative_name"]
76+
== stix_certificate["subject_alternative_name"]
77+
)
78+
assert (
79+
certificate["issuer_alternative_name"]
80+
== stix_certificate["issuer_alternative_name"]
81+
)
82+
assert (
83+
certificate["subject_directory_attributes"]
84+
== stix_certificate["subject_directory_attributes"]
85+
)
86+
assert (
87+
certificate["crl_distribution_points"]
88+
== stix_certificate["crl_distribution_points"]
89+
)
90+
assert certificate["inhibit_any_policy"] == stix_certificate["inhibit_any_policy"]
91+
assert (
92+
certificate["private_key_usage_period_not_before"]
93+
== stix_certificate["private_key_usage_period_not_before"]
94+
)
95+
assert (
96+
certificate["private_key_usage_period_not_after"]
97+
== stix_certificate["private_key_usage_period_not_after"]
98+
)
99+
assert (
100+
certificate["certificate_policies"] == stix_certificate["certificate_policies"]
101+
)
102+
assert certificate["policy_mappings"] == stix_certificate["policy_mappings"]

tests/data/certificate.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"type": "bundle",
3+
"id": "bundle--e6f05bac-a28a-47b3-9cab-fa59b49a8c34",
4+
"spec_version": "2.1",
5+
"objects": [
6+
{
7+
"type": "x509-certificate",
8+
"spec_version": "2.1",
9+
"id": "x509-certificate--9a4c2364-468d-5e8f-88de-49cd06cc5249",
10+
"is_self_signed": false,
11+
"hashes": {
12+
"SHA-1": "3ba7e9f806eb30d2f4e3f905e53f07e9acf08e1e",
13+
"SHA-256": "73b8ed5becf1ba6493d2e2215a42dfdc7877e91e311ff5e59fb43d094871e699",
14+
"MD5": "956f4b8a30ec423d4bbec9ec60df71df"
15+
},
16+
"serial_number": "3311565258528077731295218946714536456",
17+
"signature_algorithm": "SHA256-RSA",
18+
"issuer": "C=US, O=DigiCert Inc, CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1",
19+
"validity_not_before": "2025-01-02T00:00:00Z",
20+
"validity_not_after": "2026-01-21T23:59:59Z",
21+
"subject": "C=US, ST=California, L=San Francisco, O=Cloudflare\\, Inc., CN=cloudflare-dns.com",
22+
"subject_public_key_algorithm": "ECDSA",
23+
"subject_public_key_modulus": "04b0fc3e2f6d8c5e8f8e8c3d6c7a4e5f6b7c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f80",
24+
"subject_public_key_exponent": "65537",
25+
"object_marking_refs": [],
26+
"authority_key_identifier": "748580c066c7df37decfbd2937aa031dbeedcd17",
27+
"basic_constraints": "{\"is_ca\":null,\"max_path_len\":null}",
28+
"name_constraints": "{\"excluded_subtrees\":null,\"permitted_subtrees\":null}",
29+
"policy_constraints": "{\"require_explicit_policy\":null,\"inhibit_policy_mapping\":null}",
30+
"subject_key_identifier": "d4c8e1f3b5a67c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7f80",
31+
"subject_alternative_name": "{\"dns_names\":[\"cloudflare-dns.com\",\"www.cloudflare-dns.com\"],\"email_addresses\":null,\"ip_addresses\":null,\"uris\":null}",
32+
"issuer_alternative_name": "Unset()",
33+
"subject_directory_attributes": "Unset()",
34+
"inhibit_any_policy": "Unset()",
35+
"private_key_usage_period_not_before": "2025-01-02T00:00:00Z",
36+
"private_key_usage_period_not_after": "2026-01-21T23:59:59Z",
37+
"certificate_policies": "[CertificatePolicy(cps=['http://www.digicert.com/CPS'], id='2.23.140.1.2.2', user_notice=Unset())]",
38+
"crl_distribution_points": "['http://crl3.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl', 'http://crl4.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl']",
39+
"extended_key_usage": "{\"client_auth\":true,\"server_auth\":true}",
40+
"key_usage": "{\"certificate_sign\":null,\"content_commitment\":null,\"crl_sign\":null,\"data_encipherment\":null,\"decipher_only\":null,\"digital_signature\":true,\"encipher_only\":null,\"key_agreement\":true,\"key_encipherment\":null,\"value\":17}"
41+
}
42+
]
43+
}

0 commit comments

Comments
 (0)