RustCrypto / RSA

RSA implementation in pure Rust
Apache License 2.0
536 stars 146 forks source link

Is RSA PSS key loading from PKCS#8 supported? #422

Closed taleks closed 5 months ago

taleks commented 5 months ago

Hi,

Are RSA PSS keys supported in rsa crate for loading from PKCS#8? Documentation has that small greyish checkbox for "PSS: Sign & Verify" bullet (though it is not clear what does it mean as there is no any legend) plus https://docs.rs/rsa/latest/rsa/index.html#pkcs8-rsa-key-encoding mentions related traits, so I assumed it is supported. But maybe I was a bit too hasty in that assumption.

If those are supported what is the right way to load RSA PSS private key?

Context

I am trying to load private key https://www.rfc-editor.org/rfc/rfc9421.html#name-example-rsa-pss-key

Test code is:

use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use pkcs8::{DecodePrivateKey, PrivateKeyInfo};
use rsa::RsaPrivateKey;

const RSA_PSS: &str = include_str!("data/rsa2048_rfc9421.pem");

fn main() {
    let line = RSA_PSS.replace("\n", "")
        .replace("-----BEGIN PRIVATE KEY-----", "")
        .replace("-----END PRIVATE KEY-----", "");

    let bytes = BASE64_STANDARD.decode(line).unwrap();
    let pk = PrivateKeyInfo::try_from(bytes.as_slice()).unwrap();

    println!("Key info: {:?}", pk);

    if let Err(err) = RsaPrivateKey::from_pkcs8_pem(RSA_PSS) {
        println!("Error: {err}")
    }
}

Output is:

Key info: PrivateKeyInfo { version: V1, algorithm: AlgorithmIdentifier { oid: ObjectIdentifier(1.2.840.113549.1.1.10), parameters: None }, public_key: None, .. }
Error: public key error: unknown/unsupported algorithm OID: 1.2.840.113549.1.1.1

My understanding is that .10 is OID for rsassa-pss, while .1 is rsaEncryption.

Validation fails here https://github.com/RustCrypto/RSA/blob/master/src/encoding.rs#L15-L23

pkcs1::ALGORITHM_OID comes from https://github.com/RustCrypto/formats/blob/master/pkcs1/src/lib.rs#L55 where it is defined as 1.2.840.113549.1.1.1.

Not really related but...

I believe error message "public key error: unknown/unsupported algorithm OID: 1.2.840.113549.1.1.1" is misleading as code https://github.com/RustCrypto/RSA/blob/master/src/encoding.rs#L16 asserts that oid is equal to pkcs1::ALGORITHM_OID.

    algorithm.assert_algorithm_oid(pkcs1::ALGORITHM_OID)?;

And spki's code is

pub fn assert_algorithm_oid(&self, expected_oid: ObjectIdentifier) -> Result<ObjectIdentifier> {
    if self.oid == expected_oid {
        Ok(expected_oid)
    } else {
        Err(Error::OidUnknown { oid: expected_oid }) // <--- should be self.oid I guess
    }
}

plus "public key" itself in that message does not look right to me.

tarcieri commented 5 months ago

If it's truly a PSS key, try using rsa::pss::SigningKey.

I agree that should probably be self.oid rather than expected_oid in spki.

If you can make that change locally and add a [patch.crates-io] directive to point to your local copy of spki, you should be able to get the OID out.

Not sure if you can post the private key in question or not but that would be helpful in figuring out what's going wrong as well.

If you can't post the private key (totally understandable), openssl asn1parse -in data/rsa2048_rfc9421.pem and all of the lines except for the final OCTET STRING would be very helpful and should also be non-sensitive.

taleks commented 5 months ago

Hi @tarcieri, thanks for the answer.

Private key is example key from RFC 9421 https://www.rfc-editor.org/rfc/rfc9421.html#name-example-rsa-pss-key

Here is output of openssl asn1parse -in data/rsa2048_rfc9421.pem

openssl asn1parse -in data/rsa2048_rfc9421.pem
    0:d=0  hl=4 l=1214 cons: SEQUENCE          
    4:d=1  hl=2 l=   1 prim: INTEGER           :00
    7:d=1  hl=2 l=  11 cons: SEQUENCE          
    9:d=2  hl=2 l=   9 prim: OBJECT            :rsassaPss
   20:d=1  hl=4 l=1194 prim: OCTET STRING      [HEX DUMP]: ... 

If it's truly a PSS key, try using rsa::pss::SigningKey.

You mean update sample code above to something like this?

rsa::pss::SigningKey::from_pkcs8_pem(RSA_2048_PEM_EXAMPLE)

I believe DecodePrivateKey trait is not defined for SigningKey, at least I see only EncodePrivateKey here https://github.com/RustCrypto/RSA/blob/master/src/pss/signing_key.rs#L181-L188 and my IDE fails to find any related imports.

I also tried it this way:

    let pk = PrivateKeyInfo::try_from(bytes.as_slice()).unwrap(); // same as in sample in the first message
    let rsa_key = RsaPrivateKey::try_from(pk); // <-- Err(PublicKey(OidUnknown { oid: ObjectIdentifier(1.2.840.113549.1.1.1) }))
    let signing_key = rsa::pss::SigningKey::<Sha512>::new(rsa_key.unwrap());
tarcieri commented 5 months ago

Indeed you're right, we don't have a DecodePrivateKey impl for rsa::pss::SigningKey.

To the extent we support that OID at all, it's in the signature encoding itself: https://github.com/RustCrypto/RSA/blob/7341cd0/src/pss.rs#L243

cc @lumag

lumag commented 5 months ago

Hmm, let me take a look.

lumag commented 5 months ago

https://github.com/RustCrypto/RSA/pull/424

tarcieri commented 5 months ago

@taleks #424 is merged but that's on our current v0.10-pre development series. It would be good to get confirmation it addresses your issue

taleks commented 5 months ago

@tarcieri Could not check it in actual project yet as there is dependencies version conflict, but code extracted to separate workspace works with as expected with the recent commit e54fb7da1a7dea1602bdb7da8e9fbbca9edc4060 and rsa::pss::SigningKey::<Sha512>::from_pkcs8_pem(...) to load key for signing messages.

Thank you folks for the prompt response and fix.