-
Notifications
You must be signed in to change notification settings - Fork 422
Description
Hi!
tldr: If you combine a certificate with a private key of a different certificate, "check_privatekey()" should detect the mismatch. In python-openssl version 0.14-1 it does not do that anymore; in 0.13-2 it did.
To reproduce this:
- Create two different certificates with private keys respectively.
- Combine the certificate part of the first with the private key of the second.
- Load them in a context: first the the key with use_privatekey(...) and then the certificate with use_certificate(...) and call check_privatekey() to verify if they match.
This should raise an exception, but in 0.14-1 it does not.
Note that this does not occur, if you first load the certificate with use_certificate(...) and then the key with use_privatekey(...). Then the exception is thrown already when use_privatekey is called (which is the expected behavior according to [1]. However, if you switch the two lines, no exception is thrown, neither when calling use_privatekey() nor when calling check_privatekey().
For a self-contained test script see below.
It writes out the two certificates and their respective private keys to cert1.pem and cert2.pem respectively and the combination of the certificate of cert1.pem with the private key of cert2.pem to the file frankencert.pem.
If you run the openssl commandline tool and check for the matching manually, the frankencert.pem clearly does not match. Compare:
openssl rsa -noout -modulus -in frankencert.pem
openssl x509 -noout -modulus -in frankencert.pem
I tested this with python-openssl 0.14-1 on debian jessie and with python-openssl 0.13-2+deb7u1 on debian wheezy.
[1] https://www.openssl.org/docs/ssl/SSL_CTX_use_certificate.html
Test script:
#!/usr/bin/python
import OpenSSL
def GenerateSelfSignedCert(common_name, validity, serial_no):
# Create private and public key
key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
# Create self-signed certificate
cert = OpenSSL.crypto.X509()
if common_name:
cert.get_subject().CN = common_name
cert.set_serial_number(serial_no)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(validity)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(key)
cert.sign(key, "SHA1")
return (cert, key)
def WriteCertFile(cert, key, filename):
key_pem = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
cert_pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
cert_fd = open(filename, "w")
cert_fd.write(cert_pem)
cert_fd.write(key_pem)
cert_fd.close()
def TestFrankenCert():
validity = 24*60*365
(cert1, key1) = GenerateSelfSignedCert("My Certificate No 1", validity, 12345)
(cert2, key2) = GenerateSelfSignedCert("My Certificate No 2", validity, 67890)
WriteCertFile(cert1, key1, "cert1.pem")
WriteCertFile(cert2, key2, "cert2.pem")
WriteCertFile(cert1, key2, "frankencert.pem")
try:
ctx = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
ctx.use_privatekey(key2)
ctx.use_certificate(cert1)
ctx.check_privatekey()
except OpenSSL.SSL.Error as e:
print "I correctly received the exception: %s" % e
return
print "I did not receive an exception, although I should have!"
if __name__ == "__main__":
TestFrankenCert()