X.509 and PKCS#7 Support

X.509 Certificate Generator

The leancrypto library offers a X.509 certificate generator along with the X.509 parser. This generator allows to generate PQC certificates using all PQC algorithms offered by the library.

The generator is accessed by one of the following means:

  • Use of the X.509 API offered with lc_x509_generator.h.

  • Use of the X.509 generator application lc_x509_generator implemented in the apps/src directory.

The lc_509_generator application is to be considered a frontend to the API which implies that the API implements all important operations with respect to the generation.

The following sections outline lc_509_generator commands to obtain certificates.

The X.509 generator and parser for PQC certificates are tested with the artifacts from all other cryptographic providers collected at the IETF-Hackathon. In particular, the following algorithms were tested with the other implementations:

  • ML-DSA 44, ML-DSA 65, ML-DSA 87

  • SLH-DSA SHAKE256: 128s, 128f, 192s, 192f, 256s, 256f

  • Composite ML-DSA: ML-DSA44-ED25519, ML-DSA65-ED25519

Leancrypto passes the interoperability tests of the mentioned cipher algorithms.

Generate CA Certificate

The following command generates a new ML-DSA 87 key pair, generates a self-signed CA certificate with all proper key usage flags, and stores the following files:

  • ml-dsa87_cacert.der: X.509 Root-CA certificate

  • ml-dsa87_cacert.privkey: Raw ML-DSA 87 secret key

lc_x509_generator
  --keyusage digitalSignature
  --keyusage keyEncipherment
  --keyusage keyCertSign
  --keyusage critical
  --ca 
  --valid-from 1729527728
  --valid-to 2044210606
  --subject-cn "leancrypto test CA"
  --subject-ou "leancrypto test OU"
  --subject-o leancrypto
  --subject-st Saxony
  --subject-c DE
  --issuer-cn "leancrypto test CA"
  --issuer-ou "leancrypto test OU"
  --issuer-o leancrypto
  --issuer-st Saxony
  --issuer-c DE
  --serial 0102030405060708
  -o ml-dsa87_cacert.der
  --sk-file ml-dsa87_cacert.privkey
  --create-keypair ML-DSA87

The generated result looks like:

Serial Number: 01:02:03:04:05:06:07:08
Signature Algorithm: ML-DSA 87 <builtin>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test CA
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test CA
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA87
X509v3 Subject Key Identifier: 15:37:53:0b:15:c8:1c:27:31:f0:62:98:9f:c5:f7:d7:a9:bb:dc:36:14:78:38:fd:cb:30:80:01:43:2b:04:26
X509v3 Authority Key Identifier: 15:37:53:0b:15:c8:1c:27:31:f0:62:98:9f:c5:f7:d7:a9:bb:dc:36:14:78:38:fd:cb:30:80:01:43:2b:04:26
X509v3 Basic Constraints: CA (critical)
X509v3 Key Usage: (critical) digitalSignature keyEncipherment keyCertSign 
AuthID[0] = 
AuthID[1] = 1537530b15c81c2731f062989fc5f7d7a9bbdc36147838fdcb308001432b0426
AuthID[2] = 31633019060355040304126c65616e63727970746f20746573742043413011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 010203040506070831633019060355040304126c65616e63727970746f20746573742043413011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 2592
Self-signed: yes

Note: If no --skid or no --akid are used, then they are generated by hasing the public key using the default message digest algorithm of SHA3-256.

Generate Intermediate 1 Certificate

The following command generates a new ML-DSA 65 key pair, generates an intermediate certificate signed by the root certificate generated in section [Generate CA Certificate], and stores the following files:

  • ml-dsa65_int1.der: X.509 intermediate CA certificate

  • ml-dsa65_int1.privkey: Raw ML-DSA 65 secret key

lc_x509_generator
  --keyusage digitalSignature
  --keyusage keyEncipherment
  --keyusage keyCertSign
  --keyusage critical
  --ca
  --valid-from 1729527728
  --valid-to 2044210606
  --subject-cn "leancrypto test int1"
  --subject-ou "leancrypto test OU"
  --subject-o leancrypto
  --subject-st Saxony
  --subject-c DE
  --serial 0203030405060708
  -o ml-dsa65_int1.der
  --sk-file ml-dsa65_int1.privkey
  --create-keypair ML-DSA65
  --x509-signer ml-dsa87_cacert.der
  --signer-sk-file ml-dsa87_cacert.privkey

The generated result looks like:

