pyopenssl
pyopenssl copied to clipboard
No X509_STORE_set_purpose() support
Currently PKCS#7 verification is a bit hampered by the lack of X509_STORE_set_purpose() support; as it is becoming increasingly common (JWT, COSE, etc) to sign using CMS - yet run afoul of the default purpose check of open SSL if one wants to properly pin the chain.
Attached is an example (taking from the Covid Vaccination Certificates used in Europe):
- payload/signature and ca.pem contain the input data
- test.sh shows the equivalent in openssl; failing and succeeding.
- test.py shows how this now fails - as the purpose cannot be set.
And because X509_get0_signer is also not available - a NOVERIFY check is also not an option - as you cannot check the root against the signers used post the signature verification.
Below a script that exploits this lack of an interface indirectly (people are forced to set NOVERIFY to make this common case work). Also useful to create a testcase for above with.
#!/bin/sh
#
cat >openssl.cnf <<EOM
[ v3_server ]
basicConstraints=CA:FALSE
nsCertType = server
subjectAltName=DNS:www.webserver.com;DNS:sub.webserver.com;IP:1..3.4
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true
EOM
CNF=${CNF:-openssl.cnf}
openssl req -new -x509 -nodes -subj /CN=ca -out ca.pem -keyout ca.key -extensions v3_ca -extfile "$CNF"
openssl req -new -nodes -subj /CN=sub1 -keyout sub1.key |\
openssl x509 -req -CAkey ca.key -CA ca.pem -set_serial 1 -extensions v3_ca -extfile "$CNF" > sub1.pem
openssl req -new -nodes -subj /CN=sub2 -keyout sub2.key |\
openssl x509 -extfile "$CNF" -req -CAkey sub1.key -CA sub1.pem -set_serial 1 -extensions v3_ca > sub2.pem
openssl req -new -nodes -subj /CN=www.webserver.com -keyout leaf.key |\
openssl x509 -extfile "$CNF" -req -CAkey sub2.key -CA sub2.pem -set_serial 1 -extensions v3_server > leaf.pem
openssl req -new -nodes -subj /CN=sub1 -keyout sub1.key |\
cat sub1.pem sub2.pem > chain.pem
openssl verify -CAfile ca.pem -untrusted chain.pem leaf.pem
echo payload $$ > payload.bin
cat payload.bin | openssl cms -sign -signer leaf.pem -inkey leaf.key -outform DER -certfile chain.pem -out signature.p7
openssl cms -verify -CAfile ca.pem -inform DER -in signature.p7 -content payload.bin|| echo Rightly fails - as purpose wrong
openssl cms -verify -CAfile ca.pem -inform DER -in signature.p7 -content payload.bin -purpose any
# Now these are the dangerous ones - as they believe what is in the p7 - so it can be completely fake.
#
openssl cms -verify -inform DER -in signature.p7 -content payload.bin -noverify -purpose any
openssl cms -verify -inform DER -in signature.p7 -content payload.bin -noverify || echo Rightly fails - as purpose wrong
# So alice makes use of exactly that
openssl req -new -x509 -nodes -subj /CN=Alice -out alice.pem -keyout alice.key
echo Evil payload $$ ACCEPTED > evil-payload.bin
cat evil-payload.bin | openssl cms -sign -signer alice.pem -inkey alice.key -outform DER -out evil-signature.p7
openssl cms -verify -inform DER -in evil-signature.p7 -content evil-payload.bin -purpose any || echo good - alice was foiled
echo
openssl cms -verify -inform DER -in evil-signature.p7 -content evil-payload.bin -noverify -purpose any && echo VERY BAD - Alice got in
This looks like we should have a convesration about a PKCS7 verification API on the cryptography side. In general most active development occurs with cryptography and over the years we've found that adding bindings that cryptography and pyOpenSSL don't directly consume is very problematic, so we'd strongly prefer to design and implement an API for this sort of thing such that we can properly test and ensure we don't break it 😄
Coincidentally (well not quite - this was driven by the international COVID-19 response) - there is a fair bit of that ‘native’ being created at https://github.com/panzi/verify-ehc.
I could imagine this this could be wired up to a X509 trust validator that can handle meshes/cross signing - and you’d be fairly close to most of CMS.
Dw.
Given that PKCS#7 has been deprecated and removed from PyOpenSSL, and that an alternative for using this API is available in cryptography, I think this one could be closed
cc @mhils