237 lines
7.8 KiB
Python
237 lines
7.8 KiB
Python
"""
|
|
Generate self-signed certificates for mTLS integration testing.
|
|
Creates a Certificate Authority (CA), server certificates, and client certificates.
|
|
"""
|
|
|
|
import datetime
|
|
import ipaddress
|
|
from pathlib import Path
|
|
|
|
from cryptography import x509
|
|
from cryptography.hazmat.primitives import hashes, serialization
|
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
from cryptography.x509.oid import NameOID
|
|
|
|
|
|
def generate_private_key():
|
|
"""Generate an RSA private key."""
|
|
return rsa.generate_private_key(
|
|
public_exponent=65537,
|
|
key_size=2048,
|
|
)
|
|
|
|
|
|
def generate_ca_certificate():
|
|
"""Generate a self-signed CA certificate."""
|
|
private_key = generate_private_key()
|
|
|
|
subject = issuer = x509.Name(
|
|
[
|
|
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
|
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.LOCALITY_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "SGLang Test"),
|
|
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.COMMON_NAME, "Test CA"),
|
|
]
|
|
)
|
|
|
|
cert = (
|
|
x509.CertificateBuilder()
|
|
.subject_name(subject)
|
|
.issuer_name(issuer)
|
|
.public_key(private_key.public_key())
|
|
.serial_number(x509.random_serial_number())
|
|
.not_valid_before(datetime.datetime.utcnow())
|
|
.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=3650))
|
|
.add_extension(
|
|
x509.BasicConstraints(ca=True, path_length=None),
|
|
critical=True,
|
|
)
|
|
.add_extension(
|
|
x509.KeyUsage(
|
|
digital_signature=True,
|
|
key_cert_sign=True,
|
|
crl_sign=True,
|
|
key_encipherment=False,
|
|
content_commitment=False,
|
|
data_encipherment=False,
|
|
key_agreement=False,
|
|
encipher_only=False,
|
|
decipher_only=False,
|
|
),
|
|
critical=True,
|
|
)
|
|
.sign(private_key, hashes.SHA256())
|
|
)
|
|
|
|
return private_key, cert
|
|
|
|
|
|
def generate_server_certificate(ca_key, ca_cert):
|
|
"""Generate a server certificate signed by the CA."""
|
|
private_key = generate_private_key()
|
|
|
|
subject = x509.Name(
|
|
[
|
|
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
|
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.LOCALITY_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "SGLang Test"),
|
|
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.COMMON_NAME, "localhost"),
|
|
]
|
|
)
|
|
|
|
cert = (
|
|
x509.CertificateBuilder()
|
|
.subject_name(subject)
|
|
.issuer_name(ca_cert.subject)
|
|
.public_key(private_key.public_key())
|
|
.serial_number(x509.random_serial_number())
|
|
.not_valid_before(datetime.datetime.utcnow())
|
|
.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
|
|
.add_extension(
|
|
x509.SubjectAlternativeName(
|
|
[
|
|
x509.DNSName("localhost"),
|
|
x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")),
|
|
]
|
|
),
|
|
critical=False,
|
|
)
|
|
.add_extension(
|
|
x509.KeyUsage(
|
|
digital_signature=True,
|
|
key_encipherment=True,
|
|
key_cert_sign=False,
|
|
crl_sign=False,
|
|
content_commitment=False,
|
|
data_encipherment=False,
|
|
key_agreement=False,
|
|
encipher_only=False,
|
|
decipher_only=False,
|
|
),
|
|
critical=True,
|
|
)
|
|
.add_extension(
|
|
x509.ExtendedKeyUsage(
|
|
[
|
|
x509.oid.ExtendedKeyUsageOID.SERVER_AUTH,
|
|
]
|
|
),
|
|
critical=False,
|
|
)
|
|
.sign(ca_key, hashes.SHA256())
|
|
)
|
|
|
|
return private_key, cert
|
|
|
|
|
|
def generate_client_certificate(ca_key, ca_cert):
|
|
"""Generate a client certificate signed by the CA."""
|
|
private_key = generate_private_key()
|
|
|
|
subject = x509.Name(
|
|
[
|
|
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
|
|
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.LOCALITY_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "SGLang Test"),
|
|
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Test"),
|
|
x509.NameAttribute(NameOID.COMMON_NAME, "test-client"),
|
|
]
|
|
)
|
|
|
|
cert = (
|
|
x509.CertificateBuilder()
|
|
.subject_name(subject)
|
|
.issuer_name(ca_cert.subject)
|
|
.public_key(private_key.public_key())
|
|
.serial_number(x509.random_serial_number())
|
|
.not_valid_before(datetime.datetime.utcnow())
|
|
.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=365))
|
|
.add_extension(
|
|
x509.KeyUsage(
|
|
digital_signature=True,
|
|
key_encipherment=True,
|
|
key_cert_sign=False,
|
|
crl_sign=False,
|
|
content_commitment=False,
|
|
data_encipherment=False,
|
|
key_agreement=False,
|
|
encipher_only=False,
|
|
decipher_only=False,
|
|
),
|
|
critical=True,
|
|
)
|
|
.add_extension(
|
|
x509.ExtendedKeyUsage(
|
|
[
|
|
x509.oid.ExtendedKeyUsageOID.CLIENT_AUTH,
|
|
]
|
|
),
|
|
critical=False,
|
|
)
|
|
.sign(ca_key, hashes.SHA256())
|
|
)
|
|
|
|
return private_key, cert
|
|
|
|
|
|
def save_key(key, path: Path):
|
|
"""Save private key to PEM file."""
|
|
with open(path, "wb") as f:
|
|
f.write(
|
|
key.private_bytes(
|
|
encoding=serialization.Encoding.PEM,
|
|
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
|
encryption_algorithm=serialization.NoEncryption(),
|
|
)
|
|
)
|
|
|
|
|
|
def save_cert(cert, path: Path):
|
|
"""Save certificate to PEM file."""
|
|
with open(path, "wb") as f:
|
|
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
|
|
|
|
|
def generate_all_certificates(output_dir: Path):
|
|
"""Generate all certificates and keys for mTLS testing."""
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
print("==> Generating CA certificate...")
|
|
ca_key, ca_cert = generate_ca_certificate()
|
|
save_key(ca_key, output_dir / "ca-key.pem")
|
|
save_cert(ca_cert, output_dir / "ca-cert.pem")
|
|
|
|
print("==> Generating server certificate...")
|
|
server_key, server_cert = generate_server_certificate(ca_key, ca_cert)
|
|
save_key(server_key, output_dir / "server-key.pem")
|
|
save_cert(server_cert, output_dir / "server-cert.pem")
|
|
|
|
print("==> Generating client certificate...")
|
|
client_key, client_cert = generate_client_certificate(ca_key, ca_cert)
|
|
save_key(client_key, output_dir / "client-key.pem")
|
|
save_cert(client_cert, output_dir / "client-cert.pem")
|
|
|
|
print(f"==> Certificates generated successfully in {output_dir}")
|
|
print()
|
|
print("Files created:")
|
|
print(" - ca-cert.pem : CA certificate (for verifying server/client certs)")
|
|
print(" - ca-key.pem : CA private key")
|
|
print(" - server-cert.pem : Server certificate")
|
|
print(" - server-key.pem : Server private key")
|
|
print(" - client-cert.pem : Client certificate")
|
|
print(" - client-key.pem : Client private key")
|
|
print()
|
|
print("Test server can use: server-cert.pem + server-key.pem")
|
|
print("Test router can use: client-cert.pem + client-key.pem + ca-cert.pem")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
script_dir = Path(__file__).parent
|
|
certs_dir = script_dir / "test_certs"
|
|
generate_all_certificates(certs_dir)
|