Create a simple internal CA for your microservices architecture or integration tests.

The Transport Layer Security (TLS) model (sometimes called SSL by its old name) is based on the concept of certificate Authority Certificate Authoritie (CA). These institutions are trusted by the browser and operating system to sign the server’s certificate for verification of ownership.

However, for internal networks, microservice architectures, or integration testing, sometimes a local CA is more useful: a CA that is only trusted internally, and then signs the local server’s certificate.

This is especially useful for integration testing. Getting the certificate can be a burden because it takes a few minutes for the server. But using “ignore certificates” in code can be introduced into production and lead to security disasters.

CA certificates are not much different from regular server certificates. The important thing is that it is trusted by native code. For example, in the Python Requests library, you can do this by setting the REQUESTS_CA_BUNDLE variable to the directory containing the certificate.

In the example of creating certificates for integration tests, long-term certificates are not required: if your integration tests take more than a day, you should have failed the tests.

Therefore, calculate yesterday and tomorrow as valid intervals:

>>> import datetime
>>> one_day = datetime.timedelta(days=1)
>>> today = datetime.date.today()
>>> yesterday = today - one_day
>>> tomorrow = today - one_day
Copy the code

You are now ready to create a simple CA certificate. You need to generate the private key, create the public key, set the CA’s “parameters”, and then self-sign the certificate: THE CA certificate is always self-signed. Finally, export the certificate file and private key file.

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes, serialization
from cryptography import x509
from cryptography.x509.oid import NameOID


private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
public_key = private_key.public_key()
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([
    x509.NameAttribute(NameOID.COMMON_NAME, 'Simple Test CA'),
]))
builder = builder.issuer_name(x509.Name([
    x509.NameAttribute(NameOID.COMMON_NAME, 'Simple Test CA'),
]))
builder = builder.not_valid_before(yesterday)
builder = builder.not_valid_after(tomorrow)
builder = builder.serial_number(x509.random_serial_number())
builder = builder.public_key(public_key)
builder = builder.add_extension(
    x509.BasicConstraints(ca=True, path_length=None),
    critical=True)
certificate = builder.sign(
    private_key=private_key, algorithm=hashes.SHA256(),
    backend=default_backend()
)
private_bytes = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncrption())
public_bytes = certificate.public_bytes(
    encoding=serialization.Encoding.PEM)
with open("ca.pem"."wb") as fout:
    fout.write(private_bytes + public_bytes)
with open("ca.crt"."wb") as fout:
    fout.write(public_bytes)
Copy the code

Typically, a real CA will require a Certificate Signing request (CSR) to sign the certificate. But when you are your own CA, you can make your own rules! You can just sign what you want.

Continuing with the integration test example, you can create a private key and immediately sign the corresponding public key. Notice COMMON_NAME must be server name in the HTTPS URL. If you have configured name lookup, you need the server to be able to respond to requests to service.test.local.

service_private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
service_public_key = service_private_key.public_key()
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([
   x509.NameAttribute(NameOID.COMMON_NAME, 'service.test.local')
]))
builder = builder.not_valid_before(yesterday)
builder = builder.not_valid_after(tomorrow)
builder = builder.public_key(public_key)
certificate = builder.sign(
    private_key=private_key, algorithm=hashes.SHA256(),
    backend=default_backend()
)
private_bytes = service_private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncrption())
public_bytes = certificate.public_bytes(
    encoding=serialization.Encoding.PEM)
with open("service.pem"."wb") as fout:
    fout.write(private_bytes + public_bytes)
Copy the code

The service.pem file now has a private key and a “valid” certificate: it has been signed by the local CA. This file is in a format that can be used by Nginx, HAProxy, or most other HTTPS servers.

By using this logic in the test script, you can easily create a reality-looking HTTPS server as long as the client is configured to trust the CA.


Via: opensource.com/article/19/…

By Moshe Zadka, Lujun9972

This article is originally compiled by LCTT and released in Linux China