Add public key pinning
Some more reference material: https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning
There’s an OpenSSL example in there. If given a pointer or two I’d be happy to try implement this.
Currently reqwest also does not work if a certificate store is not available on the system, even if the correct certificate has been added with .add_root_certificate() (or a public key is pinned). Running without a certificate store should probably also be supported as pinning methods get added.
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error(Hyper(Error(Connect, Custom { kind: Other, error: Ssl(Error { code: ErrorCode(1), cause: Some(Ssl(ErrorStack([Error { code: 337047686, library: "SSL routines", function: "tls_process_server_certificate", reason: "certificate verify failed", file: "ssl/statem/statem_clnt.c", line: 1921 }]))) }, X509VerifyResult { code: 20, error: "unable to get local issuer certificate" }) })), "https://example.com/")', src/libcore/result.rs:1084:5
any activity on this? I have the same prolem...
Doesn't #1150 fix this?
using add_root_certificate and tls_built_in_root_certs should create the desired behavior unless I misunderstood what the issue is about.
Hi, any news here?
Doesn't #1150 fix this? using
add_root_certificateandtls_built_in_root_certsshould create the desired behavior unless I misunderstood what the issue is about.
No, add_root_certificate is much more strict because it only allows adding a root certificate. I just tried to put only the leaf certificate there, and the connection got rejected.
Furthermore, I would like to supply the sha256 of the leaf certificate instead of providing the full certificate (chain)
What would be needed for this issue to be moved from label B-rfc (Blocked Request for comments. More discussion would help move this along.) to E-*?
It'd need more of a specced out design. Likely someone else motivated to consider how exactly it would work, and then to implemented it. Some things to consider:
- What does the method and arguments look like?
- Does it work with all TLS backends?
Looking more closely at curl's implementation, I see it supports both file paths and hash strings. For the first version of the implementation, I'd focus on supporting only the hash string format (not PEM/DER files):
.pin_public_key(hash: &str)
let hash1 = "7ad9c14e9ab5eb5c52c9e5bf9d7e0b839194e4b564e0bd6744b727ab71ec84ef";
let hash2 = "498fd358cb0297e0dab6da9b2dab310534f64bee164fac22030199dba75fae51";
let client = reqwest::Client::builder()
.pin_public_key(hash1)
.pin_public_key(hash2)
.build()?;
The hash format would be:
- Raw hex-encoded SHA256 hash of the public key (64 characters)
- No "sha256//" prefix, no base64 encoding, no semicolon separators, just repeated calls if more hashes need to be allowed
- Hash format validation happens at build time, returning an error for invalid input
The behaviour would be:
- Normal validation still occurs, pinning would be an additional security check on top of the standard validation
- If at least one pin is specified, the connection fails unless the server's public key matches at least one of the pinned hashes
I would love to help implement this but my rust level is very beginner.
Hello, I made a quick proof of concept with a custom verifier here : https://github.com/nicolaspernoud/certificate-pinning-poc-rs ; The API proposed by @lucacastelnuovo could be easier... but what if we want to add extra CAs as well, etc.