sfackler / rust-native-tls

Apache License 2.0
470 stars 195 forks source link

Apple M1: Identity::from_pkcs8 does not work with "PKCS_ECDSA_P256_SHA256" #225

Open gyuho opened 2 years ago

gyuho commented 2 years ago

Hi,

The following code does not work for no apparent reason, and any hint for using ECDSA keys would be greatly appreciated!

use rcgen::{PKCS_ECDSA_P256_SHA256, CertificateParams};

let mut cert_params = CertificateParams::default();
cert_params.alg = &PKCS_ECDSA_P256_SHA256;

let cert = Certificate::from_params(cert_params).unwrap();
let cert_contents = cert.serialize_pem().unwrap();
let key_contents = cert.serialize_private_key_pem();

native_tls::Identity::from_pkcs8(cert_contents.as_bytes(), key_contents.as_bytes()).unwrap()
Custom { kind: Other, error: "failed to create native-tls Identity Unknown format in import." }'

NOTE: CertificateParams will generate the ECDSA keys as follows, and it should be the correct pkcs8 encoding:

impl KeyPair {
    /// Generate a new random key pair for the specified signature algorithm
    pub fn generate(alg :&'static SignatureAlgorithm) -> Result<Self, RcgenError> {
        let system_random = SystemRandom::new();
        match alg.sign_alg {
            SignAlgo::EcDsa(sign_alg) => {
                let key_pair_doc = EcdsaKeyPair::generate_pkcs8(sign_alg, &system_random)?;
                let key_pair_serialized = key_pair_doc.as_ref().to_vec();

                let key_pair = EcdsaKeyPair::from_pkcs8(&sign_alg, &&key_pair_doc.as_ref()).unwrap();
                Ok(KeyPair {
                    kind : KeyPairKind::Ec(key_pair),
                    alg,
                    serialized_der : key_pair_serialized,
                })
            },
            SignAlgo::EdDsa(_sign_alg) => {
                let key_pair_doc = Ed25519KeyPair::generate_pkcs8(&system_random)?;
                let key_pair_serialized = key_pair_doc.as_ref().to_vec();

                let key_pair = Ed25519KeyPair::from_pkcs8(&&key_pair_doc.as_ref()).unwrap();
                Ok(KeyPair {
                    kind : KeyPairKind::Ed(key_pair),
                    alg,
                    serialized_der : key_pair_serialized,
                })
            },

Similar code works fine for RSA keys...

sfackler commented 2 years ago

What does the PEM for one of these keys look like?

gyuho commented 2 years ago

@sfackler Here's the openssl output

openssl x509 -in /tmp/test.insecure.cert -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 5564983925622557331 (0x4d3acb65537dca93)
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: C=US, ST=NY, O=Ava Labs, CN=avalanche-ops
        Validity
            Not Before: Jan  1 00:00:00 2022 GMT
            Not After : Jan  1 00:00:00 5000 GMT
        Subject: C=US, ST=NY, O=Ava Labs, CN=avalanche-ops
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub: 
                    04:49:d2:4e:78:cf:c7:b5:04:8f:92:7e:ee:2a:4b:
                    f3:10:2a:28:bb:6b:d7:78:d8:60:6a:99:b2:32:4f:
                    02:31:69:82:24:4e:31:a2:0a:22:74:a8:4c:22:b3:
                    4e:b5:51:da:49:d1:74:6b:c3:8d:c2:e5:53:e9:9b:
                    fe:cb:f6:46:09
                ASN1 OID: prime256v1
                NIST CURVE: P-256
    Signature Algorithm: ecdsa-with-SHA256
         30:44:02:20:05:11:b8:7f:3d:c3:9b:be:0c:0e:e4:b5:7b:3d:
         12:51:96:5b:44:3e:cd:15:42:1f:89:7b:84:4c:b7:e6:37:64:
         02:20:34:b4:97:5f:eb:cb:fe:e7:e9:0b:9a:65:36:c3:8f:0f:
         2b:f1:1d:27:0d:bc:de:30:b4:c8:7f:5c:3f:60:ac:02

And here's the raw PEM cert:

cat /tmp/test.insecure.cert
-----BEGIN CERTIFICATE-----
MIIBfzCCASagAwIBAgIITTrLZVN9ypMwCgYIKoZIzj0EAwIwRTELMAkGA1UEBgwC
VVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQKDAhBdmEgTGFiczEWMBQGA1UEAwwNYXZh
bGFuY2hlLW9wczAgFw0yMjAxMDEwMDAwMDBaGA81MDAwMDEwMTAwMDAwMFowRTEL
MAkGA1UEBgwCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQKDAhBdmEgTGFiczEWMBQG
A1UEAwwNYXZhbGFuY2hlLW9wczBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEnS
TnjPx7UEj5J+7ipL8xAqKLtr13jYYGqZsjJPAjFpgiROMaIKInSoTCKzTrVR2knR
dGvDjcLlU+mb/sv2RgkwCgYIKoZIzj0EAwIDRwAwRAIgBRG4fz3Dm74MDuS1ez0S
UZZbRD7NFUIfiXuETLfmN2QCIDS0l1/ry/7n6QuaZTbDjw8r8R0nDbzeMLTIf1w/
YKwC
-----END CERTIFICATE-----
sfackler commented 2 years ago

That's the certificate rather than the private key.

sfackler commented 2 years ago

Also, which OS are you running this on?

gyuho commented 2 years ago

@sfackler

Here's the key:

cat /tmp/test.insecure.key 
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgGYZMDckD0VzoYsxG
EYR/s2sob7jlvR5oY9ZT5U5iqfehRANCAARJ0k54z8e1BI+Sfu4qS/MQKii7a9d4
2GBqmbIyTwIxaYIkTjGiCiJ0qEwis061UdpJ0XRrw43C5VPpm/7L9kYJ
-----END PRIVATE KEY-----

And I am running on OS with M1

gyuho commented 2 years ago

Hmm, works fine on linux machine...

❯ uname -r
5.10.102-99.473.amzn2.x86_64
sfackler commented 2 years ago

That makes me think that macOS's Security.framework library may not support ECDSA keys then unfortunately.

andyleiserson commented 1 year ago

I looked at this a bit. I don't think the issue is complete lack of ECDSA key support. I think it is an issue with importing ECDSA keys from unencrypted PKCS8 format. Using the command line security import importer, I was able to import a P256 key from encrypted PKCS8, and I was also able to import the same key in unencrypted "OpenSSL" format.

I was hoping it might be possible to import the unencrypted pkcs8 by explicitly specifying the format, but I was unsuccessful. The keychain importer seems to interpret "pkcs8" to mean only encrypted pkcs8. If I manually specify pkcs8 format, the import fails trying to parse EncryptedPrivateKeyInfo. If I don't specify pkcs8 format, impExpGuessKeyParams tries to detect the format, and can't find something that works, because the only format it tries for ECDSA keys is OpenSSL.