|
#!/usr/bin/env python3
# (c) 2018 jcea@jcea.es - https://www.jcea.es/
# Código liberado como Dominio Público.
# Haz con él lo que quieras.
import os
import argparse
import datetime
# Ver https://cryptography.readthedocs.org/en/latest/x509/tutorial/
# Ver https://cryptography.io/en/latest/x509/reference/#cryptography.x509.CertificateSigningRequestBuilder
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.x509.oid import NameOID
parser = argparse.ArgumentParser(
description='Genera un certificado con SubjectAlternativeName.')
parser.add_argument('--days', default=365, metavar='DÍAS', type=int,
help='Expiración en días (365 por defecto)')
parser.add_argument('--CA', default='webdav2.bt.jcea.es',
help='CA que se usará para firmar')
parser.add_argument('dominio', nargs='+', help='Dominio')
args = parser.parse_args()
dominios = args.dominio # Esto es UNICODE
key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
with open(args.CA + '.cert', 'rb') as f:
CA_certificate = f.read()
CA_certificate = x509.load_pem_x509_certificate(CA_certificate,
backend=default_backend())
try:
is_CA = CA_certificate.extensions.get_extension_for_class(
x509.BasicConstraints).value.ca
except x509.ExtensionNotFound:
is_CA = False
if not is_CA:
raise RuntimeError(f'El certificado "{args.CA}" '
'no parece ser una entidad de certificación')
with open(args.CA + '.key', 'rb') as f:
CA_key = f.read()
CA_key = serialization.load_pem_private_key(CA_key, password=None,
backend=default_backend())
subject = [i for i in CA_certificate.subject if i.oid != NameOID.COMMON_NAME]
subject.append(x509.NameAttribute(NameOID.COMMON_NAME, dominios[0]))
subject = x509.Name(subject)
SubjectAlternativeName = [x509.DNSName(dominio) for dominio in dominios]
SubjectAlternativeName = x509.SubjectAlternativeName(SubjectAlternativeName)
builder = x509.CertificateBuilder()
builder = builder.subject_name(subject)
builder = builder.issuer_name(CA_certificate.subject)
builder = builder.public_key(key.public_key())
builder = builder.serial_number(x509.random_serial_number())
builder = builder.not_valid_before(datetime.datetime.utcnow())
builder = (builder.not_valid_after(datetime.datetime.utcnow() +
datetime.timedelta(days=args.days)))
builder = builder.add_extension(SubjectAlternativeName, critical=False)
builder = builder.add_extension(x509.BasicConstraints(ca=False,
path_length=None),
critical=True)
cert = builder.sign(CA_key, hashes.SHA256(), backend=default_backend())
with open(dominios[0] + '.cert', 'wb') as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
with open(dominios[0] + '.key', 'wb',
opener=lambda x, y: os.open(x, y, mode=0o400)) as f:
f.write(key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption())
)
|