Serial Number: 02:03:03:04:05:06:07:08
Signature Algorithm: ML-DSA 87 <builtin>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test CA
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int1
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA65
X509v3 Subject Key Identifier: eb:0f:c7:e1:2c:61:d5:96:04:c8:3a:46:c0:16:a1:18:5b:24:9b:6a:ff:87:0e:ce:4b:a8:a4:96:53:7c:62:f3
X509v3 Authority Key Identifier: 15:37:53:0b:15:c8:1c:27:31:f0:62:98:9f:c5:f7:d7:a9:bb:dc:36:14:78:38:fd:cb:30:80:01:43:2b:04:26
X509v3 Basic Constraints: CA (critical)
X509v3 Key Usage: (critical) digitalSignature keyEncipherment keyCertSign 
AuthID[0] = 
AuthID[1] = 1537530b15c81c2731f062989fc5f7d7a9bbdc36147838fdcb308001432b0426
AuthID[2] = 31633019060355040304126c65616e63727970746f20746573742043413011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 020303040506070831633019060355040304126c65616e63727970746f20746573742043413011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 1952
Self-signed: no

Generate Intermediate 2 Certificate

The following command generates a new ML-DSA 44 key pair, generates an intermediate certificate signed by the intermediate 1 certificate generated in section [Generate Intermediate 1 Certificate], and stores the following files:

  • ml-dsa44_int2.der: X.509 intermediate CA certificate

  • ml-dsa44_int2.privkey: Raw ML-DSA 44 secret key

lc_x509_generator 
  --keyusage digitalSignature
  --keyusage keyEncipherment
  --keyusage keyCertSign
  --keyusage critical
  --ca
  --valid-from 1729527728
  --valid-to 2044210606
  --subject-cn "leancrypto test int2"
  --subject-ou "leancrypto test OU"
  --subject-o leancrypto
  --subject-st Saxony
  --subject-c DE
  --serial 0303040506070809
  -o ml-dsa44_int2.der
  --sk-file ml-dsa44_int2.privkey
  --create-keypair ML-DSA44
  --x509-signer ml-dsa65_int1.der
  --signer-sk-file ml-dsa65_int1.privkey

The generated result looks like:

Serial Number: 03:03:04:05:06:07:08:09
Signature Algorithm: ML-DSA 65 <builtin>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int1
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int2
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA44
X509v3 Subject Key Identifier: 3d:f5:05:f8:c2:fe:1a:95:e4:96:d5:40:3c:39:82:ff:4b:c7:41:ce:30:78:74:f8:22:8f:34:32:5c:17:3e:1d
X509v3 Authority Key Identifier: eb:0f:c7:e1:2c:61:d5:96:04:c8:3a:46:c0:16:a1:18:5b:24:9b:6a:ff:87:0e:ce:4b:a8:a4:96:53:7c:62:f3
X509v3 Basic Constraints: CA (critical)
X509v3 Key Usage: (critical) digitalSignature keyEncipherment keyCertSign 
AuthID[0] = 
AuthID[1] = eb0fc7e12c61d59604c83a46c016a1185b249b6aff870ece4ba8a496537c62f3
AuthID[2] = 3165301b060355040304146c65616e63727970746f207465737420696e74313011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 03030405060708093165301b060355040304146c65616e63727970746f207465737420696e74313011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 1312
Self-signed: no

Generate Leaf Certificate

The following command generates a new ML-DSA 87 key pair, generates a leaf signed by the intermediate 2 certificate generated in section [Generate Intermediate 2 Certificate], and stores the following files:

  • ml-dsa87_leaf.der: X.509 leaf certificate with usual key usage and EKU flags

  • ml-dsa87_leaf.privkey: Raw ML-DSA 87 secret key

lc_x509_generator
  --keyusage dataEncipherment
  --keyusage critical
  --eku critical
  --eku serverAuth
  --eku codeSigning
  --valid-from 1729527728
  --valid-to 2044210606
  --subject-cn "leancrypto test leaf"
  --subject-ou "leancrypto test OU"
  --subject-o leancrypto
  --subject-st Saxony
  --subject-c DE
  --serial 0405060708090001
  -o ml-dsa87_leaf.der
  --sk-file ml-dsa87_leaf.privkey
  --create-keypair ML-DSA87
  --x509-signer ml-dsa44_int2.der
  --signer-sk-file ml-dsa44_int2.privkey

The generated result looks like:

