Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions examples/create_observable_x509_certificate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# coding: utf-8
import os

from pycti import OpenCTIApiClient

# Variables
api_url = os.getenv("OPENCTI_API_URL", "http://opencti:4000")
api_token = os.getenv("OPENCTI_API_TOKEN", "bfa014e0-e02e-4aa6-a42b-603b19dcf159")

# OpenCTI initialization
opencti_api_client = OpenCTIApiClient(api_url, api_token)

observable_certificate = opencti_api_client.stix_cyber_observable.create(
observableData={
"type": "x509-certificate",
"hashes": {
"SHA-1": "3ba7e9f806eb30d2f4e3f905e53f07e9acf08e1e",
"SHA-256": "73b8ed5becf1ba6493d2e2215a42dfdc7877e91e311ff5e59fb43d094871e699",
"MD5": "956f4b8a30ec423d4bbec9ec60df71df",
},
"serial_number": "3311565258528077731295218946714536456",
"signature_algorithm": "SHA256-RSA",
"issuer": "C=US, O=DigiCert Inc, CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1",
"validity_not_before": "2025-01-02T00:00:00Z",
"validity_not_after": "2026-01-21T23:59:59Z",
"subject": "C=US, ST=California, L=San Francisco, O=Cloudflare\\, Inc., CN=cloudflare-dns.com",
"subject_public_key_algorithm": "ECDSA",
"authority_key_identifier": "748580c066c7df37decfbd2937aa031dbeedcd17",
"basic_constraints": '{"is_ca":null,"max_path_len":null}',
"certificate_policies": "[CertificatePolicy(cps=['http://www.digicert.com/CPS'], id='2.23.140.1.2.2', user_notice=Unset())]",
"crl_distribution_points": "['http://crl3.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl', 'http://crl4.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl']",
"extended_key_usage": '{"client_auth":true,"server_auth":true}',
"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}',
}
)

print(observable_certificate)
32 changes: 32 additions & 0 deletions pycti/entities/opencti_stix_core_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,22 @@ def __init__(self, opencti, file):
algorithm
hash
}
basic_constraints
name_constraints
policy_constraints
key_usage
extended_key_usage
subject_key_identifier
authority_key_identifier
subject_alternative_name
issuer_alternative_name
subject_directory_attributes
crl_distribution_points
inhibit_any_policy
private_key_usage_period_not_before
private_key_usage_period_not_after
certificate_policies
policy_mappings
}
... on IPv4Addr {
value
Expand Down Expand Up @@ -1290,6 +1306,22 @@ def __init__(self, opencti, file):
algorithm
hash
}
basic_constraints
name_constraints
policy_constraints
key_usage
extended_key_usage
subject_key_identifier
authority_key_identifier
subject_alternative_name
issuer_alternative_name
subject_directory_attributes
crl_distribution_points
inhibit_any_policy
private_key_usage_period_not_before
private_key_usage_period_not_after
certificate_policies
policy_mappings
}
... on IPv4Addr {
value
Expand Down
80 changes: 80 additions & 0 deletions pycti/entities/opencti_stix_cyber_observable.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,86 @@ def create(self, **kwargs):
if "subject_public_key_exponent" in observable_data
else None
),
"basic_constraints": (
observable_data["basic_constraints"]
if "basic_constraints" in observable_data
else None
),
"name_constraints": (
observable_data["name_constraints"]
if "name_constraints" in observable_data
else None
),
"policy_constraints": (
observable_data["policy_constraints"]
if "policy_constraints" in observable_data
else None
),
"key_usage": (
observable_data["key_usage"]
if "key_usage" in observable_data
else None
),
"extended_key_usage": (
observable_data["extended_key_usage"]
if "extended_key_usage" in observable_data
else None
),
"subject_key_identifier": (
observable_data["subject_key_identifier"]
if "subject_key_identifier" in observable_data
else None
),
"authority_key_identifier": (
observable_data["authority_key_identifier"]
if "authority_key_identifier" in observable_data
else None
),
"subject_alternative_name": (
observable_data["subject_alternative_name"]
if "subject_alternative_name" in observable_data
else None
),
"issuer_alternative_name": (
observable_data["issuer_alternative_name"]
if "issuer_alternative_name" in observable_data
else None
),
"subject_directory_attributes": (
observable_data["subject_directory_attributes"]
if "subject_directory_attributes" in observable_data
else None
),
"crl_distribution_points": (
observable_data["crl_distribution_points"]
if "crl_distribution_points" in observable_data
else None
),
"inhibit_any_policy": (
observable_data["inhibit_any_policy"]
if "inhibit_any_policy" in observable_data
else None
),
"private_key_usage_period_not_before": (
observable_data["private_key_usage_period_not_before"]
if "private_key_usage_period_not_before" in observable_data
else None
),
"private_key_usage_period_not_after": (
observable_data["private_key_usage_period_not_after"]
if "private_key_usage_period_not_after" in observable_data
else None
),
"certificate_policies": (
observable_data["certificate_policies"]
if "certificate_policies" in observable_data
else None
),
"policy_mappings": (
observable_data["policy_mappings"]
if "policy_mappings" in observable_data
else None
),
}
elif type == "SSH-Key" or type.lower() == "ssh-key":
input_variables["SSHKey"] = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@
algorithm
hash
}
basic_constraints
name_constraints
policy_constraints
key_usage
extended_key_usage
subject_key_identifier
authority_key_identifier
subject_alternative_name
issuer_alternative_name
subject_directory_attributes
crl_distribution_points
inhibit_any_policy
private_key_usage_period_not_before
private_key_usage_period_not_after
certificate_policies
policy_mappings
}
... on SSHKey {
key_type
Expand Down Expand Up @@ -488,6 +504,22 @@
algorithm
hash
}
basic_constraints
name_constraints
policy_constraints
key_usage
extended_key_usage
subject_key_identifier
authority_key_identifier
subject_alternative_name
issuer_alternative_name
subject_directory_attributes
crl_distribution_points
inhibit_any_policy
private_key_usage_period_not_before
private_key_usage_period_not_after
certificate_policies
policy_mappings
}
... on SSHKey {
key_type
Expand Down
32 changes: 32 additions & 0 deletions tests/02-integration/entities/test_observables.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# coding: utf-8
import datetime
import json


def test_promote_observable_to_indicator_deprecated(api_client):
Expand All @@ -11,3 +13,33 @@ def test_promote_observable_to_indicator_deprecated(api_client):
)
assert observable is not None, "Returned observable is NoneType"
assert observable.get("id") == obs1.get("id")


