smdpp: add proper tls support, cert generation FOR TESTING
If TLS is enabled (default) it will automagically generate missing pem files + dh params. A faithful reproduction of the certs found in SGP.26_v1.5_Certificates_18_07_2024.zip available at https://www.gsma.com/solutions-and-impact/technologies/esim/gsma_resources/sgp-26-test-certificate-definition-v1-5/ can be generated by running contrib/generate_certs.py. This allows adjusting the expiry dates, CA flag, and other parameters FOR TESTING. Certs can be used by the smdpp by running $ python -u osmo-smdpp.py -c generated Change-Id: I84b2666422b8ff565620f3827ef4d4d7635a21be
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -10,3 +10,6 @@
|
|||||||
/smdpp-data/sm-dp-sessions
|
/smdpp-data/sm-dp-sessions
|
||||||
dist
|
dist
|
||||||
tags
|
tags
|
||||||
|
smdpp-data/certs/DPtls/CERT_S_SM_DP_TLS_NIST.pem
|
||||||
|
smdpp-data/generated
|
||||||
|
smdpp-data/certs/dhparam2048.pem
|
||||||
|
|||||||
661
contrib/generate_smdpp_certs.py
Executable file
661
contrib/generate_smdpp_certs.py
Executable file
@@ -0,0 +1,661 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Faithfully reproduces the smdpp certs contained in SGP.26_v1.5_Certificates_18_07_2024.zip
|
||||||
|
available at https://www.gsma.com/solutions-and-impact/technologies/esim/gsma_resources/sgp-26-test-certificate-definition-v1-5/
|
||||||
|
Only usable for testing, it obviously uses a different CI key.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import binascii
|
||||||
|
from datetime import datetime
|
||||||
|
from cryptography import x509
|
||||||
|
from cryptography.x509.oid import NameOID, ExtensionOID
|
||||||
|
from cryptography.hazmat.primitives import hashes, serialization
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
|
||||||
|
# Custom OIDs used in certificates
|
||||||
|
OID_CERTIFICATE_POLICIES_CI = "2.23.146.1.2.1.0" # CI cert policy
|
||||||
|
OID_CERTIFICATE_POLICIES_TLS = "2.23.146.1.2.1.3" # DPtls cert policy
|
||||||
|
OID_CERTIFICATE_POLICIES_AUTH = "2.23.146.1.2.1.4" # DPauth cert policy
|
||||||
|
OID_CERTIFICATE_POLICIES_PB = "2.23.146.1.2.1.5" # DPpb cert policy
|
||||||
|
|
||||||
|
# Subject Alternative Name OIDs
|
||||||
|
OID_CI_RID = "2.999.1" # CI Registered ID
|
||||||
|
OID_DP_RID = "2.999.10" # DP+ Registered ID
|
||||||
|
OID_DP2_RID = "2.999.12" # DP+2 Registered ID
|
||||||
|
OID_DP4_RID = "2.999.14" # DP+4 Registered ID
|
||||||
|
OID_DP8_RID = "2.999.18" # DP+8 Registered ID
|
||||||
|
|
||||||
|
|
||||||
|
class SimplifiedCertificateGenerator:
|
||||||
|
def __init__(self):
|
||||||
|
self.backend = default_backend()
|
||||||
|
# Store generated CI keys to sign other certs
|
||||||
|
self.ci_certs = {} # {"BRP": cert, "NIST": cert}
|
||||||
|
self.ci_keys = {} # {"BRP": key, "NIST": key}
|
||||||
|
|
||||||
|
def get_curve(self, curve_type):
|
||||||
|
"""Get the appropriate curve object."""
|
||||||
|
if curve_type == "BRP":
|
||||||
|
return ec.BrainpoolP256R1()
|
||||||
|
else:
|
||||||
|
return ec.SECP256R1()
|
||||||
|
|
||||||
|
def generate_key_pair(self, curve):
|
||||||
|
"""Generate a new EC key pair."""
|
||||||
|
private_key = ec.generate_private_key(curve, self.backend)
|
||||||
|
return private_key
|
||||||
|
|
||||||
|
def load_private_key_from_hex(self, hex_key, curve):
|
||||||
|
"""Load EC private key from hex string."""
|
||||||
|
key_bytes = binascii.unhexlify(hex_key.replace(":", "").replace(" ", "").replace("\n", ""))
|
||||||
|
key_int = int.from_bytes(key_bytes, 'big')
|
||||||
|
return ec.derive_private_key(key_int, curve, self.backend)
|
||||||
|
|
||||||
|
def generate_ci_cert(self, curve_type):
|
||||||
|
"""Generate CI certificate for either BRP or NIST curve."""
|
||||||
|
curve = self.get_curve(curve_type)
|
||||||
|
private_key = self.generate_key_pair(curve)
|
||||||
|
|
||||||
|
# Build subject and issuer (self-signed) - same for both
|
||||||
|
subject = issuer = x509.Name([
|
||||||
|
x509.NameAttribute(NameOID.COMMON_NAME, "Test CI"),
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "TESTCERT"),
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "RSPTEST"),
|
||||||
|
x509.NameAttribute(NameOID.COUNTRY_NAME, "IT"),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Build certificate - all parameters same for both
|
||||||
|
builder = x509.CertificateBuilder()
|
||||||
|
builder = builder.subject_name(subject)
|
||||||
|
builder = builder.issuer_name(issuer)
|
||||||
|
builder = builder.not_valid_before(datetime(2020, 4, 1, 8, 27, 51))
|
||||||
|
builder = builder.not_valid_after(datetime(2055, 4, 1, 8, 27, 51))
|
||||||
|
builder = builder.serial_number(0xb874f3abfa6c44d3)
|
||||||
|
builder = builder.public_key(private_key.public_key())
|
||||||
|
|
||||||
|
# Add extensions - all same for both
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.BasicConstraints(ca=True, path_length=None),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CertificatePolicies([
|
||||||
|
x509.PolicyInformation(
|
||||||
|
x509.ObjectIdentifier(OID_CERTIFICATE_POLICIES_CI),
|
||||||
|
policy_qualifiers=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.KeyUsage(
|
||||||
|
digital_signature=False,
|
||||||
|
content_commitment=False,
|
||||||
|
key_encipherment=False,
|
||||||
|
data_encipherment=False,
|
||||||
|
key_agreement=False,
|
||||||
|
key_cert_sign=True,
|
||||||
|
crl_sign=True,
|
||||||
|
encipher_only=False,
|
||||||
|
decipher_only=False
|
||||||
|
),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectAlternativeName([
|
||||||
|
x509.RegisteredID(x509.ObjectIdentifier(OID_CI_RID))
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CRLDistributionPoints([
|
||||||
|
x509.DistributionPoint(
|
||||||
|
full_name=[x509.UniformResourceIdentifier("http://ci.test.example.com/CRL-A.crl")],
|
||||||
|
relative_name=None,
|
||||||
|
reasons=None,
|
||||||
|
crl_issuer=None
|
||||||
|
),
|
||||||
|
x509.DistributionPoint(
|
||||||
|
full_name=[x509.UniformResourceIdentifier("http://ci.test.example.com/CRL-B.crl")],
|
||||||
|
relative_name=None,
|
||||||
|
reasons=None,
|
||||||
|
crl_issuer=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
certificate = builder.sign(private_key, hashes.SHA256(), self.backend)
|
||||||
|
|
||||||
|
self.ci_keys[curve_type] = private_key
|
||||||
|
self.ci_certs[curve_type] = certificate
|
||||||
|
|
||||||
|
return certificate, private_key
|
||||||
|
|
||||||
|
def generate_dp_cert(self, curve_type, subject_cn, serial, key_hex,
|
||||||
|
cert_policy_oid, rid_oid, validity_start, validity_end):
|
||||||
|
"""Generate a DP certificate signed by CI - works for both BRP and NIST."""
|
||||||
|
curve = self.get_curve(curve_type)
|
||||||
|
private_key = self.load_private_key_from_hex(key_hex, curve)
|
||||||
|
|
||||||
|
ci_cert = self.ci_certs[curve_type]
|
||||||
|
ci_key = self.ci_keys[curve_type]
|
||||||
|
|
||||||
|
subject = x509.Name([
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "ACME"),
|
||||||
|
x509.NameAttribute(NameOID.COMMON_NAME, subject_cn),
|
||||||
|
])
|
||||||
|
|
||||||
|
builder = x509.CertificateBuilder()
|
||||||
|
builder = builder.subject_name(subject)
|
||||||
|
builder = builder.issuer_name(ci_cert.subject)
|
||||||
|
builder = builder.not_valid_before(validity_start)
|
||||||
|
builder = builder.not_valid_after(validity_end)
|
||||||
|
builder = builder.serial_number(serial)
|
||||||
|
builder = builder.public_key(private_key.public_key())
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.AuthorityKeyIdentifier.from_issuer_public_key(ci_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectAlternativeName([
|
||||||
|
x509.RegisteredID(x509.ObjectIdentifier(rid_oid))
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.KeyUsage(
|
||||||
|
digital_signature=True,
|
||||||
|
content_commitment=False,
|
||||||
|
key_encipherment=False,
|
||||||
|
data_encipherment=False,
|
||||||
|
key_agreement=False,
|
||||||
|
key_cert_sign=False,
|
||||||
|
crl_sign=False,
|
||||||
|
encipher_only=False,
|
||||||
|
decipher_only=False
|
||||||
|
),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CertificatePolicies([
|
||||||
|
x509.PolicyInformation(
|
||||||
|
x509.ObjectIdentifier(cert_policy_oid),
|
||||||
|
policy_qualifiers=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CRLDistributionPoints([
|
||||||
|
x509.DistributionPoint(
|
||||||
|
full_name=[x509.UniformResourceIdentifier("http://ci.test.example.com/CRL-A.crl")],
|
||||||
|
relative_name=None,
|
||||||
|
reasons=None,
|
||||||
|
crl_issuer=None
|
||||||
|
),
|
||||||
|
x509.DistributionPoint(
|
||||||
|
full_name=[x509.UniformResourceIdentifier("http://ci.test.example.com/CRL-B.crl")],
|
||||||
|
relative_name=None,
|
||||||
|
reasons=None,
|
||||||
|
crl_issuer=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
certificate = builder.sign(ci_key, hashes.SHA256(), self.backend)
|
||||||
|
|
||||||
|
return certificate, private_key
|
||||||
|
|
||||||
|
def generate_tls_cert(self, curve_type, subject_cn, dns_name, serial, key_hex,
|
||||||
|
rid_oid, validity_start, validity_end):
|
||||||
|
"""Generate a TLS certificate signed by CI."""
|
||||||
|
curve = self.get_curve(curve_type)
|
||||||
|
private_key = self.load_private_key_from_hex(key_hex, curve)
|
||||||
|
|
||||||
|
ci_cert = self.ci_certs[curve_type]
|
||||||
|
ci_key = self.ci_keys[curve_type]
|
||||||
|
|
||||||
|
subject = x509.Name([
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "ACME"),
|
||||||
|
x509.NameAttribute(NameOID.COMMON_NAME, subject_cn),
|
||||||
|
])
|
||||||
|
|
||||||
|
builder = x509.CertificateBuilder()
|
||||||
|
builder = builder.subject_name(subject)
|
||||||
|
builder = builder.issuer_name(ci_cert.subject)
|
||||||
|
builder = builder.not_valid_before(validity_start)
|
||||||
|
builder = builder.not_valid_after(validity_end)
|
||||||
|
builder = builder.serial_number(serial)
|
||||||
|
builder = builder.public_key(private_key.public_key())
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.KeyUsage(
|
||||||
|
digital_signature=True,
|
||||||
|
content_commitment=False,
|
||||||
|
key_encipherment=False,
|
||||||
|
data_encipherment=False,
|
||||||
|
key_agreement=False,
|
||||||
|
key_cert_sign=False,
|
||||||
|
crl_sign=False,
|
||||||
|
encipher_only=False,
|
||||||
|
decipher_only=False
|
||||||
|
),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.ExtendedKeyUsage([
|
||||||
|
x509.oid.ExtendedKeyUsageOID.SERVER_AUTH,
|
||||||
|
x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH
|
||||||
|
]),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CertificatePolicies([
|
||||||
|
x509.PolicyInformation(
|
||||||
|
x509.ObjectIdentifier(OID_CERTIFICATE_POLICIES_TLS),
|
||||||
|
policy_qualifiers=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.AuthorityKeyIdentifier.from_issuer_public_key(ci_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectAlternativeName([
|
||||||
|
x509.DNSName(dns_name),
|
||||||
|
x509.RegisteredID(x509.ObjectIdentifier(rid_oid))
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CRLDistributionPoints([
|
||||||
|
x509.DistributionPoint(
|
||||||
|
full_name=[x509.UniformResourceIdentifier("http://ci.test.example.com/CRL-A.crl")],
|
||||||
|
relative_name=None,
|
||||||
|
reasons=None,
|
||||||
|
crl_issuer=None
|
||||||
|
),
|
||||||
|
x509.DistributionPoint(
|
||||||
|
full_name=[x509.UniformResourceIdentifier("http://ci.test.example.com/CRL-B.crl")],
|
||||||
|
relative_name=None,
|
||||||
|
reasons=None,
|
||||||
|
crl_issuer=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
certificate = builder.sign(ci_key, hashes.SHA256(), self.backend)
|
||||||
|
|
||||||
|
return certificate, private_key
|
||||||
|
|
||||||
|
def generate_eum_cert(self, curve_type, key_hex):
|
||||||
|
"""Generate EUM certificate signed by CI."""
|
||||||
|
curve = self.get_curve(curve_type)
|
||||||
|
private_key = self.load_private_key_from_hex(key_hex, curve)
|
||||||
|
|
||||||
|
ci_cert = self.ci_certs[curve_type]
|
||||||
|
ci_key = self.ci_keys[curve_type]
|
||||||
|
|
||||||
|
subject = x509.Name([
|
||||||
|
x509.NameAttribute(NameOID.COUNTRY_NAME, "ES"),
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "RSP Test EUM"),
|
||||||
|
x509.NameAttribute(NameOID.COMMON_NAME, "EUM Test"),
|
||||||
|
])
|
||||||
|
|
||||||
|
builder = x509.CertificateBuilder()
|
||||||
|
builder = builder.subject_name(subject)
|
||||||
|
builder = builder.issuer_name(ci_cert.subject)
|
||||||
|
builder = builder.not_valid_before(datetime(2020, 4, 1, 9, 28, 37))
|
||||||
|
builder = builder.not_valid_after(datetime(2054, 3, 24, 9, 28, 37))
|
||||||
|
builder = builder.serial_number(0x12345678)
|
||||||
|
builder = builder.public_key(private_key.public_key())
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.AuthorityKeyIdentifier.from_issuer_public_key(ci_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.KeyUsage(
|
||||||
|
digital_signature=False,
|
||||||
|
content_commitment=False,
|
||||||
|
key_encipherment=False,
|
||||||
|
data_encipherment=False,
|
||||||
|
key_agreement=False,
|
||||||
|
key_cert_sign=True,
|
||||||
|
crl_sign=False,
|
||||||
|
encipher_only=False,
|
||||||
|
decipher_only=False
|
||||||
|
),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CertificatePolicies([
|
||||||
|
x509.PolicyInformation(
|
||||||
|
x509.ObjectIdentifier("2.23.146.1.2.1.2"), # EUM policy
|
||||||
|
policy_qualifiers=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectAlternativeName([
|
||||||
|
x509.RegisteredID(x509.ObjectIdentifier("2.999.5"))
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.BasicConstraints(ca=True, path_length=0),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CRLDistributionPoints([
|
||||||
|
x509.DistributionPoint(
|
||||||
|
full_name=[x509.UniformResourceIdentifier("http://ci.test.example.com/CRL-B.crl")],
|
||||||
|
relative_name=None,
|
||||||
|
reasons=None,
|
||||||
|
crl_issuer=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Name Constraints
|
||||||
|
constrained_name = x509.Name([
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "RSP Test EUM"),
|
||||||
|
x509.NameAttribute(NameOID.SERIAL_NUMBER, "89049032"),
|
||||||
|
])
|
||||||
|
|
||||||
|
name_constraints = x509.NameConstraints(
|
||||||
|
permitted_subtrees=[
|
||||||
|
x509.DirectoryName(constrained_name)
|
||||||
|
],
|
||||||
|
excluded_subtrees=None
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
name_constraints,
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
certificate = builder.sign(ci_key, hashes.SHA256(), self.backend)
|
||||||
|
|
||||||
|
return certificate, private_key
|
||||||
|
|
||||||
|
def generate_euicc_cert(self, curve_type, eum_cert, eum_key, key_hex):
|
||||||
|
"""Generate eUICC certificate signed by EUM."""
|
||||||
|
curve = self.get_curve(curve_type)
|
||||||
|
private_key = self.load_private_key_from_hex(key_hex, curve)
|
||||||
|
|
||||||
|
subject = x509.Name([
|
||||||
|
x509.NameAttribute(NameOID.COUNTRY_NAME, "ES"),
|
||||||
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "RSP Test EUM"),
|
||||||
|
x509.NameAttribute(NameOID.SERIAL_NUMBER, "89049032123451234512345678901235"),
|
||||||
|
x509.NameAttribute(NameOID.COMMON_NAME, "Test eUICC"),
|
||||||
|
])
|
||||||
|
|
||||||
|
builder = x509.CertificateBuilder()
|
||||||
|
builder = builder.subject_name(subject)
|
||||||
|
builder = builder.issuer_name(eum_cert.subject)
|
||||||
|
builder = builder.not_valid_before(datetime(2020, 4, 1, 9, 48, 58))
|
||||||
|
builder = builder.not_valid_after(datetime(7496, 1, 24, 9, 48, 58))
|
||||||
|
builder = builder.serial_number(0x0200000000000001)
|
||||||
|
builder = builder.public_key(private_key.public_key())
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.AuthorityKeyIdentifier.from_issuer_public_key(eum_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.SubjectKeyIdentifier.from_public_key(private_key.public_key()),
|
||||||
|
critical=False
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.KeyUsage(
|
||||||
|
digital_signature=True,
|
||||||
|
content_commitment=False,
|
||||||
|
key_encipherment=False,
|
||||||
|
data_encipherment=False,
|
||||||
|
key_agreement=False,
|
||||||
|
key_cert_sign=False,
|
||||||
|
crl_sign=False,
|
||||||
|
encipher_only=False,
|
||||||
|
decipher_only=False
|
||||||
|
),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
builder = builder.add_extension(
|
||||||
|
x509.CertificatePolicies([
|
||||||
|
x509.PolicyInformation(
|
||||||
|
x509.ObjectIdentifier("2.23.146.1.2.1.1"), # eUICC policy
|
||||||
|
policy_qualifiers=None
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
critical=True
|
||||||
|
)
|
||||||
|
|
||||||
|
certificate = builder.sign(eum_key, hashes.SHA256(), self.backend)
|
||||||
|
|
||||||
|
return certificate, private_key
|
||||||
|
|
||||||
|
def save_cert_and_key(self, cert, key, cert_path_der, cert_path_pem, key_path_sk, key_path_pk):
|
||||||
|
"""Save certificate and key in various formats."""
|
||||||
|
# Create directories if needed
|
||||||
|
os.makedirs(os.path.dirname(cert_path_der), exist_ok=True)
|
||||||
|
|
||||||
|
with open(cert_path_der, "wb") as f:
|
||||||
|
f.write(cert.public_bytes(serialization.Encoding.DER))
|
||||||
|
|
||||||
|
if cert_path_pem:
|
||||||
|
with open(cert_path_pem, "wb") as f:
|
||||||
|
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
||||||
|
|
||||||
|
if key and key_path_sk:
|
||||||
|
with open(key_path_sk, "wb") as f:
|
||||||
|
f.write(key.private_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||||
|
encryption_algorithm=serialization.NoEncryption()
|
||||||
|
))
|
||||||
|
|
||||||
|
if key and key_path_pk:
|
||||||
|
with open(key_path_pk, "wb") as f:
|
||||||
|
f.write(key.public_key().public_bytes(
|
||||||
|
encoding=serialization.Encoding.PEM,
|
||||||
|
format=serialization.PublicFormat.SubjectPublicKeyInfo
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
gen = SimplifiedCertificateGenerator()
|
||||||
|
|
||||||
|
output_dir = "smdpp-data/generated"
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
print("=== Generating CI Certificates ===")
|
||||||
|
|
||||||
|
for curve_type in ["BRP", "NIST"]:
|
||||||
|
ci_cert, ci_key = gen.generate_ci_cert(curve_type)
|
||||||
|
suffix = "_ECDSA_BRP" if curve_type == "BRP" else "_ECDSA_NIST"
|
||||||
|
gen.save_cert_and_key(
|
||||||
|
ci_cert, ci_key,
|
||||||
|
f"{output_dir}/CertificateIssuer/CERT_CI{suffix}.der",
|
||||||
|
f"{output_dir}/CertificateIssuer/CERT_CI{suffix}.pem",
|
||||||
|
None, None
|
||||||
|
)
|
||||||
|
print(f"Generated CI {curve_type} certificate")
|
||||||
|
|
||||||
|
print("\n=== Generating DPauth Certificates ===")
|
||||||
|
|
||||||
|
dpauth_configs = [
|
||||||
|
("BRP", "TEST SM-DP+", 256, "93:fb:33:d0:58:4f:34:9b:07:f8:b5:d2:af:93:d7:c3:e3:54:b3:49:a3:b9:13:50:2e:6a:bc:07:0e:4d:49:29", OID_DP_RID, "DPauth"),
|
||||||
|
("NIST", "TEST SM-DP+", 256, "0a:7c:c1:c2:44:e6:0c:52:cd:5b:78:07:ab:8c:36:0c:26:52:46:01:50:7d:ca:bc:5d:d5:98:b5:a6:16:d5:d5", OID_DP_RID, "DPauth"),
|
||||||
|
("BRP", "TEST SM-DP+2", 512, "0c:17:35:5c:01:1d:0f:e8:d7:da:dd:63:f1:97:85:cf:6c:51:cb:cd:46:6a:e8:8b:e8:f8:1b:c1:05:88:46:f6", OID_DP2_RID, "DP2auth"),
|
||||||
|
("NIST", "TEST SM-DP+2", 512, "9c:32:a0:95:d4:88:42:d9:ff:a4:04:f7:12:51:2a:a2:c5:42:5a:1a:26:38:6a:b6:a1:45:d5:81:1e:03:91:41", OID_DP2_RID, "DP2auth"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for curve_type, cn, serial, key_hex, rid_oid, name_prefix in dpauth_configs:
|
||||||
|
cert, key = gen.generate_dp_cert(
|
||||||
|
curve_type, cn, serial, key_hex,
|
||||||
|
OID_CERTIFICATE_POLICIES_AUTH, rid_oid,
|
||||||
|
datetime(2020, 4, 1, 8, 31, 30),
|
||||||
|
datetime(2030, 3, 30, 8, 31, 30)
|
||||||
|
)
|
||||||
|
suffix = "_ECDSA_BRP" if curve_type == "BRP" else "_ECDSA_NIST"
|
||||||
|
gen.save_cert_and_key(
|
||||||
|
cert, key,
|
||||||
|
f"{output_dir}/DPauth/CERT_S_SM_{name_prefix}{suffix}.der",
|
||||||
|
None,
|
||||||
|
f"{output_dir}/DPauth/SK_S_SM_{name_prefix}{suffix}.pem",
|
||||||
|
f"{output_dir}/DPauth/PK_S_SM_{name_prefix}{suffix}.pem"
|
||||||
|
)
|
||||||
|
print(f"Generated {name_prefix} {curve_type} certificate")
|
||||||
|
|
||||||
|
print("\n=== Generating DPpb Certificates ===")
|
||||||
|
|
||||||
|
dppb_configs = [
|
||||||
|
("BRP", "TEST SM-DP+", 257, "75:ff:32:2f:41:66:16:da:e1:a4:84:ef:71:d4:87:4f:b0:df:32:95:fd:35:c2:cb:a4:89:fb:b2:bb:9c:7b:f6", OID_DP_RID, "DPpb"),
|
||||||
|
("NIST", "TEST SM-DP+", 257, "dc:d6:94:b7:78:95:7e:8e:9a:dd:bd:d9:44:33:e9:ef:8f:73:d1:1e:49:1c:48:d4:25:a3:8a:94:91:bd:3b:ed", OID_DP_RID, "DPpb"),
|
||||||
|
("BRP", "TEST SM-DP+2", 513, "9c:ae:2e:1a:56:07:a9:d5:78:38:2e:ee:93:2e:25:1f:52:30:4f:86:ee:b1:f1:70:8c:db:d3:c0:7b:e2:cd:3d", OID_DP2_RID, "DP2pb"),
|
||||||
|
("NIST", "TEST SM-DP+2", 513, "66:93:11:49:63:9d:ba:ac:1d:c3:d3:06:c5:8b:d2:df:d2:2f:73:bf:63:ac:86:31:98:32:90:b5:7f:90:93:45", OID_DP2_RID, "DP2pb"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for curve_type, cn, serial, key_hex, rid_oid, name_prefix in dppb_configs:
|
||||||
|
cert, key = gen.generate_dp_cert(
|
||||||
|
curve_type, cn, serial, key_hex,
|
||||||
|
OID_CERTIFICATE_POLICIES_PB, rid_oid,
|
||||||
|
datetime(2020, 4, 1, 8, 34, 46),
|
||||||
|
datetime(2030, 3, 30, 8, 34, 46)
|
||||||
|
)
|
||||||
|
suffix = "_ECDSA_BRP" if curve_type == "BRP" else "_ECDSA_NIST"
|
||||||
|
gen.save_cert_and_key(
|
||||||
|
cert, key,
|
||||||
|
f"{output_dir}/DPpb/CERT_S_SM_{name_prefix}{suffix}.der",
|
||||||
|
None,
|
||||||
|
f"{output_dir}/DPpb/SK_S_SM_{name_prefix}{suffix}.pem",
|
||||||
|
f"{output_dir}/DPpb/PK_S_SM_{name_prefix}{suffix}.pem"
|
||||||
|
)
|
||||||
|
print(f"Generated {name_prefix} {curve_type} certificate")
|
||||||
|
|
||||||
|
print("\n=== Generating DPtls Certificates ===")
|
||||||
|
|
||||||
|
dptls_configs = [
|
||||||
|
("BRP", "testsmdpplus1.example.com", "testsmdpplus1.example.com", 9, "3f:67:15:28:02:b3:f4:c7:fa:e6:79:58:55:f6:82:54:1e:45:e3:5e:ff:f4:e8:a0:55:65:a0:f1:91:2a:78:2e", OID_DP_RID, "DP_TLS_BRP"),
|
||||||
|
("NIST", "testsmdpplus1.example.com", "testsmdpplus1.example.com", 9, "a0:3e:7c:e4:55:04:74:be:a4:b7:a8:73:99:ce:5a:8c:9f:66:1b:68:0f:94:01:39:ff:f8:4e:9d:ec:6a:4d:8c", OID_DP_RID, "DP_TLS_NIST"),
|
||||||
|
("NIST", "testsmdpplus2.example.com", "testsmdpplus2.example.com", 12, "4e:65:61:c6:40:88:f6:69:90:7a:db:e3:94:b1:1a:84:24:2e:03:3a:82:a8:84:02:31:63:6d:c9:1b:4e:e3:f5", OID_DP2_RID, "DP2_TLS"),
|
||||||
|
("NIST", "testsmdpplus4.example.com", "testsmdpplus4.example.com", 14, "f2:65:9d:2f:52:8f:4b:11:37:40:d5:8a:0d:2a:f3:eb:2b:48:e1:22:c2:b6:0a:6a:f6:fc:96:ad:86:be:6f:a4", OID_DP4_RID, "DP4_TLS"),
|
||||||
|
("NIST", "testsmdpplus8.example.com", "testsmdpplus8.example.com", 18, "ff:6e:4a:50:9b:ad:db:38:10:88:31:c2:3c:cc:2d:44:30:7a:f2:81:e9:25:96:7f:8c:df:1d:95:54:a0:28:8d", OID_DP8_RID, "DP8_TLS"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for curve_type, cn, dns, serial, key_hex, rid_oid, name_prefix in dptls_configs:
|
||||||
|
cert, key = gen.generate_tls_cert(
|
||||||
|
curve_type, cn, dns, serial, key_hex, rid_oid,
|
||||||
|
datetime(2024, 7, 9, 15, 29, 36),
|
||||||
|
datetime(2025, 8, 11, 15, 29, 36)
|
||||||
|
)
|
||||||
|
gen.save_cert_and_key(
|
||||||
|
cert, key,
|
||||||
|
f"{output_dir}/DPtls/CERT_S_SM_{name_prefix}.der",
|
||||||
|
None,
|
||||||
|
f"{output_dir}/DPtls/SK_S_SM_{name_prefix.replace('_BRP', '_BRP').replace('_NIST', '_NIST')}.pem",
|
||||||
|
f"{output_dir}/DPtls/PK_S_SM_{name_prefix.replace('_BRP', '_BRP').replace('_NIST', '_NIST')}.pem"
|
||||||
|
)
|
||||||
|
print(f"Generated {name_prefix} certificate")
|
||||||
|
|
||||||
|
print("\n=== Generating EUM Certificates ===")
|
||||||
|
|
||||||
|
eum_configs = [
|
||||||
|
("BRP", "12:9b:0a:b1:3f:17:e1:4a:40:b6:fa:4e:d8:23:e0:cf:46:5b:7b:3d:73:24:05:e6:29:5d:3b:23:b0:45:c9:9a"),
|
||||||
|
("NIST", "25:e6:75:77:28:e1:e9:51:13:51:9c:dc:34:55:5c:29:ba:ed:23:77:3a:c5:af:dd:dc:da:d9:84:89:8a:52:f0"),
|
||||||
|
]
|
||||||
|
|
||||||
|
eum_certs = {}
|
||||||
|
eum_keys = {}
|
||||||
|
|
||||||
|
for curve_type, key_hex in eum_configs:
|
||||||
|
cert, key = gen.generate_eum_cert(curve_type, key_hex)
|
||||||
|
eum_certs[curve_type] = cert
|
||||||
|
eum_keys[curve_type] = key
|
||||||
|
suffix = "_ECDSA_BRP" if curve_type == "BRP" else "_ECDSA_NIST"
|
||||||
|
gen.save_cert_and_key(
|
||||||
|
cert, key,
|
||||||
|
f"{output_dir}/EUM/CERT_EUM{suffix}.der",
|
||||||
|
None,
|
||||||
|
f"{output_dir}/EUM/SK_EUM{suffix}.pem",
|
||||||
|
f"{output_dir}/EUM/PK_EUM{suffix}.pem"
|
||||||
|
)
|
||||||
|
print(f"Generated EUM {curve_type} certificate")
|
||||||
|
|
||||||
|
print("\n=== Generating eUICC Certificates ===")
|
||||||
|
|
||||||
|
euicc_configs = [
|
||||||
|
("BRP", "8d:c3:47:a7:6d:b7:bd:d6:22:2d:d7:5e:a1:a1:68:8a:ca:81:1e:4c:bc:6a:7f:6a:ef:a4:b2:64:19:62:0b:90"),
|
||||||
|
("NIST", "11:e1:54:67:dc:19:4f:33:71:83:e4:60:c9:f6:32:60:09:1e:12:e8:10:26:cd:65:61:e1:7c:6d:85:39:cc:9c"),
|
||||||
|
]
|
||||||
|
|
||||||
|
for curve_type, key_hex in euicc_configs:
|
||||||
|
cert, key = gen.generate_euicc_cert(curve_type, eum_certs[curve_type], eum_keys[curve_type], key_hex)
|
||||||
|
suffix = "_ECDSA_BRP" if curve_type == "BRP" else "_ECDSA_NIST"
|
||||||
|
gen.save_cert_and_key(
|
||||||
|
cert, key,
|
||||||
|
f"{output_dir}/eUICC/CERT_EUICC{suffix}.der",
|
||||||
|
None,
|
||||||
|
f"{output_dir}/eUICC/SK_EUICC{suffix}.pem",
|
||||||
|
f"{output_dir}/eUICC/PK_EUICC{suffix}.pem"
|
||||||
|
)
|
||||||
|
print(f"Generated eUICC {curve_type} certificate")
|
||||||
|
|
||||||
|
print("\n=== Certificate generation complete! ===")
|
||||||
|
print(f"All certificates saved to: {output_dir}/")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -308,13 +308,13 @@ class SmDppHttpServer:
|
|||||||
return cert
|
return cert
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __init__(self, server_hostname: str, ci_certs_path: str, use_brainpool: bool = False):
|
def __init__(self, server_hostname: str, ci_certs_path: str, common_cert_path: str, use_brainpool: bool = False):
|
||||||
self.server_hostname = server_hostname
|
self.server_hostname = server_hostname
|
||||||
self.upp_dir = os.path.realpath(os.path.join(DATA_DIR, 'upp'))
|
self.upp_dir = os.path.realpath(os.path.join(DATA_DIR, 'upp'))
|
||||||
self.ci_certs = self.load_certs_from_path(ci_certs_path)
|
self.ci_certs = self.load_certs_from_path(ci_certs_path)
|
||||||
# load DPauth cert + key
|
# load DPauth cert + key
|
||||||
self.dp_auth = CertAndPrivkey(oid.id_rspRole_dp_auth_v2)
|
self.dp_auth = CertAndPrivkey(oid.id_rspRole_dp_auth_v2)
|
||||||
cert_dir = os.path.join(DATA_DIR, 'certs')
|
cert_dir = common_cert_path
|
||||||
if use_brainpool:
|
if use_brainpool:
|
||||||
self.dp_auth.cert_from_der_file(os.path.join(cert_dir, 'DPauth', 'CERT_S_SM_DPauth_ECDSA_BRP.der'))
|
self.dp_auth.cert_from_der_file(os.path.join(cert_dir, 'DPauth', 'CERT_S_SM_DPauth_ECDSA_BRP.der'))
|
||||||
self.dp_auth.privkey_from_pem_file(os.path.join(cert_dir, 'DPauth', 'SK_S_SM_DPauth_ECDSA_BRP.pem'))
|
self.dp_auth.privkey_from_pem_file(os.path.join(cert_dir, 'DPauth', 'SK_S_SM_DPauth_ECDSA_BRP.pem'))
|
||||||
@@ -758,13 +758,43 @@ def main(argv):
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("-H", "--host", help="Host/IP to bind HTTP to", default="localhost")
|
parser.add_argument("-H", "--host", help="Host/IP to bind HTTP to", default="localhost")
|
||||||
parser.add_argument("-p", "--port", help="TCP port to bind HTTP to", default=8000)
|
parser.add_argument("-p", "--port", help="TCP port to bind HTTP to", default=8000)
|
||||||
#parser.add_argument("-v", "--verbose", help="increase output verbosity", action='count', default=0)
|
parser.add_argument("-c", "--certdir", help=f"cert subdir relative to {DATA_DIR}", default="certs")
|
||||||
|
parser.add_argument("-s", "--nossl", help="do NOT use ssl", action='store_true', default=False)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
hs = SmDppHttpServer(HOSTNAME, os.path.join(DATA_DIR, 'certs', 'CertificateIssuer'), use_brainpool=False)
|
common_cert_path = os.path.join(DATA_DIR, args.certdir)
|
||||||
#hs.app.run(endpoint_description="ssl:port=8000:dhParameters=dh_param_2048.pem")
|
hs = SmDppHttpServer(server_hostname=HOSTNAME, ci_certs_path=os.path.join(common_cert_path, 'CertificateIssuer'), common_cert_path=common_cert_path, use_brainpool=False)
|
||||||
hs.app.run(args.host, args.port)
|
if(args.nossl):
|
||||||
|
hs.app.run(args.host, args.port)
|
||||||
|
else:
|
||||||
|
cert_derpath = Path(common_cert_path) / 'DPtls' / 'CERT_S_SM_DP_TLS_NIST.der'
|
||||||
|
cert_pempath = Path(common_cert_path) / 'DPtls' / 'CERT_S_SM_DP_TLS_NIST.pem'
|
||||||
|
cert_skpath = Path(common_cert_path) / 'DPtls' / 'SK_S_SM_DP_TLS_NIST.pem'
|
||||||
|
dhparam_path = Path(common_cert_path) / "dhparam2048.pem"
|
||||||
|
if not dhparam_path.exists():
|
||||||
|
print("Generating dh params, this takes a few seconds..")
|
||||||
|
# Generate DH parameters with 2048-bit key size and generator 2
|
||||||
|
parameters = dh.generate_parameters(generator=2, key_size=2048)
|
||||||
|
pem_data = parameters.parameter_bytes(encoding=Encoding.PEM,format=ParameterFormat.PKCS3)
|
||||||
|
with open(dhparam_path, 'wb') as file:
|
||||||
|
file.write(pem_data)
|
||||||
|
print("DH params created successfully")
|
||||||
|
|
||||||
|
if not cert_pempath.exists():
|
||||||
|
print("Translating tls server cert from DER to PEM..")
|
||||||
|
with open(cert_derpath, 'rb') as der_file:
|
||||||
|
der_cert_data = der_file.read()
|
||||||
|
|
||||||
|
cert = x509.load_der_x509_certificate(der_cert_data)
|
||||||
|
pem_cert = cert.public_bytes(Encoding.PEM) #.decode('utf-8')
|
||||||
|
|
||||||
|
with open(cert_pempath, 'wb') as pem_file:
|
||||||
|
pem_file.write(pem_cert)
|
||||||
|
|
||||||
|
SERVER_STRING = f'ssl:{args.port}:privateKey={cert_skpath}:certKey={cert_pempath}:dhParameters={dhparam_path}'
|
||||||
|
print(SERVER_STRING)
|
||||||
|
|
||||||
|
hs.app.run(host=HOSTNAME, port=args.port, endpoint_description=SERVER_STRING)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(sys.argv)
|
main(sys.argv)
|
||||||
|
|||||||
16
smdpp-data/certs/DPtls/CERT_S_SM_DP_TLS_BRP.pem
Normal file
16
smdpp-data/certs/DPtls/CERT_S_SM_DP_TLS_BRP.pem
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICgjCCAimgAwIBAgIBCTAKBggqhkjOPQQDAjBEMRAwDgYDVQQDDAdUZXN0IENJ
|
||||||
|
MREwDwYDVQQLDAhURVNUQ0VSVDEQMA4GA1UECgwHUlNQVEVTVDELMAkGA1UEBhMC
|
||||||
|
SVQwHhcNMjQwNzA5MTUyOTM2WhcNMjUwODExMTUyOTM2WjAzMQ0wCwYDVQQKDARB
|
||||||
|
Q01FMSIwIAYDVQQDDBl0ZXN0c21kcHBsdXMxLmV4YW1wbGUuY29tMFowFAYHKoZI
|
||||||
|
zj0CAQYJKyQDAwIIAQEHA0IABEwizNgsjQIh+dhUO3LhB7zJ/ZBU1mx1wOt0p73n
|
||||||
|
MOdhjvZbJwteguQ6eW+N7guvivvrilNiU3oC/WXHnkEZa7WjggEaMIIBFjAOBgNV
|
||||||
|
HQ8BAf8EBAMCB4AwIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBQG
|
||||||
|
A1UdIAQNMAswCQYHZ4ESAQIBAzAdBgNVHQ4EFgQUPTMJg/OfzFvS5K1ophmnR0iu
|
||||||
|
i50wHwYDVR0jBBgwFoAUwLxwujaSnUO0Z/9XVwUw5Xq4/NgwKQYDVR0RBCIwIIIZ
|
||||||
|
dGVzdHNtZHBwbHVzMS5leGFtcGxlLmNvbYgDiDcKMGEGA1UdHwRaMFgwKqAooCaG
|
||||||
|
JGh0dHA6Ly9jaS50ZXN0LmV4YW1wbGUuY29tL0NSTC1BLmNybDAqoCigJoYkaHR0
|
||||||
|
cDovL2NpLnRlc3QuZXhhbXBsZS5jb20vQ1JMLUIuY3JsMAoGCCqGSM49BAMCA0cA
|
||||||
|
MEQCIHHmXEy9mgudh/VbK0hJwmX7eOgbvHLnlujrpQzvUd4uAiBFVJgSdzYvrmJ9
|
||||||
|
5yeIvmjHwxSMBgQp2dde7OtdVEK8Kw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
1
smdpp-data/sm-dp-sessions-BRP
Symbolic link
1
smdpp-data/sm-dp-sessions-BRP
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/tmp/sm-dp-sessions-BRP
|
||||||
1
smdpp-data/sm-dp-sessions-NIST
Symbolic link
1
smdpp-data/sm-dp-sessions-NIST
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/tmp/sm-dp-sessions-NIST
|
||||||
Reference in New Issue
Block a user