1768910460 english chat2026-devblog

Chat2026 part 2: Obtaining the keys and certificates

So I'm writing this as I'm coding. It might not be a well structured lecture, I simply write down everything as it comes to my mind.

I'm tracking progress in the https://git.jxzqj.com/chat2026.git repository, which you can clone from command line using simply:

    git clone https://git.jxzqj.com/chat2026.git

Then you can git checkout <COMMIT_HASH> the specific commits I mention in this article. As I post later updates you can git pull origin main the repo to get the latest commits I have added. So you can follow the progress and diffs on this project.

So after messing around with the repo a bit I started by looking at the rcgen crate. It seems it can create the certificates I need.

So the first attempt was to create and write a CA cert like this:

use rcgen::{CertificateParams, KeyPair};

pub type BoxedError = Box<dyn std::error::Error + Send + Sync>;
pub type GenResult<T> = Result<T, BoxedError>;

fn main() -> GenResult<()> {
    let key = KeyPair::generate()?;

    let mut cp = CertificateParams::new(["foo".to_string(), "bar".to_string()])?;
    cp.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);

    let ca_cert = cp.self_signed(&key)?;
    let ca_cert_pem = ca_cert.pem();

    std::fs::write("ca-cert.crt", ca_cert_pem.as_bytes())?;

    println!("Ok done!");
    Ok(())
}

This creates a CA cert, which looks OK, when I dump it using the openssl command:

$ openssl x509 -noout -text -in ca-cert.crt 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            69:98:f4:22:15:95:87:af:89:48:f1:64:95:24:ed:92:66:57:5e:81
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN=rcgen self signed cert
        Validity
            Not Before: Jan  1 00:00:00 1975 GMT
            Not After : Jan  1 00:00:00 4096 GMT
        Subject: CN=rcgen self signed cert
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:99:ec:03:e0:f4:c3:9f:ef:52:4d:bc:f1:10:b2:
                    e0:95:92:a6:4a:61:09:e7:f0:ed:f0:48:b7:8a:04:
                    61:19:04:de:c4:24:21:3b:87:d4:64:5d:0b:c8:31:
                    22:6b:0d:7f:6b:51:16:81:a1:4e:38:ab:5d:fc:15:
                    1a:30:30:a3:6f
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:foo, DNS:bar
            X509v3 Subject Key Identifier: 
                45:6B:83:27:6B:B4:BB:A8:FD:6C:DD:3E:AC:C5:51:59:5F:43:FB:9B
            X509v3 Basic Constraints: critical
                CA:TRUE
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        30:45:02:20:18:e8:af:95:32:18:b4:c6:99:43:32:a7:24:45:
        5c:b2:e0:4c:97:06:cb:b4:80:eb:d5:ad:fb:bb:51:be:ab:10:
        02:21:00:b7:aa:44:a3:ab:0e:44:4d:e4:f9:29:4a:f2:eb:46:
        08:e4:2f:ed:54:cc:a1:96:e1:d7:97:98:65:8a:00:7d:3c

But I don't like the default "rcgen self signed cert" name, so it needs to be set to something more meaningful. So added the following line to make it related to this project:

cp.distinguished_name.push(rcgen::DnType::CommonName, "Chat2026 root cert.");

Since this is a CA cert, we probably don't even need to specify the domains yet, so I changed that array to empty too.

    let mut cp = CertificateParams::new([])?;

So the next step is making the actual device certificate that was signed by this root. So added the following lines to the code:

    // Create device certificate.

    let issuer = Issuer::from_params(&cp, key);

    let cert_key = KeyPair::generate()?;
    let mut device_cert_params = CertificateParams::new(["username".to_string()])?;
    device_cert_params.distinguished_name.push(DnType::CommonName, "Chat2026 device cert.");

    let dev_cert = device_cert_params.signed_by(&cert_key, &issuer)?;
    let dev_cert_pem = dev_cert.pem();
    std::fs::write("dev-cert.crt", dev_cert_pem.as_bytes())?;

This creates a certificate that might look like this when dumped:

$ openssl x509 -noout -text -in dev-cert.crt 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            73:a8:75:92:d5:31:72:da:a3:35:70:97:fe:63:44:a1:03:4f:fe:80
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN=Chat2026 root cert.
        Validity
            Not Before: Jan  1 00:00:00 1975 GMT
            Not After : Jan  1 00:00:00 4096 GMT
        Subject: CN=Chat2026 device cert.
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:9b:fb:fb:71:03:4c:88:2d:95:2f:e7:b7:3b:a9:
                    3a:1b:aa:a1:9c:46:dc:4d:ec:b2:d0:b6:8a:28:c1:
                    82:80:ae:26:aa:33:3f:a0:5a:be:99:6a:79:c8:57:
                    dc:9c:08:7c:4d:ff:76:74:17:96:03:33:68:4d:11:
                    d7:02:90:b2:0a
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:username
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        30:46:02:21:00:88:96:fd:6b:2a:45:f6:80:a7:48:2b:58:11:
        ee:a4:7b:0e:51:00:1a:0d:3d:e1:45:74:e1:d2:c4:3d:6f:e3:
        56:02:21:00:ad:f8:46:ee:9a:cb:23:4d:e4:85:6f:59:1f:71:
        8b:cd:97:fb:50:dd:e1:ea:9a:1d:a2:5c:31:77:f5:31:21:f1

The current commit hash we are at right now is: fb58042faa5930d29345548a7b90d70092eb4ce5

So we could create another certificate that's signed by the root, that's good. So my general idea is that each user client will create a CA cert that basically identifies the user, this key will sign the device keys that will be used for encryption. Each device an user has will have a different encryption key and the corresponding certificate that's all signed by the same CA key. For this reason it may make sense to use the user name as the common name in the CA key. While use some kind of device key as common name for the actual encryption keys. So I did that and we are now at commit 8c0fc5ee11c7aaa6f8447fe58d14dbe6efe6642a.

Since the same device may be used in both server and client role, depending on which direction the connection happens. I may need to think about if I need 2 separate keys and certificates, for encryption and client certificate purposes. This is something I need to read more about. I may also need to set the "key usage purpose" and "extended key usage purpose" fields of the certificates to make it work correctly. I think I can do that now. For some pointers I've looked at the certificate used in my browser. It seems CRL signing and key signing needs to be on the list for a CA cert. So for the CA key I added the purposes into the code:

    cp.key_usages.push(KeyUsagePurpose::KeyCertSign);
    cp.key_usages.push(KeyUsagePurpose::CrlSign);

For the device certificates I'll need to set extended key usage which specifies that the cert is good for both server and client authentication. So I added the lines:

    device_cert_params.extended_key_usages.push(ExtendedKeyUsagePurpose::ClientAuth);
    device_cert_params.extended_key_usages.push(ExtendedKeyUsagePurpose::ServerAuth);

So now we are at the commit eb2146700f2989214d40eea3c36684142b16ed02.

So in the next part I will explore this keys and certificates further. It might be possible that I can use the same key and cert for both client and server side just fine.

Feedback

Posts

See the latest posts below, click the "..." to see them all. Click the tags to filter by tag. You can also subscribe to RSS in those lists.

Double entry bookkeeping explained - 1771536720 english finance

Chat2026 part 10: continuing application design - 1770850620 english chat2026-devblog

If you want privacy, please use a desktop PC - 1770575340 english privacy

YouTube is now practically unsearchable - 1770565140 english rants

Chat2026 part 9: using CRL in the server and client examples - 1770031740 english chat2026-devblog

...