You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kg...@apache.org on 2016/01/18 20:07:25 UTC

qpid-proton git commit: PROTON-1088 - Added two functions pn_ssl_get_cert_fingerprint() and pn_ssl_get_remote_subject_subfield() to obtain certificate fingerprint and certificate subject subfields respectively

Repository: qpid-proton
Updated Branches:
  refs/heads/master ba108cf30 -> 463cfd7ac


PROTON-1088 - Added two functions pn_ssl_get_cert_fingerprint() and pn_ssl_get_remote_subject_subfield() to obtain certificate fingerprint and certificate subject subfields respectively


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/463cfd7a
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/463cfd7a
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/463cfd7a

Branch: refs/heads/master
Commit: 463cfd7ac5b531edb10f9f022bb18228fe21b64b
Parents: ba108cf
Author: ganeshmurthy <gm...@redhat.com>
Authored: Thu Jan 14 09:50:16 2016 -0500
Committer: ganeshmurthy <gm...@redhat.com>
Committed: Mon Jan 18 10:30:42 2016 -0500

----------------------------------------------------------------------
 proton-c/bindings/python/cproton.i              |   7 ++
 proton-c/bindings/python/proton/__init__.py     |  67 ++++++++++
 proton-c/include/proton/ssl.h                   |  55 +++++++++
 proton-c/src/ssl/openssl.c                      | 122 +++++++++++++++++++
 proton-j/src/main/resources/cssl.py             |  12 ++
 tests/python/proton_tests/ssl.py                |  60 ++++++++-
 tests/python/proton_tests/ssl_db/README.txt     |   7 ++
 .../proton_tests/ssl_db/client-certificate1.p12 | Bin 0 -> 1612 bytes
 .../proton_tests/ssl_db/client-certificate1.pem |  16 +++
 .../proton_tests/ssl_db/client-private-key1.pem |  30 +++++
 .../proton_tests/ssl_db/client-request1.pem     |  14 +++
 tests/python/proton_tests/ssl_db/client.pkcs12  | Bin 1540 -> 2975 bytes
 12 files changed, 389 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/proton-c/bindings/python/cproton.i
----------------------------------------------------------------------
diff --git a/proton-c/bindings/python/cproton.i b/proton-c/bindings/python/cproton.i
index 1485801..734069f 100644
--- a/proton-c/bindings/python/cproton.i
+++ b/proton-c/bindings/python/cproton.i
@@ -233,6 +233,12 @@ bool pn_ssl_get_cipher_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE)
 bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE);
 %ignore pn_ssl_get_protocol_name;
 