def test_certificate_creation_mapping(api_client):
with open("tests/data/certificate.json", "r") as content_file:
content = json.loads(content_file.read())

result = api_client.stix_cyber_observable.create(observableData=content)
assert result is not None

certificate = api_client.stix_cyber_observable.read(id=result["id"])

for key in content:
if key == "type":
assert certificate["entity_type"] == "X509-Certificate"
elif key == "hashes":
assert {
item["algorithm"]: item["hash"] for item in certificate["hashes"]
} == content["hashes"]
elif key in [
"validity_not_before",
"validity_not_after",
"private_key_usage_period_not_before",
"private_key_usage_period_not_after",
]:
assert datetime.datetime.fromisoformat(
certificate[key].replace("Z", "+00:00")
) == datetime.datetime.fromisoformat(content[key].replace("Z", "+00:00"))

else:
assert certificate[key] == content[key]
34 changes: 34 additions & 0 deletions tests/data/certificate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"type": "x509-certificate",
"is_self_signed": false,
"hashes": {
"SHA-1": "3ba7e9f806eb30d2f4e3f905e53f07e9acf08e1e",
"SHA-256": "73b8ed5becf1ba6493d2e2215a42dfdc7877e91e311ff5e59fb43d094871e699",
"MD5": "956f4b8a30ec423d4bbec9ec60df71df"
},
"serial_number": "3311565258528077731295218946714536456",
"signature_algorithm": "SHA256-RSA",
"issuer": "C=US, O=DigiCert Inc, CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1",
"validity_not_before": "2025-01-02T00:00:00Z",
"validity_not_after": "2026-01-21T23:59:59Z",
"subject": "C=US, ST=California, L=San Francisco, O=Cloudflare\\, Inc., CN=cloudflare-dns.com",
"subject_public_key_algorithm": "ECDSA",
"subject_public_key_modulus": "04b0fc3e2f6d8c5e8f8e8c3d6c7a4e5f6b7c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f80",
"subject_public_key_exponent": 65537,
"authority_key_identifier": "748580c066c7df37decfbd2937aa031dbeedcd17",
"basic_constraints": "{\"is_ca\":null,\"max_path_len\":null}",
"name_constraints": "{\"excluded_subtrees\":null,\"permitted_subtrees\":null}",
"policy_constraints": "{\"require_explicit_policy\":null,\"inhibit_policy_mapping\":null}",
"subject_key_identifier": "d4c8e1f3b5a67c8d9e0f1a2b3c4d5e6f708192a3b4c5d6e7f80",
"subject_alternative_name": "{\"dns_names\":[\"cloudflare-dns.com\",\"www.cloudflare-dns.com\"],\"email_addresses\":null,\"ip_addresses\":null,\"uris\":null}",
"issuer_alternative_name": "Unset()",
"subject_directory_attributes": "Unset()",
"inhibit_any_policy": "Unset()",
"private_key_usage_period_not_before": "2025-01-02T00:00:00Z",
"private_key_usage_period_not_after": "2026-01-21T23:59:59Z",
"certificate_policies": "[CertificatePolicy(cps=['http://www.digicert.com/CPS'], id='2.23.140.1.2.2', user_notice=Unset())]",
"policy_mappings": "Unset()",
"crl_distribution_points": "['http://crl3.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl', 'http://crl4.digicert.com/DigiCertGlobalG2TLSRSASHA2562020CA1-1.crl']",
"extended_key_usage": "{\"client_auth\":true,\"server_auth\":true}",
"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}"
}