Skip to content

Commit 5793e69

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

File tree

6 files changed

+247
-0
lines changed

6 files changed

+247
-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: 32 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,33 @@ 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 = json.loads(content_file.read())
21+
22+
result = api_client.stix_cyber_observable.create(observableData=content)
23+
assert result is not None
24+
25+
certificate = api_client.stix_cyber_observable.read(id=result["id"])
26+
27+
for key in content:
28+
if key == "type":
29+
assert certificate["entity_type"] == "X509-Certificate"
30+
elif key == "hashes":
31+
assert {
32+
item["algorithm"]: item["hash"] for item in certificate["hashes"]
33+
} == content["hashes"]
34+
elif key in [
35+
"validity_not_before",
36+
"validity_not_after",
37+
"private_key_usage_period_not_before",
38+
"private_key_usage_period_not_after",
39+
]:
40+
assert datetime.datetime.fromisoformat(
41+
certificate[key].replace("Z", "+00:00")
42+
) == datetime.datetime.fromisoformat(content[key].replace("Z", "+00:00"))
43+
44+
else:
45+
assert certificate[key] == content[key]

tests/data/certificate.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"type": "x509-certificate",
3+
"is_self_signed": false,
4+
"hashes": {
5+
"SHA-1": "3ba7e9f806eb30d2f4e3f905e53f07e9acf08e1e",
6+
"SHA-256": "73b8ed5becf1ba6493d2e2215a42dfdc7877e91e311ff5e59fb43d094871e699",
7+
"MD5": "956f4b8a30ec423d4bbec9ec60df71df"
8+
},
9+
"serial_number": "3311565258528077731295218946714536456",
10+
"signature_algorithm": "SHA256-RSA",
11+
"issuer": "C=US, O=DigiCert Inc, CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1",
12+
"validity_not_before": "2025-01-02T00:00:00Z",
13+
"validity_not_after": "2026-01-21T23:59:59Z",
14+
"subject": "C=US, ST=California, L=San Francisco, O=Cloudflare\\, Inc., CN=cloudflare-dns.com",
15+
"subject_public_key_algorithm": "ECDSA",
16+
"subject_public_key_modulus": "04b0fc3e2f6d8c5e8f8e8c3d6c7a4e5f6b7c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f80",
17+
"subject_public_key_exponent": 65537,
18+
"authority_key_identifier": "748580c066c7df37decfbd2937aa031dbeedcd17",
19+
"basic_constraints": "{\"is_ca\":null,\"max_path_len\":null}",
20+
"name_constraints": "{\"excluded_subtrees\":null,\"permitted_subtrees\":null}",
21+
"policy_constraints": "{\"require_explicit_policy\":null,\"inhibit_policy_mapping\":null}",
22+
"subject_key_identifier": "d4c8e1f3b5a67c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7f80",
23+
"subject_alternative_name": "{\"dns_names\":[\"cloudflare-dns.com\",\"www.cloudflare-dns.com\"],\"email_addresses\":null,\"ip_addresses\":null,\"uris\":null}",
24+
"issuer_alternative_name": "Unset()",
25+
"subject_directory_attributes": "Unset()",
26+
"inhibit_any_policy": "Unset()",
27+
"private_key_usage_period_not_before": "2025-01-02T00:00:00Z",
28+
"private_key_usage_period_not_after": "2026-01-21T23:59:59Z",
29+
"certificate_policies": "[CertificatePolicy(cps=['http://www.digicert.com/CPS'], id='2.23.140.1.2.2', user_notice=Unset())]",
30+
"policy_mappings": "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+
}

0 commit comments

Comments
 (0)