+char* pn_ssl_get_remote_subject_subfield(pn_ssl_t *ssl, pn_ssl_cert_subject_subfield field);
+%ignore pn_ssl_get_remote_subject_subfield;
+
+int pn_ssl_get_cert_fingerprint(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZE, pn_ssl_hash_alg hash_alg);
+%ignore pn_ssl_get_cert_fingerprint;
+
 %rename(pn_ssl_get_peer_hostname) wrap_pn_ssl_get_peer_hostname;
 %inline %{
   int wrap_pn_ssl_get_peer_hostname(pn_ssl_t *ssl, char *VTEXT_OUT, size_t *VTEXT_SIZE) {
@@ -243,6 +249,7 @@ bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *OUTPUT, size_t MAX_OUTPUT_SIZ
 %}
 %ignore pn_ssl_get_peer_hostname;
 
+
 %immutable PN_PYREF;
 %inline %{
   extern const pn_class_t *PN_PYREF;

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/proton-c/bindings/python/proton/__init__.py
----------------------------------------------------------------------
diff --git a/proton-c/bindings/python/proton/__init__.py b/proton-c/bindings/python/proton/__init__.py
index 5720e1c..9dfd9a6 100644
--- a/proton-c/bindings/python/proton/__init__.py
+++ b/proton-c/bindings/python/proton/__init__.py
@@ -3627,6 +3627,73 @@ class SSL(object):
       return name
     return None
 
+  SHA1 = PN_SSL_SHA1
+  SHA256 = PN_SSL_SHA256
+  SHA512 = PN_SSL_SHA512
+  MD5 = PN_SSL_MD5
+
+  CERT_COUNTRY_NAME = PN_SSL_CERT_SUBJECT_COUNTRY_NAME
+  CERT_STATE_OR_PROVINCE = PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE
+  CERT_CITY_OR_LOCALITY = PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY
+  CERT_ORGANIZATION_NAME = PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME
+  CERT_ORGANIZATION_UNIT = PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT
+  CERT_COMMON_NAME = PN_SSL_CERT_SUBJECT_COMMON_NAME
+
+  def get_cert_subject_subfield(self, subfield_name):
+    subfield_value = pn_ssl_get_remote_subject_subfield(self._ssl, subfield_name)
+    return subfield_value
+
+  def get_cert_subject(self):
+    subject = pn_ssl_get_remote_subject(self._ssl)
+    return subject
+
+  def _get_cert_subject_unknown_subfield(self):
+    # Pass in an unhandled enum
+    return self.get_cert_subject_subfield(10)
+
+  # Convenience functions for obtaining the subfields of the subject field.
+  def get_cert_common_name(self):
+    return self.get_cert_subject_subfield(SSL.CERT_COMMON_NAME)
+
+  def get_cert_organization(self):
+    return self.get_cert_subject_subfield(SSL.CERT_ORGANIZATION_NAME)
+
+  def get_cert_organization_unit(self):
+    return self.get_cert_subject_subfield(SSL.CERT_ORGANIZATION_UNIT)
+
+  def get_cert_locality_or_city(self):
+    return self.get_cert_subject_subfield(SSL.CERT_CITY_OR_LOCALITY)
+
+  def get_cert_country(self):
+    return self.get_cert_subject_subfield(SSL.CERT_COUNTRY_NAME)
+
+  def get_cert_state_or_province(self):
+    return self.get_cert_subject_subfield(SSL.CERT_STATE_OR_PROVINCE)
+
+  def get_cert_fingerprint(self, fingerprint_length, digest_name):
+    rc, fingerprint_str = pn_ssl_get_cert_fingerprint(self._ssl, fingerprint_length, digest_name)
+    if rc == PN_OK:
+      return fingerprint_str
+    return None
+
+  # Convenience functions for obtaining fingerprint for specific hashing algorithms
+  def _get_cert_fingerprint_unknown_hash_alg(self):
+    return self.get_cert_fingerprint(41, 10)
+
+  def get_cert_fingerprint_sha1(self):
+    return self.get_cert_fingerprint(41, SSL.SHA1)
+
+  def get_cert_fingerprint_sha256(self):
+    # sha256 produces a fingerprint that is 64 characters long
+    return self.get_cert_fingerprint(65, SSL.SHA256)
+
+  def get_cert_fingerprint_sha512(self):
+    # sha512 produces a fingerprint that is 128 characters long
+    return self.get_cert_fingerprint(129, SSL.SHA512)
+
+  def get_cert_fingerprint_md5(self):
+    return self.get_cert_fingerprint(33, SSL.MD5)
+
   @property
   def remote_subject(self):
     return pn_ssl_get_remote_subject( self._ssl )

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/proton-c/include/proton/ssl.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/ssl.h b/proton-c/include/proton/ssl.h
index d8d4d1f..6c22014 100644
--- a/proton-c/include/proton/ssl.h
+++ b/proton-c/include/proton/ssl.h
@@ -337,6 +337,61 @@ PN_EXTERN int pn_ssl_get_peer_hostname( pn_ssl_t *ssl, char *hostname, size_t *b
  */
 PN_EXTERN const char* pn_ssl_get_remote_subject(pn_ssl_t *ssl);
 
+/**
+ * Enumeration identifying the sub fields of the subject field in the ssl certificate.
+ */
+typedef enum {
+  PN_SSL_CERT_SUBJECT_COUNTRY_NAME,
+  PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE,
+  PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY,
+  PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME,
+  PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT,
+  PN_SSL_CERT_SUBJECT_COMMON_NAME
+} pn_ssl_cert_subject_subfield;
+
+
+/**
+ * Enumeration identifying hashing algorithm.
+ */
+typedef enum {
+  PN_SSL_SHA1,   /* Produces hash that is 20 bytes long */
+  PN_SSL_SHA256, /* Produces hash that is 32 bytes long */
+  PN_SSL_SHA512, /* Produces hash that is 64 bytes long */
+  PN_SSL_MD5     /* Produces hash that is 16 bytes long */
+} pn_ssl_hash_alg;
+
+/**
+ * Get the fingerprint of the certificate. The certificate fingerprint (as displayed in the Fingerprints section when
+ * looking at a certificate with say the Firefox browser) is the hexadecimal hash of the entire certificate.
+ * The fingerprint is not part of the certificate, rather it is computed from the certificate and can be used to uniquely identify a certificate.
+ * @param[in] ssl the ssl client/server to query
+ * @param[in] fingerprint char pointer. The certificate fingerprint (in hex format) will be populated in this array.
+ *            If sha1 is the digest name, the fingerprint is 41 characters long (40 + 1 '\0' character), 65 characters long for
+ *            sha256 and 129 characters long for sha512 and 33 characters for md5.
+ * @param[in] fingerprint_length - Must be at >= 33 for md5, >= 41 for sha1, >= 65 for sha256 and >=129 for sha512.
+ * @param[in] the hash algorithm to use. Must be of type pn_ssl_hash_alg (currently supports sha1, sha256, sha512 and md5)
+ * @return error code - Returns 0 on success. Return a value less than zero if there were any errors. Upon execution of this function,
+ *                      char *fingerprint will contain the appropriate null terminated hex fingerprint
+ */
+PN_EXTERN int pn_ssl_get_cert_fingerprint(pn_ssl_t *ssl0,
+                                          char *fingerprint,
+                                          size_t fingerprint_length,
+                                          pn_ssl_hash_alg hash_alg);
+
+/**
+ * Returns a char pointer that contains the value of the sub field of the subject field in the ssl certificate. The subject field usually contains the following sub fields -
+ * C = ISO3166 two character country code
+ * ST = state or province
+ * L = Locality; generally means city
+ * O = Organization - Company Name
+ * OU = Organization Unit - division or unit
+ * CN = CommonName
+ * @param[in] ssl the ssl client/server to query
+ * @param[in] The enumeration pn_ssl_cert_subject_subfield representing the required sub field.
+ * @return A null terminated string which contains the requested sub field value which is valid until the ssl object is destroyed.
+ */
+PN_EXTERN const char* pn_ssl_get_remote_subject_subfield(pn_ssl_t *ssl0, pn_ssl_cert_subject_subfield field);
+
 /** @} */
 
 #ifdef __cplusplus

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/proton-c/src/ssl/openssl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/ssl/openssl.c b/proton-c/src/ssl/openssl.c
index 8710225..923c0dc 100644
--- a/proton-c/src/ssl/openssl.c
+++ b/proton-c/src/ssl/openssl.c
@@ -1294,6 +1294,128 @@ const char* pn_ssl_get_remote_subject(pn_ssl_t *ssl0)
   return ssl->subject;
 }
 
+int pn_ssl_get_cert_fingerprint(pn_ssl_t *ssl0, char *fingerprint, size_t fingerprint_length, pn_ssl_hash_alg hash_alg)
+{
+    const char *digest_name = NULL;
+    size_t min_required_length;
+
+    // old versions of python expect fingerprint to contain a valid string on
+    // return from this function
+    fingerprint[0] = 0;
+
+    // Assign the correct digest_name value based on the enum values.
+    switch (hash_alg) {
+        case PN_SSL_SHA1 :
+            min_required_length = 41; // 40 hex characters + 1 '\0' character
+            digest_name = "sha1";
+            break;
+        case PN_SSL_SHA256 :
+            min_required_length = 65; // 64 hex characters + 1 '\0' character
+            digest_name = "sha256";
+            break;
+        case PN_SSL_SHA512 :
+            min_required_length = 129; // 128 hex characters + 1 '\0' character
+            digest_name = "sha512";
+            break;
+        case PN_SSL_MD5 :
+            min_required_length = 33; // 32 hex characters + 1 '\0' character
+            digest_name = "md5";
+            break;
+        default:
+            ssl_log_error("Unknown or unhandled hash algorithm %i \n", hash_alg);
+            return PN_ERR;
+
+    }
+
+    if(fingerprint_length < min_required_length) {
+        ssl_log_error("Insufficient fingerprint_length %i. fingerprint_length must be %i or above for %s digest\n",
+            fingerprint_length, min_required_length, digest_name);
+        return PN_ERR;
+    }
+
+    const EVP_MD  *digest = EVP_get_digestbyname(digest_name);
+
+    pni_ssl_t *ssl = get_ssl_internal(ssl0);
+
+    X509 *cert = SSL_get_peer_certificate(ssl->ssl);
+
+    if(cert) {
+        unsigned int len;
+
+        unsigned char bytes[64]; // sha512 uses 64 octets, we will use that as the maximum.
+
+        if (X509_digest(cert, digest, bytes, &len) != 1) {
+            ssl_log_error("Failed to extract X509 digest\n");
+            return PN_ERR;
+       }
+
+        char *cursor = fingerprint;
+
+        for (size_t i=0; i<len ; i++) {
+            cursor +=  snprintf((char *)cursor, fingerprint_length, "%02x", bytes[i]);
+            fingerprint_length = fingerprint_length - 2;
+        }
+
+        return PN_OK;
+    }
+    else {
+        ssl_log_error("No certificate is available yet \n");
+        return PN_ERR;
+    }
+
+    return 0;
+}
+
+
+const char* pn_ssl_get_remote_subject_subfield(pn_ssl_t *ssl0, pn_ssl_cert_subject_subfield field)
+{
+    int openssl_field = 0;
+
+    // Assign openssl internal representations of field values to openssl_field
+    switch (field) {
+        case PN_SSL_CERT_SUBJECT_COUNTRY_NAME :
+            openssl_field = NID_countryName;
+            break;
+        case PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE :
+            openssl_field = NID_stateOrProvinceName;
+            break;
+        case PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY :
+            openssl_field = NID_localityName;
+            break;
+        case PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME :
+            openssl_field = NID_organizationName;
+            break;
+        case PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT :
+            openssl_field = NID_organizationalUnitName;
+            break;
+        case PN_SSL_CERT_SUBJECT_COMMON_NAME :
+            openssl_field = NID_commonName;
+            break;
+        default:
+            ssl_log_error("Unknown or unhandled certificate subject subfield %i \n", field);
+            return NULL;
+    }
+
+    pni_ssl_t *ssl = get_ssl_internal(ssl0);
+    X509 *cert = SSL_get_peer_certificate(ssl->ssl);
+
+    X509_NAME *subject_name = X509_get_subject_name(cert);
+
+    // TODO (gmurthy) - A server side cert subject field can have more than one common name like this - Subject: CN=www.domain1.com, CN=www.domain2.com, see https://bugzilla.mozilla.org/show_bug.cgi?id=380656
+    // For now, we will only return the first common name if there is more than one common name in the cert
+    int index = X509_NAME_get_index_by_NID(subject_name, openssl_field, -1);
+
+    if (index > -1) {
+        X509_NAME_ENTRY *ne = X509_NAME_get_entry(subject_name, index);
+        if(ne) {
+            ASN1_STRING *name_asn1 = X509_NAME_ENTRY_get_data(ne);
+            return (char *) name_asn1->data;
+        }
+    }
+
+    return NULL;
+}
+
 static ssize_t process_input_done(pn_transport_t *transport, unsigned int layer, const char *input_data, size_t len)
 {
   return PN_EOS;

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/proton-j/src/main/resources/cssl.py
----------------------------------------------------------------------
diff --git a/proton-j/src/main/resources/cssl.py b/proton-j/src/main/resources/cssl.py
index 74497ef..d389984 100644
--- a/proton-j/src/main/resources/cssl.py
+++ b/proton-j/src/main/resources/cssl.py
@@ -34,6 +34,18 @@ PN_SSL_VERIFY_PEER=1
 PN_SSL_ANONYMOUS_PEER=2
 PN_SSL_VERIFY_PEER_NAME=3
 
+PN_SSL_SHA1=0
+PN_SSL_SHA256=1
+PN_SSL_SHA512=2
+PN_SSL_MD5=3
+
+PN_SSL_CERT_SUBJECT_COUNTRY_NAME=0
+PN_SSL_CERT_SUBJECT_STATE_OR_PROVINCE=1
+PN_SSL_CERT_SUBJECT_CITY_OR_LOCALITY=2
+PN_SSL_CERT_SUBJECT_ORGANIZATION_NAME=3
+PN_SSL_CERT_SUBJECT_ORGANIZATION_UNIT=4
+PN_SSL_CERT_SUBJECT_COMMON_NAME=5
+
 PN_SSL_MODE_J2P = {
   SslDomain.Mode.CLIENT: PN_SSL_MODE_CLIENT,
   SslDomain.Mode.SERVER: PN_SSL_MODE_SERVER

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/tests/python/proton_tests/ssl.py
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/ssl.py b/tests/python/proton_tests/ssl.py
index b367340..54f5074 100644
--- a/tests/python/proton_tests/ssl.py
+++ b/tests/python/proton_tests/ssl.py
@@ -23,7 +23,7 @@ from . import common
 import random
 import string
 import subprocess
-
+import sys
 from proton import *
 from .common import Skipped, pump
 
@@ -185,6 +185,63 @@ class SslTest(common.Test):
         server.connection.close()
         self._pump( client, server )
 
+    def test_certificate_fingerprint_and_subfields(self):
+        if "java" in sys.platform:
+            raise Skipped("Not yet implemented in Proton-J")
+
+        self.server_domain.set_credentials(self._testpath("server-certificate.pem"),
+                                           self._testpath("server-private-key.pem"),
+                                           "server-password")
+        self.server_domain.set_trusted_ca_db(self._testpath("ca-certificate.pem"))
+        self.server_domain.set_peer_authentication( SSLDomain.VERIFY_PEER,
+                                                    self._testpath("ca-certificate.pem") )
+        server = SslTest.SslTestConnection( self.server_domain, mode=Transport.SERVER )
+
+        # give the client a certificate, but let's not require server authentication
+        self.client_domain.set_credentials(self._testpath("client-certificate1.pem"),
+                                           self._testpath("client-private-key1.pem"),
+                                           "client-password")
+        self.client_domain.set_peer_authentication( SSLDomain.ANONYMOUS_PEER )
+        client = SslTest.SslTestConnection( self.client_domain )
+
+        client.connection.open()
+        server.connection.open()
+        self._pump( client, server )
+
+        # Test the subject subfields
+        self.assertEqual("Client", server.ssl.get_cert_organization())
+        self.assertEqual("Dev", server.ssl.get_cert_organization_unit())
+        self.assertEqual("ST", server.ssl.get_cert_state_or_province())
+        self.assertEqual("US", server.ssl.get_cert_country())
+        self.assertEqual("City", server.ssl.get_cert_locality_or_city())
+        self.assertEqual("O=Server,CN=A1.Good.Server.domain.com", client.ssl.get_cert_subject())
+        self.assertEqual("O=Client,CN=127.0.0.1,C=US,ST=ST,L=City,OU=Dev", server.ssl.get_cert_subject())
+
+        self.assertEqual("03c97341abafe5861d6969c66a190d2846d905d6", server.ssl.get_cert_fingerprint_sha1())
+        self.assertEqual("ad86cc76278e69aef3a5c0dda13fc49831622f92d1364ce1ed361ff842b0143a", server.ssl.get_cert_fingerprint_sha256())
+        self.assertEqual("9fb3340ecee4471534d60be025358cae33ef2cc9442ca8bb7703a68db68d9ffb7963678292996011fa55a9a2524b84a40a11a2778f25797e78e23cf05623218d",
+                         server.ssl.get_cert_fingerprint_sha512())
+        self.assertEqual("0d4faa84a0bb6846eaec6b7493916b30", server.ssl.get_cert_fingerprint_md5())
+
+        # Test the various fingerprint algorithms
+        self.assertEqual("f390ddb4dc8a90bcf3528774b622a7f87f7322b5", client.ssl.get_cert_fingerprint_sha1())
+        self.assertEqual("c116e902345c99bc01dda14b7a5f1b8ae6a451eddb23e5336c996ba4d12bc122", client.ssl.get_cert_fingerprint_sha256())
+        self.assertEqual("8941c8ce00824ab7196bb1952787c90ef7f9bd837cbb0bb4823f57fc89e80033c75adc98b78801928d0035bcd6db6ddc9ab6da026c6548a66ede5c4f43f7e166",
+                         client.ssl.get_cert_fingerprint_sha512())
+        self.assertEqual("d008bf05afbc983a3f98ae56e3eba643", client.ssl.get_cert_fingerprint_md5())
+
+        self.assertEqual(None, client.ssl.get_cert_fingerprint(21, SSL.SHA1)) # Should be at least 41
+        self.assertEqual(None, client.ssl.get_cert_fingerprint(50, SSL.SHA256)) # Should be at least 65
+        self.assertEqual(None, client.ssl.get_cert_fingerprint(128, SSL.SHA512)) # Should be at least 129
+        self.assertEqual(None, client.ssl.get_cert_fingerprint(10, SSL.MD5)) # Should be at least 33
+        self.assertEqual(None, client.ssl._get_cert_subject_unknown_subfield())
+
+        self.assertNotEqual(None, client.ssl.get_cert_fingerprint(50, SSL.SHA1)) # Should be at least 41
+        self.assertNotEqual(None, client.ssl.get_cert_fingerprint(70, SSL.SHA256)) # Should be at least 65
+        self.assertNotEqual(None, client.ssl.get_cert_fingerprint(130, SSL.SHA512)) # Should be at least 129
+        self.assertNotEqual(None, client.ssl.get_cert_fingerprint(35, SSL.MD5)) # Should be at least 33
+        self.assertEqual(None, client.ssl._get_cert_fingerprint_unknown_hash_alg())
+
     def test_client_authentication(self):
         """ Force the client to authenticate.
         """
@@ -208,6 +265,7 @@ class SslTest(common.Test):
         client.connection.open()
         server.connection.open()
         self._pump( client, server )
+
         assert client.ssl.protocol_name() is not None
         client.connection.close()
         server.connection.close()

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/tests/python/proton_tests/ssl_db/README.txt
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/ssl_db/README.txt b/tests/python/proton_tests/ssl_db/README.txt
index 5b35421..f2ce482 100644
--- a/tests/python/proton_tests/ssl_db/README.txt
+++ b/tests/python/proton_tests/ssl_db/README.txt
@@ -48,6 +48,12 @@ keytool -storetype pkcs12 -keystore client.pkcs12 -storepass client-password -al
 keytool -storetype pkcs12 -keystore ca.pkcs12 -storepass ca-password -alias ca -keypass ca-password -gencert -rfc -validity 99999 -infile client-request.pem -outfile client-certificate.pem
 openssl pkcs12 -nocerts -passin pass:client-password -in client.pkcs12 -passout pass:client-password -out client-private-key.pem
 
+# Create another client certificate with a different subject line
+keytool -storetype pkcs12 -keystore client.pkcs12 -storepass client-password -alias client-certificate1 -keypass client-password -genkey  -dname "O=Client,CN=127.0.0.1,C=US,ST=ST,L=City,OU=Dev" -validity 99999
+keytool -storetype pkcs12 -keystore client.pkcs12 -storepass client-password -alias client-certificate1 -keypass client-password -certreq -file client-request1.pem
+keytool -storetype pkcs12 -keystore ca.pkcs12 -storepass ca-password -alias ca -keypass ca-password -gencert -rfc -validity 99999 -infile client-request1.pem -outfile client-certificate1.pem
+openssl pkcs12 -nocerts -passin pass:client-password -in client.pkcs12 -passout pass:client-password -out client-private-key1.pem
+
 # Create a "bad" certificate - not signed by a trusted authority
 keytool -storetype pkcs12 -keystore bad-server.pkcs12 -storepass server-password -alias bad-server -keypass server-password -genkey -dname "O=Not Trusted Inc,CN=127.0.0.1" -validity 99999
 openssl pkcs12 -nocerts -passin pass:server-password -in bad-server.pkcs12 -passout pass:server-password -out bad-server-private-key.pem
@@ -68,5 +74,6 @@ openssl pkcs12 -nocerts -passin pass:server-password -in server.pkcs12 -passout
 openssl pkcs12 -export -out ca-certificate.p12 -in ca-certificate.pem -name ca-certificate -nokeys -passout pass:
 openssl pkcs12 -export -out server-certificate.p12 -passin pass:server-password -passout pass:server-password -inkey server-private-key.pem -in server-certificate.pem -name server-certificate
 openssl pkcs12 -export -out client-certificate.p12 -passin pass:client-password -passout pass:client-password -inkey client-private-key.pem -in client-certificate.pem -name client-certificate
+openssl pkcs12 -export -out client-certificate1.p12 -passin pass:client-password -passout pass:client-password -inkey client-private-key1.pem -in client-certificate1.pem -name client-certificate1
 openssl pkcs12 -export -out bad-server-certificate.p12 -passin pass:server-password -passout pass:server-password -inkey bad-server-private-key.pem -in bad-server-certificate.pem -name bad-server
 openssl pkcs12 -export -out server-wc-certificate.p12 -passin pass:server-password -passout pass:server-password -inkey server-wc-private-key.pem -in server-wc-certificate.pem -name server-wc-certificate

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/tests/python/proton_tests/ssl_db/client-certificate1.p12
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/ssl_db/client-certificate1.p12 b/tests/python/proton_tests/ssl_db/client-certificate1.p12
new file mode 100644
index 0000000..bb5c567
Binary files /dev/null and b/tests/python/proton_tests/ssl_db/client-certificate1.p12 differ

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/tests/python/proton_tests/ssl_db/client-certificate1.pem
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/ssl_db/client-certificate1.pem b/tests/python/proton_tests/ssl_db/client-certificate1.pem
new file mode 100644
index 0000000..9b136ac
--- /dev/null
+++ b/tests/python/proton_tests/ssl_db/client-certificate1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIIDDDCCAsqgAwIBAgIEGzO0QTALBgcqhkjOOAQDBQAwMTEXMBUGA1UEAxMOVHJ1c3RlZC5DQS5j
+b20xFjAUBgNVBAoTDVRydXN0IE1lIEluYy4wIBcNMTYwMTEzMjIxOTE4WhgPMjI4OTEwMjcyMjE5
+MThaMFwxDDAKBgNVBAsTA0RldjENMAsGA1UEBxMEQ2l0eTELMAkGA1UECBMCU1QxCzAJBgNVBAYT
+AlVTMRIwEAYDVQQDEwkxMjcuMC4wLjExDzANBgNVBAoTBkNsaWVudDCCAbcwggEsBgcqhkjOOAQB
+MIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2
+y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQT
+WhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3e
+y7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8
+FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQB
+TDv+z0kqA4GEAAKBgAsLC8PBAEzv2Vl4Ji1k3mVI49YzWsz6Yh/HPCjy1s9aueNRM0ZlhQWv3TIw
+gOcbIoUp10y4Rc4fsVIrjpoC4tmZCZmkimnoa+Lp0whDUlwWrEFZ971NqkQsmagfpuXVYeE4hB1Y
+OoETBpybd+liDaikymLWFqwd9XmuQlysEhWCo0IwQDAfBgNVHSMEGDAWgBSrEL6O+KB+mJHozd9v
+pemmAbEUljAdBgNVHQ4EFgQUBlPIzSsmgK6IwNtZitN/NrQEztMwCwYHKoZIzjgEAwUAAy8AMCwC
+FGxlMH0zwjoCJMfntSRAwxM+4JK7AhR6Y/CgFBrJCgDFAYkFH8ucHoii6w==
+-----END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/tests/python/proton_tests/ssl_db/client-private-key1.pem
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/ssl_db/client-private-key1.pem b/tests/python/proton_tests/ssl_db/client-private-key1.pem
new file mode 100644
index 0000000..b847a30
--- /dev/null
+++ b/tests/python/proton_tests/ssl_db/client-private-key1.pem
@@ -0,0 +1,30 @@
+Bag Attributes
+    friendlyName: client-certificate1
+    localKeyID: 54 69 6D 65 20 31 34 35 32 37 32 33 35 33 33 32 36 32 
+Key Attributes: <No Attributes>
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIBnjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIY3SXzWdZ9g8CAggA
+MBQGCCqGSIb3DQMHBAgNjO/Glke9hgSCAVgtzKi1oXuMXuv2D7H2Mn4OQIevpn7S
+WMvJmFSNiNvIAmKhJjPyaPKYYgCMALYi+mvFWfv/EaQuiovX45GTMrCFM4MdUuo0
+YCTEOB8ehrHEoyy76U61wN+EfdEYlS3/KfnbdnhHD342jdV+yO5T5pgpacu+5Inz
+IzvcBrGFehqTI9fq3Zkl1dOEpfrfahrO/8rq+UZQzqjIaiWi6irSFihQNsvVKUkd
+TLUDmSX591+dUblMlH8g/V3pY9EAEjdxGuY2mEqEe/B2WH74s3xyOb0UljkC6AmE
+MYGGXR8XK1RYrKQp3SwUMlisz72JhrfzvUlYEMRuclNKNNjyEPBO85iXLPFmSNQ5
+4H/N/P3lO6WXny1Ea46kc5+ulC2CQkvetZhWcOtHvf4WRVt6wx2xQDhZwcUM7zDk
+uUV8pFqT1iUmx8k4Zasp1hNi0zSLnf3mKVPF3EFDTbU8qQ==
+-----END ENCRYPTED PRIVATE KEY-----
+Bag Attributes
+    friendlyName: client-certificate
+    localKeyID: 54 69 6D 65 20 31 33 36 33 37 39 34 34 32 33 34 36 33 
+Key Attributes: <No Attributes>
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIBljBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIFcxTKG24KKICAggA
+MBQGCCqGSIb3DQMHBAh/2ViSC9U9qQSCAVCM9V+dw+CnGKguFH5LYpCtoepRaDvC
+yTsGPmmJOh+wDObU8U+4kEYdGLCJgqblXWJEt9uAVOwg3EK30mSCT33N/VIbSbgv
+LadI7WjqtMKU5xJGscb71uGUdmiJrXLtp/1TnwNZM48QGpzuj+Eegn7j3BRX7bSS
+EwrUxqSm67ahi1/R2driAuZQxdvotBQNUq7ADZQVAISKMhblJs3Z7mX4EBxveS/f
+YIwDQ+ZfBfOnEhSNFxk6/zIxWqA0/3gFY2/M1uI3TvXuuOHags6+QuHHnpgrV7J+
+1Oaq5cAD1OlGd/fqerTxRdIUQMVixFVqMYZUEnqeiWOY/CxkynHh7MUT1/LaUN2d
+7QVvpVXHvTvG4BPlApxIVV4/LoeYd4+jEkr/w7sq0msuMRP1qu1aNnl4XRtrvFOY
+hDpaYO/Lp7tv9r9geUszmWvlqkHm2nPocJo=
+-----END ENCRYPTED PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/tests/python/proton_tests/ssl_db/client-request1.pem
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/ssl_db/client-request1.pem b/tests/python/proton_tests/ssl_db/client-request1.pem
new file mode 100644
index 0000000..2bafb09
--- /dev/null
+++ b/tests/python/proton_tests/ssl_db/client-request1.pem
@@ -0,0 +1,14 @@
+-----BEGIN NEW CERTIFICATE REQUEST-----
+MIICkDCCAk4CAQAwXDEMMAoGA1UECxMDRGV2MQ0wCwYDVQQHEwRDaXR5MQswCQYDVQQIEwJTVDEL
+MAkGA1UEBhMCVVMxEjAQBgNVBAMTCTEyNy4wLjAuMTEPMA0GA1UEChMGQ2xpZW50MIIBtzCCASwG
+ByqGSM44BAEwggEfAoGBAP1/U4EddRIpUt9KnC7s5Of2EbdSPO9EAMMeP4C2USZpRV1AIlH7WT2N
+WPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P2
+08UewwI1VBNaFpEy9nXzrith1yrv8iIDGZ3RSAHHAhUAl2BQjxUjC8yykrmCouuEC/BYHPUCgYEA
+9+GghdabPd7LvKtcNrhXuXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3
+zwkyjMim4TwWeotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKL
+Zl6Ae1UlZAFMO/7PSSoDgYQAAoGACwsLw8EATO/ZWXgmLWTeZUjj1jNazPpiH8c8KPLWz1q541Ez
+RmWFBa/dMjCA5xsihSnXTLhFzh+xUiuOmgLi2ZkJmaSKaehr4unTCENSXBasQVn3vU2qRCyZqB+m
+5dVh4TiEHVg6gRMGnJt36WINqKTKYtYWrB31ea5CXKwSFYKgMDAuBgkqhkiG9w0BCQ4xITAfMB0G
+A1UdDgQWBBQGU8jNKyaArojA21mK0382tATO0zALBgcqhkjOOAQDBQADLwAwLAIUIH29x1jT3akD
+Y1rLTr6o5z5c++cCFAG+rzzJYzmBAhwTQsKtoQPtVCwA
+-----END NEW CERTIFICATE REQUEST-----

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/463cfd7a/tests/python/proton_tests/ssl_db/client.pkcs12
----------------------------------------------------------------------
diff --git a/tests/python/proton_tests/ssl_db/client.pkcs12 b/tests/python/proton_tests/ssl_db/client.pkcs12
index a11300d..87d6690 100644
Binary files a/tests/python/proton_tests/ssl_db/client.pkcs12 and b/tests/python/proton_tests/ssl_db/client.pkcs12 differ


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org