Serial Number: 04:05:06:07:08:09:00:01
Signature Algorithm: ML-DSA 44 <builtin>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int2
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test leaf
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA87
X509v3 Subject Key Identifier: c7:e3:6f:df:03:93:c2:25:06:2b:ee:85:02:73:c5:31:1f:6f:86:c8:31:05:93:f3:c2:44:bf:92:56:73:34:54
X509v3 Authority Key Identifier: 3d:f5:05:f8:c2:fe:1a:95:e4:96:d5:40:3c:39:82:ff:4b:c7:41:ce:30:78:74:f8:22:8f:34:32:5c:17:3e:1d
X509v3 Key Usage: (critical) dataEncipherment 
X509v3 Extended Key Usage: (critical) ServerAuthentication CodeSigning 
AuthID[0] = 
AuthID[1] = 3df505f8c2fe1a95e496d5403c3982ff4bc741ce307874f8228f34325c173e1d
AuthID[2] = 3165301b060355040304146c65616e63727970746f207465737420696e74323011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 04050607080900013165301b060355040304146c65616e63727970746f207465737420696e74323011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 2592
Self-signed: no

PKCS#7 Message Generator

The leancrypto library offers a PKCS#7 message generator along with the PKCS#7 parser. This generator allows to generate PQC certificates using all PQC algorithms offered by the library.

The generator is accessed by one of the following means:

  • Use of the PKCS#7 API offered with lc_pkcs7_generator.h.

  • Use of the PKCS#7 generator application lc_pkcs7_generator implemented in the apps/src directory.

The lc_pkcs7_generator application is to be considered a frontend to the API which implies that the API implements all important operations with respect to the generation.

The following sections outline lc_pkcs7_generator commands to obtain such messages.

Generate PKCS#7 Message with 4-way Certificate Chain

The following command uses X.509 certificates and keys generated in the preceding sections:

  1. generates a PKCS#7 message ml-dsa.p7b

  2. signs the data found in the file ml-dsa87_cacert.der

  3. signs with the signer X.509 certificate ml-dsa87_leaf.der and its associated private key ml-dsa87_leaf.privkey

  4. adds the signer certificate, and the 3 CA certificates of ml-dsa44_int2.der, ml-dsa65_int1.der, and the root CA certificate ml-dsa87_cacert.der (the root or the intermediate certificates are not needed, provided they are present in the trust store during later verification)

  5. verifies the PKCS#7 message against a trust store holding the root CA certificate ml-dsa87_cacert.der

The command generates:

  • ml-dsa.p7b: PKCS#7 message
lc_pkcs7_generator
  -o ml-dsa.p7b 
  -i ml-dsa87_cacert.der
  --x509-signer ml-dsa87_leaf.der
  --signer-sk-file ml-dsa87_leaf.privkey
  --x509-cert ml-dsa44_int2.der
  --x509-cert ml-dsa65_int1.der
  --x509-cert ml-dsa87_cacert.der
  --trust-anchor ml-dsa87_cacert.der

Verify PKCS#7 Message with 4-way Certificate Chain

The following command verifies the PKCS#7 message generated in the preceding sections. It uses

  • the data in the file in the file ml-dsa87_cacert.der that was signed

  • the PKCS#7 message in ml-dsa.p7b

  • using the trust anchor ml-dsa87_cacert.der

lc_pkcs7_geerator
  --print-pkcs7 ml-dsa.p7b
  -i ml-dsa87_cacert.der
  --trust-anchor ml-dsa87_cacert.der

The command should return without an error. The command also prints out the PKCS#7 message with its 4-way certificate chain:

