
When designing a Public Key Infrastructure, the question of a suitable form of cryptography or the selection of suitable algorithms inevitably arises at some point. There are now a number of key generation algorithms, signature algorithms and signature hash algorithms available for this purpose. However, which one should you choose? Do you have to commit to just one or are combinations possible? What about a combination of all possible or at least the most widely used algorithms? I wanted to practically explore this:
As a first step, I set up a PKI consisting of 1 Root CA, 5 Sub CAs, and 1 Leaf Certificate using OpenSSL.
1. Create a DSA 4096 bit self-signed Root-CA utilizing SHA3-256
openssl dsaparam -out dsaparam.pem 4096
openssl gendsa -out DSA-Root.key dsaparam.pem
openssl req -x509 -key DSA-Root.key -subj "/CN=DSA-Root-CA/O=CryptoAgility/C=DE" -addext "keyUsage = cRLSign, keyCertSign" -out DSA-Root.pem -sha3-256
2. Create an EdDSA (curve25519) 256 bit Sub1-Ca utilizing utilizing SHA512
openssl genpkey -algorithm ed25519 -out EdDSA-SUB1.key
openssl req -new -key EdDSA-SUB1.key -subj "/CN=EdDSA-Sub1-CA/O=CryptoAgility/C=DE" -addext "keyUsage = cRLSign, keyCertSign" -out EdDSA-Sub1.req
openssl x509 -CA DSA-Root.pem -CAkey DSA-Root.key -req -in EdDSA-Sub1.req -days 365 -out EdDSA-Sub1.pem -sha512 -extfile config.cfg -extensions ca_reqext
3. Create a RSA 3096 bit Sub2-Ca utilizing Ed25519
openssl genrsa -out RSA-Sub2.key 3096
openssl req -new -key RSA-Sub2.key -subj "/CN=RSA-Sub2-CA/O=CryptoAgility/C=DE" -addext "keyUsage = cRLSign, keyCertSign" -out RSA-Sub2.req
openssl x509 -CA EdDSA-Sub1.pem -CAkey EdDSA-Sub1.key -req -in RSA-Sub2.req -days 365 -out RSA-Sub2.pem -extfile config.cfg -extensions ca_reqext
4. Create an EdDSA (curve448) 456 bit Sub3-Ca utilizing SHA3-384
openssl genpkey -algorithm ed448 -out EdDSA-Sub3.key
openssl req -new -key EdDSA-SUB3.key -subj "/CN=EdDSA-Sub3-CA/O=CryptoAgility/C=DE" -addext "keyUsage = cRLSign, keyCertSign" -out EdDSA-Sub3.req
openssl x509 -CA RSA-Sub2.pem -CAkey RSA-Sub2.key -req -in EdDSA-Sub3.req -days 365 -out EdDSA-Sub3.pem -sha3-384 -extfile config.cfg -extensions ca_reqext
5. Create an ECDSA (secp256k1) 256 bit Sub4-Ca utilizing Ed448 (SHAKE)
openssl ecparam -name secp256k1 -genkey -noout -out ECDSA-Sub4.key
openssl req -new -key ECDSA-Sub4.key -subj "/CN=ECDSA-Sub4-CA/O=CryptoAgility/C=DE" -addext "keyUsage = cRLSign, keyCertSign" -out ECDSA-Sub4.req
openssl x509 -CA EdDSA-Sub3.pem -CAkey EdDSA-Sub3.key -req -in ECDSA-Sub4.req -days 365 -out ECDSA-Sub4.pem -sha256 -extfile config.cfg -extensions ca_reqext
6. Create an ECDSA (secp521r1) 521 bit Sub-Ca5 utilizing SHA256
openssl ecparam -name secp521r1 -genkey -noout -out ECDSA-Sub5.key
openssl req -new -key ECDSA-Sub5.key -subj "/CN=ECDSA-Sub5-CA/O=CryptoAgility/C=DE" -addext "keyUsage = cRLSign, keyCertSign" -out ECDSA-Sub5.req
openssl x509 -CA ECDSA-Sub4.pem -CAkey ECDSA-Sub4.key -req -in ECDSA-Sub5.req -days 365 -out ECDSA-Sub5.pem -sha256 -extfile config.cfg -extensions ca_reqext
7. Create a DSA 4096 bit leaf-certificate utilizing SHA3-512
openssl gendsa -out DSA-Leaf.key dsaparam.pem
openssl req -new -key DSA-Leaf.key -subj "/CN=DSA-Leaf/O=CryptoAgility/C=DE" -addext "keyUsage = digitalSignature" -out DSA-Leaf.req
openssl x509 -CA ECDSA-Sub5.pem -CAkey ECDSA-Sub5.key -req -in DSA-Leaf.req -days 365 -out DSA-Leaf.pem -SHA3-512 -extfile config.cfg -extensions ca_reqext6
As you might see from the OpenSSL commands used, it’s a colorful mixture of key generation, hashing methods and signature algorithms.
Unfortunately, during the subsequent verification of the certificate chain, it was discovered that it was not possible to do so using either OpenSSL or Windows however OpenSSL was used to create these certificates. Every attempt resulted in a verification error.
Because I wanted to investigate the matter further, I tried to reconstruct a corresponding chain validation using Python to be sure that it is not valid for whatever reason.
from cryptography.x509 import load_pem_x509_certificat
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.x509.oid import ExtensionOID
from cryptography.hazmat.primitives.serialization import Encoding
from cryptography.hazmat.primitives.serialization import PublicFormat
from cryptography.hazmat.primitives.asymmetric import padding
import sys
with open ("DSA-Root.pem", "rb") as f:
DSA_Root_pem = f.read()
DSA_Root_cert = load_pem_x509_certificate(DSA_Root_pem, default_backend())
f.close()
with open ("EdDSA-Sub1.pem", "rb") as f:
EdDSA_Sub1_pem = f.read()
EdDSA_Sub1_cert = load_pem_x509_certificate(EdDSA_Sub1_pem, default_backend())
f.close()
with open ("RSA-Sub2.pem", "rb") as f:
RSA_Sub2_pem = f.read()
RSA_Sub2_cert = load_pem_x509_certificate(RSA_Sub2_pem, default_backend())
f.close()
with open ("EdDSA-Sub3.pem", "rb") as f:
EdDSA_Sub3_pem = f.read()
EdDSA_Sub3_cert = load_pem_x509_certificate(EdDSA_Sub3_pem, default_backend())
f.close()
with open ("ECDSA-Sub4.pem", "rb") as f:
ECDSA_Sub4_pem = f.read()
ECDSA_Sub4_cert = load_pem_x509_certificate(ECDSA_Sub4_pem, default_backend())
f.close()
with open ("ECDSA-Sub5.pem", "rb") as f:
ECDSA_Sub5_pem = f.read()
ECDSA_Sub5_cert = load_pem_x509_certificate(ECDSA_Sub5_pem, default_backend())
f.close()
with open ("DSA-Leaf.pem", "rb") as f:
DSA_Leaf_pem = f.read()
DSA_Leaf_cert = load_pem_x509_certificate(DSA_Leaf_pem, default_backend())
f.close()
def check_chain_AKI_SKI():
cert_chain_array = [DSA_Root_cert, EdDSA_Sub1_cert, RSA_Sub2_cert,EdDSA_Sub3_cert,ECDSA_Sub4_cert,ECDSA_Sub5_cert, DSA_Leaf_cert]
for x in range(1,len(cert_chain_array)-1):
if(cert_chain_array[x-1].extensions.get_extension_for_oid(ExtensionOID.SUBJECT_KEY_IDENTIFIER).value.digest == cert_chain_array[x].extensions.get_extension_for_oid(ExtensionOID.AUTHORITY_KEY_IDENTIFIER).value.key_identifier):
pass
else:
raise Exception("Invalid chain")
def check_chain_AuthorityDN_SubjectDN():
cert_chain_array = [DSA_Root_cert, EdDSA_Sub1_cert, RSA_Sub2_cert,EdDSA_Sub3_cert,ECDSA_Sub4_cert,ECDSA_Sub5_cert, DSA_Leaf_cert]
for x in range(1,len(cert_chain_array)-1):
if(cert_chain_array[x-1].subject == cert_chain_array[x].issuer):
pass
else:
raise Exception("Invalid chain")
DSA_ROOT_pub = DSA_Root_cert.public_key()
EdDSA_Sub1_signature= EdDSA_Sub1_cert.signature
EdDSA_Sub1_pub = EdDSA_Sub1_cert.public_key()
RSA_Sub2_signature = RSA_Sub2_cert.signature
RSA_Sub2_pub = RSA_Sub2_cert.public_key()
EdDSA_Sub3_signature = EdDSA_Sub3_cert.signature
EdDSA_Sub3_pub = EdDSA_Sub3_cert.public_key()
ECDSA_Sub4_signature = ECDSA_Sub4_cert.signature
ECDSA_Sub4_pub = ECDSA_Sub4_cert.public_key()
ECDSA_Sub5_signature = ECDSA_Sub5_cert.signature
ECDSA_Sub5_pub = ECDSA_Sub5_cert.public_key()
DSA_Leaf_signature = DSA_Leaf_cert.signature
try:
check_chain_AKI_SKI()
check_chain_AuthorityDN_SubjectDN()
print("SelfSigned RootCA -> SignatureKeyType:DSA PubKeyKeylength:{} SignatureHashAlgorithm:{} SignatureSizeInBytes:{}".format(DSA_ROOT_pub.key_size,"sha3-256",len(DSA_Root_cert.signature)))
DSA_ROOT_pub.verify(EdDSA_Sub1_signature,EdDSA_Sub1_cert.tbs_certificate_bytes,hashes.SHA512())
print("SubCa1\t\t -> SignatureKeyType:DSA PubKeyKeylength:{} SignatureHashAlgorithm:{} SignatureSizeInBytes:{}".format(DSA_ROOT_pub.key_size,"sha3-256",len(EdDSA_Sub1_cert.signature)))
EdDSA_Sub1_pub.verify(RSA_Sub2_signature,RSA_Sub2_cert.tbs_certificate_bytes)
print("SubCa2\t\t -> SignatureKeyType:Ed25519 PubKeyKeylength:{} SignatureHashAlgorithm:{} SignatureSizeInBytes:{}".format(len(EdDSA_Sub1_pub.public_bytes(Encoding.Raw, PublicFormat.Raw) * 8),"sha-512",len(RSA_Sub2_cert.signature)))
RSA_Sub2_pub.verify(EdDSA_Sub3_signature,EdDSA_Sub3_cert.tbs_certificate_bytes,padding.PKCS1v15(),hashes.SHA3_384())
print("SubCa3\t\t -> SignatureKeyType:RSA PubKeyKeylength:{} SignatureHashAlgorithm:{} SignatureSizeInBytes:{}".format(RSA_Sub2_pub.key_size,EdDSA_Sub3_cert.signature_hash_algorithm.name,len(EdDSA_Sub3_cert.signature)))
EdDSA_Sub3_pub.verify(ECDSA_Sub4_signature, ECDSA_Sub4_cert.tbs_certificate_bytes)
print("SubCa4\t\t -> SignatureKeyType:Ed448 PubKeyKeylength:{} SignatureHashAlgorithm:{} SignatureSizeInBytes:{}".format(len(EdDSA_Sub3_pub.public_bytes(Encoding.Raw, PublicFormat.Raw) * 8),"shake-256",len(ECDSA_Sub4_cert.signature)))
ECDSA_Sub4_pub.verify(ECDSA_Sub5_signature, ECDSA_Sub5_cert.tbs_certificate_bytes, ec.ECDSA(hashes.SHA256()))
print("SubCa5\t\t -> SignatureKeyType:ECDSA PubKeyKeylength:{} SignatureHashAlgorithm:{} SignatureSizeInBytes:{}".format(ECDSA_Sub4_pub.key_size,ECDSA_Sub5_cert.signature_hash_algorithm.name,len(ECDSA_Sub5_cert.signature)))
ECDSA_Sub5_pub.verify(DSA_Leaf_signature, DSA_Leaf_cert.tbs_certificate_bytes, ec.ECDSA(hashes.SHA3_512()))
print("Leaf-Cert\t -> SignatureKeyType:ECDSA PubKeyKeylength:{} SignatureHashAlgorithm:{} SignatureSizeInBytes:{}".format(ECDSA_Sub5_pub.key_size,DSA_Leaf_cert.signature_hash_algorithm.name,len(DSA_Leaf_cert.signature)))
print("Signature-Chain is valid!")
except:
print("Signature-Chain not valid!")
sys.exit()e
Output:
SelfSigned RootCA -> SignatureKeyType:DSA PubKeyKeylength:4096 SignatureHashAlgorithm:sha3-256 SignatureSizeInBytes:62
SubCa1 -> SignatureKeyType:DSA PubKeyKeylength:4096 SignatureHashAlgorithm:sha3-256 SignatureSizeInBytes:62
SubCa2 -> SignatureKeyType:Ed25519 PubKeyKeylength:256 SignatureHashAlgorithm:sha-512 SignatureSizeInBytes:64
SubCa3 -> SignatureKeyType:RSA PubKeyKeylength:3096 SignatureHashAlgorithm:sha3-384 SignatureSizeInBytes:387
SubCa4 -> SignatureKeyType:Ed448 PubKeyKeylength:456 SignatureHashAlgorithm:shake-256 SignatureSizeInBytes:114
SubCa5 -> SignatureKeyType:ECDSA PubKeyKeylength:256 SignatureHashAlgorithm:sha256 SignatureSizeInBytes:72
Leaf-Cert -> SignatureKeyType:ECDSA PubKeyKeylength:521 SignatureHashAlgorithm:sha3-512 SignatureSizeInBytes:139
Signature-Chain is valid!
Surprisingly, with Python and using the pycryptography library, which is a really good library, a successful validation was possible.
And the moral of this story: even though there is nothing cryptographic against the use of different algorithms and keys, it does not necessarily mean that the systems using them have the necessary cryptographic agility to handle them.
Foresighted action is therefore indispensable.
Limitations:
1.Ed25519 always uses SHA512 and curve25519
2.Ed448 always uses SHAKE256 and curve448
3.ECDSA signatures are always 2 times longer than the signer’s private key
4.The OID for DSA_with_SHA3-256 is still not registered (Source: oid-info.com)