I'm trying to programmatically create client certificates for mosquitto clients to use. Using XCA, I've been able to create a self signed CA, an intermediate, and then a certificate from the intermediate. I've configured mosquitto with the `require_certificate true' option. Using keys exported from XCA, I'm able to use the paho clients for testing:
mosquitto_sub -h ubuntu -p 8765 -t /1/2/3 --cafile ~/CA2/Chain.crt -d --cert ./test1.crt --key ./test1.pem
This works.
Now I'm trying to automate the key generation with a python3 script using the cryptography
module.
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.x509.oid import NameOID
from pathlib import Path
from datetime import datetime, timezone, timedelta
import uuid
privateKey = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
backend=default_backend())
with Path('test2.pem').open('wb') as stream:
stream.write(privateKey.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption()))
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, uuid.uuid4().hex),
]))
builder = builder.issuer_name(x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, u'ubuntu'),
]))
builder = builder.not_valid_before(datetime.now(timezone.utc) - timedelta(days=1))
builder = builder.not_valid_after(datetime.now(timezone.utc) + timedelta(days=365*50))
builder = builder.serial_number(x509.random_serial_number())
builder = builder.public_key(privateKey.public_key())
builder = builder.add_extension(
x509.BasicConstraints(ca=False, path_length=None), critical=True,
)
builder = builder.add_extension(
x509.SubjectKeyIdentifier.from_public_key(privateKey.public_key()), critical=False,
)
builder = builder.add_extension(
x509.KeyUsage(digital_signature=True,
content_commitment=False,
key_encipherment=True,
data_encipherment=True,
key_agreement=False,
key_cert_sign=False,
crl_sign=False,
encipher_only=False,
decipher_only=False), critical=False,
)
certificate = builder.sign(
private_key=privateKey, algorithm=hashes.SHA256(),
backend=default_backend()
)
print(certificate)
with Path('test2.crt').open('wb') as stream:
stream.write(certificate.public_bytes(encoding=serialization.Encoding.PEM))
I've used openssl x509 -in certFile.crt -text -noout
to compare the XCA variant vs my automated one. They look darn near identical to me. However, the sub doesn't work:
$ mosquitto_sub -h ubuntu -p 8765 -t /1/2/3 --cafile ~/CA2/Chain.crt -d --cert ./cert.crt --key ./key.pem
Client mosqsub|34613-ubuntu sending CONNECT
Error: A TLS error occurred.
The problem as pointed out in another source, was that I was signing the new certificate with the same key as I was generating the certificate from. Like the issue name is to be the same name as the issuing certificate, the signing key should be the private key of that same issuing certificate. Replacing the signing clause with something like:
parentKey = serialization.load_pem_private_key(
Path('path/to/intermediate_key.pem').read_bytes(),
password=None,
backend=default_backend())
certificate = builder.sign(
private_key=parentKey,
algorithm=hashes.SHA256(),
backend=default_backend()
)