Skip to content

check_privatekey does not throw an exception for mismatching keys anymore #178

@helgar

Description

@helgar

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()

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions