Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x509-cert: certificate verification support #838

Open
dayday2019 opened this issue Jan 4, 2023 · 6 comments
Open

x509-cert: certificate verification support #838

dayday2019 opened this issue Jan 4, 2023 · 6 comments

Comments

@dayday2019
Copy link

No description provided.

@tarcieri
Copy link
Member

tarcieri commented Jan 4, 2023

We don’t implement this yet, sorry. The x509-cert crate is still a work in progress

@dayday2019
Copy link
Author

Thank you for your reply! Will you implement this in a near future? I am looking forward to it

@tarcieri
Copy link
Member

tarcieri commented Jan 4, 2023

You can try out @carl-wallace's crate which implements certificate verification:

https://github.com/carl-wallace/rust-pki/tree/main/certval

Hopefully we can get such work upstream soon

@lumag
Copy link
Contributor

lumag commented Apr 16, 2023

@dayday2019
a very limited implementation. Hope it can be helpful. License: Apache-2 + MIT

Note: it doesn't perform path validation, etc. Just signature checks.

pub fn verify_signature(
    cert: &Certificate,
    signed_data: &[u8],
    signature: &[u8],
    algo: &AlgorithmIdentifierOwned,
) -> Result<(), Box<dyn Error>> {
    let spki = cert.tbs_certificate.subject_public_key_info.owned_to_ref();

    match algo.oid {
        OIDdb::rfc5912::SHA_1_WITH_RSA_ENCRYPTION => {
            println!("PKCS#1 v1.5 / SHA1 signature");
            rsa::pkcs1v15::VerifyingKey::<Sha1>::new(RsaPublicKey::try_from(spki)?)
                .verify(signed_data, &signature.try_into()?)?;
        }

        OIDdb::rfc5912::SHA_256_WITH_RSA_ENCRYPTION => {
            println!("PKCS#1 v1.5 / SHA256 signature");
            rsa::pkcs1v15::VerifyingKey::<Sha256>::new(RsaPublicKey::try_from(spki)?)
                .verify(signed_data, &signature.try_into()?)?;
        }

        OIDdb::rfc5912::ID_RSASSA_PSS => {
            let params = algo
                .parameters
                .as_ref()
                .ok_or("Empty PSS parameters")?
                .decode_as::<RsaPssParams>()?;

            match params.hash.oid {
                OIDdb::rfc5912::ID_SHA_256 => {
                    println!("PSS / SHA256 signature");

                    rsa::pss::VerifyingKey::<Sha256>::new(RsaPublicKey::try_from(spki)?)
                        .verify(signed_data, &signature.try_into()?)?
                }
                OIDdb::rfc5912::ID_SHA_1 => {
                    println!("PSS / SHA1 signature");

                    rsa::pss::VerifyingKey::<Sha1>::new(RsaPublicKey::try_from(spki)?)
                        .verify(signed_data, &signature.try_into()?)?
                }
                _ => return Err(format!("Unknown PSS hash algo {}", params.hash.oid).into()),
            }
        }

        OIDdb::rfc5912::ECDSA_WITH_SHA_256 => {
            println!("ECDSA P256 signature");

            let signature = p256::ecdsa::DerSignature::try_from(signature)?;

            p256::ecdsa::VerifyingKey::try_from(spki)?.verify(signed_data, &signature)?;
        }

        OIDdb::rfc5912::ECDSA_WITH_SHA_384 => {
            println!("ECDSA P384 signature");

            let signature = p384::ecdsa::DerSignature::try_from(signature)?;

            p384::ecdsa::VerifyingKey::try_from(spki)?.verify(signed_data, &signature)?;
        }
        _ => {
            return Err(format!(
                "Unknown signature algo {}",
                cert.tbs_certificate.signature.oid
            )
            .into())
        }
    }

    Ok(())
}

pub fn verify_cert_signature(
    cert: &Certificate,
    signed: &Certificate,
) -> Result<(), Box<dyn Error>> {
    if cert.tbs_certificate.subject != signed.tbs_certificate.issuer {
        return Err("Certificate issuer does not match".into());
    }

    let signed_data = signed.tbs_certificate.to_der()?;
    let signature = signed
        .signature
        .as_bytes()
        .ok_or("Could not get cert signature")?;

    verify_signature(cert, &signed_data, signature, &signed.signature_algorithm)
}

@woodruffw
Copy link
Contributor

Tying a few related threads together: PyCA Cryptography (i.e. pip install cryptography) recently merged a pure-Rust X.509 path validator, which we've tested pretty extensively against both other implementations and well-known pathological cases.

The code of that implementation is 100% pure Rust (with a trait abstraction for any backend to provide the crypto), but is tied to PyCA Cryptography's own ASN.1/DER and X.509 libraries. Still, the core approach may be instructive/valuable/reusable for any other Rust implementation 🙂

The code is here: https://github.com/pyca/cryptography/tree/main/src/rust/cryptography-x509-verification/src, and can be followed top-down from verify in lib.rs.

(I'm happy to answer questions about the implementation as well! Feel free to ping me here or on the RustCrypto Zulip 🙂)

@tarcieri
Copy link
Member

@woodruffw x509-limbo seems interesting for testing!

@tarcieri tarcieri changed the title How to verify the signature of a x509 certificate? Is there a function to do this? x509-certificate: certificate verification support Aug 18, 2024
@tarcieri tarcieri changed the title x509-certificate: certificate verification support x509-cert: certificate verification support Aug 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants
@tarcieri @lumag @woodruffw @dayday2019 and others