======= X.509 certificate listing ==========
Serial Number: 04:05:06:07:08:09:00:01
Signature Algorithm: ML-DSA44 <builtin hash>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int2
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test leaf
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA87
X509v3 Subject Key Identifier: 0d:0e:0f:00:01:02:03
X509v3 Authority Key Identifier: 0c:0d:0e:0f:00:01:02
X509v3 Key Usage: (critical) dataEncipherment 
X509v3 Extended Key Usage: (critical) ServerAuthentication CodeSigning 
AuthID[0] = 
AuthID[1] = 0c0d0e0f000102
AuthID[2] = 3165301b060355040304146c65616e63727970746f207465737420696e74323011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 04050607080900013165301b060355040304146c65616e63727970746f207465737420696e74323011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 2592
Self-signed: no
====== End one certificate ==========
Serial Number: 03:03:04:05:06:07:08:09
Signature Algorithm: ML-DSA65 <builtin hash>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int1
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int2
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA44
X509v3 Subject Key Identifier: 0c:0d:0e:0f:00:01:02
X509v3 Authority Key Identifier: 0b:0c:0d:0e:0f:00:01
X509v3 Basic Constraints: CA (critical)
X509v3 Key Usage: (critical) digitalSignature keyEncipherment keyCertSign 
AuthID[0] = 
AuthID[1] = 0b0c0d0e0f0001
AuthID[2] = 3165301b060355040304146c65616e63727970746f207465737420696e74313011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 03030405060708093165301b060355040304146c65616e63727970746f207465737420696e74313011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 1312
Self-signed: no
====== End one certificate ==========
Serial Number: 02:03:03:04:05:06:07:08
Signature Algorithm: ML-DSA87 <builtin hash>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test CA
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int1
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA65
X509v3 Subject Key Identifier: 0b:0c:0d:0e:0f:00:01
X509v3 Authority Key Identifier: 0a:0b:0c:0d:0e:0f
X509v3 Basic Constraints: CA (critical)
X509v3 Key Usage: (critical) digitalSignature keyEncipherment keyCertSign 
AuthID[0] = 
AuthID[1] = 0a0b0c0d0e0f
AuthID[2] = 31633019060355040304126c65616e63727970746f20746573742043413011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 020303040506070831633019060355040304126c65616e63727970746f20746573742043413011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 1952
Self-signed: no
====== End one certificate ==========
Serial Number: 01:02:03:04:05:06:07:08
Signature Algorithm: ML-DSA87 <builtin hash>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test CA
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test CA
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA87
X509v3 Subject Key Identifier: 0a:0b:0c:0d:0e:0f
X509v3 Authority Key Identifier: 0a:0b:0c:0d:0e:0f
X509v3 Basic Constraints: CA (critical)
X509v3 Key Usage: (critical) digitalSignature keyEncipherment keyCertSign 
AuthID[0] = 
AuthID[1] = 0a0b0c0d0e0f
AuthID[2] = 31633019060355040304126c65616e63727970746f20746573742043413011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 010203040506070831633019060355040304126c65616e63727970746f20746573742043413011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 2592
Self-signed: yes
====== End one certificate ==========
======= PKCS7 signed info listing ==========
Serial Number: 04:05:06:07:08:09:00:01
Signature Algorithm: ML-DSA44 <builtin hash>
Issuer: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test int2
Subject: C = DE, ST = Saxony, O = leancrypto, OU = leancrypto test OU, CN = leancrypto test leaf
Valid From: 2024-10-21 18:22:08
Valid To: 2034-10-11 22:16:46
Public Key Algorithm: ML-DSA87
X509v3 Subject Key Identifier: 0d:0e:0f:00:01:02:03
X509v3 Authority Key Identifier: 0c:0d:0e:0f:00:01:02
X509v3 Key Usage: (critical) dataEncipherment 
X509v3 Extended Key Usage: (critical) ServerAuthentication CodeSigning 
AuthID[0] = 
AuthID[1] = 0c0d0e0f000102
AuthID[2] = 3165301b060355040304146c65616e63727970746f207465737420696e74323011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
X.509 ID = 04050607080900013165301b060355040304146c65616e63727970746f207465737420696e74323011060355040a040a6c65616e63727970746f3009060355040604024445300d060355040804065361786f6e793019060355040b04126c65616e63727970746f2074657374204f55
Public key size 2592
Self-signed: no
Signed Authinfo = 8189304f06092a864886f70d0109043142044016cc53131266f12f8dbb5b10de31c1868a44c1b1a4eadb0d1ea43e36802cf5fe7aa2c22659be36ea3f8d4462a66eb2500533b1ac8378f8f96ae6e91031779878301806092a864886f70d010903310b00092a864886f70d010701301c06092a864886f70d010905310f170d3234313132313038313833385a
Signature Algorithm: ML-DSA87 <builtin hash>
AuthID[0] = 0d0e0f00010203
AuthID[1] = 
AuthID[2] = 
signerInfos messageDigest = 7f155a4b8be3f85994060b90c3ec0e6a2e4dc5dac44d904940289bf3006b421b8b7613825368d6c451676acd5b03df307f8921171fff4261fd0584ac0653160a
Message digest algorithm: SHA3-512
Size of protected data: 7606
====== End one PKCS7 signed info listing ==========

PKCS#7 Trust Verification

The trust verification is implemented with the API call of lc_pkcs7_verify. It applies the following approach:

  • The signer of the message must be present in the PKCS#7 message and its signature of the message is verified along with the signer certificate validity.

  • If no trust store is used, ensure that the certificate chain contains a root CA and that there is a certificate chain starting with the message signer to the CA. If such certificate chain validation was successful, the PKCS#7 message verification succeeds.

  • When generating a trust store, all added certificates are validated to lead to a root CA found in the trust store.

  • If a trust store is used, ensure that the certificate chain starts with the signer of the message and leads to a certificate found in the trust store. As any certificate in the trust store is guaranteed to have a valid chain to a root CA (see preceding bullet), no further certificate chain validation is applied. If such certificate chain validation is successful, the PKCS#7 message verification succeeds.

  • A root CA or intermediate CA certificate are required to possess the key usage flag of keyCertSign for successful validation.

  • A caller can provide the required key usage or EKU flags that a signer must bear for successful validation to lc_pkcs7_verify or via lc_pkcs7_generator.