Merge branch '1.x' of ssh://authdev.it.ohio-state.edu/~scantor/git/cpp-xmltooling...
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / SecurityHelper.cpp
index 6370079..e85306f 100644 (file)
@@ -1,17 +1,21 @@
-/*
- *  Copyright 2001-2009 Internet2
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
  */
 
 /**
 
 #include "internal.h"
 #include "logging.h"
+#include "io/HTTPResponse.h"
 #include "security/OpenSSLCryptoX509CRL.h"
 #include "security/SecurityHelper.h"
 #include "security/X509Credential.h"
+#include "soap/HTTPSOAPTransport.h"
 #include "util/NDC.h"
 
 #include <fstream>
+#include <openssl/evp.h>
 #include <openssl/pem.h>
 #include <openssl/pkcs12.h>
+#include <xsec/enc/XSECCryptoException.hpp>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
+#include <xercesc/util/Base64.hpp>
+
+#ifdef WIN32
+# if (OPENSSL_VERSION_NUMBER >= 0x00907000)
+#  define XMLTOOLING_OPENSSL_HAVE_EC 1
+# endif
+#endif
+
+#if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
+# include <xsec/enc/OpenSSL/OpenSSLCryptoKeyEC.hpp>
+#endif
 
 using namespace xmltooling::logging;
 using namespace xmltooling;
@@ -54,7 +73,7 @@ static int passwd_callback(char* buf, int len, int verify, void* passwd)
 
 const char* SecurityHelper::guessEncodingFormat(const char* pathname)
 {
-    const char* format=NULL;
+    const char* format=nullptr;
     BIO* in=BIO_new(BIO_s_file_internal());
     if (in && BIO_read_filename(in, pathname)>0) {
         const int READSIZE = 1;
@@ -85,7 +104,7 @@ const char* SecurityHelper::guessEncodingFormat(const char* pathname)
             // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
             // If it fails, must be another kind of DER-encoded structure.
             PKCS12* p12;
-            if ((p12=d2i_PKCS12_bio(in, NULL)) == NULL) {
+            if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
                 format = "DER";
             }
             else {
@@ -110,8 +129,8 @@ XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char*
     log.info("loading private key from file (%s)", pathname);
 
     // Native objects.
-    PKCS12* p12=NULL;
-    EVP_PKEY* pkey=NULL;
+    PKCS12* p12=nullptr;
+    EVP_PKEY* pkey=nullptr;
 
     BIO* in=BIO_new(BIO_s_file_internal());
     if (in && BIO_read_filename(in, pathname)>0) {
@@ -144,7 +163,7 @@ XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char*
             else {
                 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
                 // If it fails, must be another kind of DER-encoded structure.
-                if ((p12=d2i_PKCS12_bio(in, NULL)) == NULL) {
+                if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
                     format = "DER";
                     if (BIO_seek(in, mark) < 0) {
                         log_openssl();
@@ -161,17 +180,17 @@ XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char*
 
         // The format should be known, so parse accordingly.
         if (!strcmp(format, "PEM")) {
-            pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback, const_cast<char*>(password));
+            pkey = PEM_read_bio_PrivateKey(in, nullptr, passwd_callback, const_cast<char*>(password));
         }
         else if (!strcmp(format, "DER")) {
-            pkey=d2i_PrivateKey_bio(in, NULL);
+            pkey=d2i_PrivateKey_bio(in, nullptr);
         }
         else if (!strcmp(format, "PKCS12")) {
             if (!p12)
-                p12 = d2i_PKCS12_bio(in, NULL);
+                p12 = d2i_PKCS12_bio(in, nullptr);
             if (p12) {
-                X509* x=NULL;
-                PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, NULL);
+                X509* x=nullptr;
+                PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, nullptr);
                 PKCS12_free(p12);
                 X509_free(x);
             }
@@ -185,7 +204,7 @@ XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char*
 
     // Now map it to an XSEC wrapper.
     if (pkey) {
-        XSECCryptoKey* ret=NULL;
+        XSECCryptoKey* ret=nullptr;
         switch (pkey->type) {
             case EVP_PKEY_RSA:
                 ret=new OpenSSLCryptoKeyRSA(pkey);
@@ -195,6 +214,11 @@ XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char*
                 ret=new OpenSSLCryptoKeyDSA(pkey);
                 break;
 
+#if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
+            case EVP_PKEY_EC:
+                ret=new OpenSSLCryptoKeyEC(pkey);
+                break;
+#endif
             default:
                 log.error("unsupported private key type");
         }
@@ -220,8 +244,8 @@ vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromFile(
     vector<XSECCryptoX509*>::size_type count = certs.size();
 
     // Native objects.
-    X509* x=NULL;
-    PKCS12* p12=NULL;
+    X509* x=nullptr;
+    PKCS12* p12=nullptr;
 
     BIO* in=BIO_new(BIO_s_file_internal());
     if (in && BIO_read_filename(in, pathname)>0) {
@@ -254,7 +278,7 @@ vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromFile(
             else {
                 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
                 // If it fails, must be another kind of DER-encoded structure.
-                if ((p12=d2i_PKCS12_bio(in, NULL)) == NULL) {
+                if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
                     format = "DER";
                     if (BIO_seek(in, mark) < 0) {
                         log_openssl();
@@ -270,13 +294,13 @@ vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromFile(
 
         // The format should be known, so parse accordingly.
         if (!strcmp(format, "PEM")) {
-            while (x=PEM_read_bio_X509(in, NULL, NULL, NULL)) {
+            while (x=PEM_read_bio_X509(in, nullptr, nullptr, nullptr)) {
                 certs.push_back(new OpenSSLCryptoX509(x));
                 X509_free(x);
             }
         }
         else if (!strcmp(format, "DER")) {
-            x=d2i_X509_bio(in, NULL);
+            x=d2i_X509_bio(in, nullptr);
             if (x) {
                 certs.push_back(new OpenSSLCryptoX509(x));
                 X509_free(x);
@@ -284,9 +308,9 @@ vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromFile(
         }
         else if (!strcmp(format, "PKCS12")) {
             if (!p12)
-                p12 = d2i_PKCS12_bio(in, NULL);
+                p12 = d2i_PKCS12_bio(in, nullptr);
             if (p12) {
-                EVP_PKEY* pkey=NULL;
+                EVP_PKEY* pkey=nullptr;
                 STACK_OF(X509)* CAstack = sk_X509_new_null();
                 PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, &CAstack);
                 PKCS12_free(p12);
@@ -362,15 +386,15 @@ vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromFile(
             log.debug("CRL encoding format for (%s) dynamically resolved as (%s)", pathname, format);
         }
 
-        X509_CRL* crl=NULL;
+        X509_CRL* crl=nullptr;
         if (!strcmp(format, "PEM")) {
-            while (crl=PEM_read_bio_X509_CRL(in, NULL, NULL, NULL)) {
+            while (crl=PEM_read_bio_X509_CRL(in, nullptr, nullptr, nullptr)) {
                 crls.push_back(new OpenSSLCryptoX509CRL(crl));
                 X509_CRL_free(crl);
             }
         }
         else if (!strcmp(format, "DER")) {
-            crl=d2i_X509_CRL_bio(in, NULL);
+            crl=d2i_X509_CRL_bio(in, nullptr);
             if (crl) {
                 crls.push_back(new OpenSSLCryptoX509CRL(crl));
                 X509_CRL_free(crl);
@@ -397,6 +421,10 @@ XSECCryptoKey* SecurityHelper::loadKeyFromURL(SOAPTransport& transport, const ch
     transport.send();
     istream& msg = transport.receive();
 
+    // Check for "not modified" status.
+    if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
+        throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
+
     // Dump to output file.
     ofstream out(backing, fstream::trunc|fstream::binary);
     out << msg.rdbuf();
@@ -412,6 +440,10 @@ vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromURL(
     transport.send();
     istream& msg = transport.receive();
 
+    // Check for "not modified" status.
+    if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
+        throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
+
     // Dump to output file.
     ofstream out(backing, fstream::trunc|fstream::binary);
     out << msg.rdbuf();
@@ -428,6 +460,10 @@ vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromURL(
     transport.send();
     istream& msg = transport.receive();
 
+    // Check for "not modified" status.
+    if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
+        throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
+
     // Dump to output file.
     ofstream out(backing, fstream::trunc|fstream::binary);
     out << msg.rdbuf();
@@ -480,11 +516,76 @@ bool SecurityHelper::matches(const XSECCryptoKey& key1, const XSECCryptoKey& key
         return (dsa1 && dsa2 && BN_cmp(dsa1->priv_key,dsa2->priv_key) == 0);
     }
 
+#if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
+    // If one key is public or both, just compare the public key half.
+    if (key1.getKeyType()==XSECCryptoKey::KEY_EC_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_EC_PAIR) {
+        if (key2.getKeyType()!=XSECCryptoKey::KEY_EC_PUBLIC && key2.getKeyType()!=XSECCryptoKey::KEY_EC_PAIR)
+            return false;
+        const EC_KEY* ec1 = static_cast<const OpenSSLCryptoKeyEC&>(key1).getOpenSSLEC();
+        const EC_KEY* ec2 = static_cast<const OpenSSLCryptoKeyEC&>(key2).getOpenSSLEC();
+        if (!ec1 || !ec2)
+            return false;
+        if (EC_GROUP_cmp(EC_KEY_get0_group(ec1), EC_KEY_get0_group(ec2), nullptr) != 0)
+            return false;
+        return (EC_POINT_cmp(EC_KEY_get0_group(ec1), EC_KEY_get0_public_key(ec1), EC_KEY_get0_public_key(ec2), nullptr) == 0);
+    }
+
+    // For a private key, compare the private half.
+    if (key1.getKeyType()==XSECCryptoKey::KEY_EC_PRIVATE) {
+        if (key2.getKeyType()!=XSECCryptoKey::KEY_EC_PRIVATE && key2.getKeyType()!=XSECCryptoKey::KEY_EC_PAIR)
+            return false;
+        const EC_KEY* ec1 = static_cast<const OpenSSLCryptoKeyEC&>(key1).getOpenSSLEC();
+        const EC_KEY* ec2 = static_cast<const OpenSSLCryptoKeyEC&>(key2).getOpenSSLEC();
+        return (ec1 && ec2 && BN_cmp(EC_KEY_get0_private_key(ec1), EC_KEY_get0_private_key(ec2)) == 0);
+    }
+#endif
+
     Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("unsupported key type for comparison");
     return false;
 }
 
-string SecurityHelper::getDEREncoding(const XSECCryptoKey& key, bool nowrap)
+string SecurityHelper::doHash(const char* hashAlg, const char* buf, unsigned long buflen, bool toHex)
+{
+    static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+    string ret;
+
+    const EVP_MD* md = EVP_get_digestbyname(hashAlg);
+    if (!md) {
+        Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hashAlg);
+        return ret;
+    }
+
+    BIO* chain = BIO_new(BIO_s_mem());
+    BIO* b = BIO_new(BIO_f_md());
+    BIO_set_md(b, md);
+    chain = BIO_push(b, chain);
+    BIO_write(chain, buf, buflen);
+    BIO_flush(chain);
+
+    char digest[EVP_MAX_MD_SIZE];
+    int len = BIO_gets(chain, digest, EVP_MD_size(md));
+    BIO_free_all(chain);
+    if (len != EVP_MD_size(md)) {
+        Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error(
+            "hash result length (%d) did not match expected value (%d)", len, EVP_MD_size(md)
+            );
+        return ret;
+    }
+    if (toHex) {
+        for (int i=0; i < len; ++i) {
+            ret += (DIGITS[((unsigned char)(0xF0 & digest[i])) >> 4 ]);
+            ret += (DIGITS[0x0F & digest[i]]);
+        }
+    }
+    else {
+        for (int i=0; i < len; ++i) {
+            ret += digest[i];
+        }
+    }
+    return ret;
+}
+
+string SecurityHelper::getDEREncoding(const XSECCryptoKey& key, const char* hash, bool nowrap)
 {
     string ret;
 
@@ -493,51 +594,94 @@ string SecurityHelper::getDEREncoding(const XSECCryptoKey& key, bool nowrap)
         return ret;
     }
 
+    const RSA* rsa = nullptr;
+    const DSA* dsa = nullptr;
+#if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
+    const EC_KEY* ec = nullptr;
+#endif
+
     if (key.getKeyType() == XSECCryptoKey::KEY_RSA_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_RSA_PAIR) {
-        const RSA* rsa = static_cast<const OpenSSLCryptoKeyRSA&>(key).getOpenSSLRSA();
+        rsa = static_cast<const OpenSSLCryptoKeyRSA&>(key).getOpenSSLRSA();
         if (!rsa) {
             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("key was not populated");
             return ret;
         }
-        BIO* base64 = BIO_new(BIO_f_base64());
-        if (nowrap)
-            BIO_set_flags(base64, BIO_FLAGS_BASE64_NO_NL);
-        BIO* mem = BIO_new(BIO_s_mem());
-        BIO_push(base64, mem);
-        i2d_RSA_PUBKEY_bio(base64, const_cast<RSA*>(rsa));
-        BIO_flush(base64);
-        BUF_MEM* bptr=NULL;
-        BIO_get_mem_ptr(base64, &bptr);
-        if (bptr && bptr->length > 0)
-            ret.append(bptr->data, bptr->length);
-        BIO_free_all(base64);
     }
     else if (key.getKeyType() == XSECCryptoKey::KEY_DSA_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_DSA_PAIR) {
-        const DSA* dsa = static_cast<const OpenSSLCryptoKeyDSA&>(key).getOpenSSLDSA();
+        dsa = static_cast<const OpenSSLCryptoKeyDSA&>(key).getOpenSSLDSA();
         if (!dsa) {
             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("key was not populated");
             return ret;
         }
-        BIO* base64 = BIO_new(BIO_f_base64());
-        if (nowrap)
-            BIO_set_flags(base64, BIO_FLAGS_BASE64_NO_NL);
-        BIO* mem = BIO_new(BIO_s_mem());
-        BIO_push(base64, mem);
-        i2d_DSA_PUBKEY_bio(base64, const_cast<DSA*>(dsa));
-        BIO_flush(base64);
-        BUF_MEM* bptr=NULL;
-        BIO_get_mem_ptr(base64, &bptr);
-        if (bptr && bptr->length > 0)
-            ret.append(bptr->data, bptr->length);
-        BIO_free_all(base64);
     }
+#if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
+    else if (key.getKeyType() == XSECCryptoKey::KEY_EC_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_EC_PAIR) {
+        ec = static_cast<const OpenSSLCryptoKeyEC&>(key).getOpenSSLEC();
+        if (!ec) {
+            Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("key was not populated");
+            return ret;
+        }
+    }
+#endif
     else {
-        Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("encoding of non-RSA/DSA public keys not supported");
+        Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("public key type not supported");
+        return ret;
+    }
+
+    const EVP_MD* md=nullptr;
+    if (hash) {
+        md = EVP_get_digestbyname(hash);
+        if (!md) {
+            Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hash);
+            return ret;
+        }
+    }
+
+    BIO* chain = BIO_new(BIO_s_mem());
+    BIO* b = BIO_new(BIO_f_base64());
+    if (nowrap)
+        BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
+    chain = BIO_push(b, chain);
+    if (md) {
+        b = BIO_new(BIO_f_md());
+        BIO_set_md(b, md);
+        chain = BIO_push(b, chain);
     }
+
+    if (rsa)
+        i2d_RSA_PUBKEY_bio(chain, const_cast<RSA*>(rsa));
+    else if (dsa)
+        i2d_DSA_PUBKEY_bio(chain, const_cast<DSA*>(dsa));
+#if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
+    else
+        i2d_EC_PUBKEY_bio(chain, const_cast<EC_KEY*>(ec));
+#endif
+
+    BIO_flush(chain);
+    if (md) {
+        char digest[EVP_MAX_MD_SIZE];
+        int len = BIO_gets(chain, digest, EVP_MD_size(md));
+        if (len != EVP_MD_size(md)) {
+            BIO_free_all(chain);
+            return ret;
+        }
+        b = BIO_pop(chain);
+        BIO_free(chain);
+        chain = b;
+        BIO_reset(chain);
+        BIO_write(chain, digest, len);
+        BIO_flush(chain);
+    }
+    BUF_MEM* bptr=nullptr;
+    BIO_get_mem_ptr(chain, &bptr);
+    if (bptr && bptr->length > 0)
+        ret.append(bptr->data, bptr->length);
+    BIO_free_all(chain);
+
     return ret;
 }
 
-string SecurityHelper::getDEREncoding(const XSECCryptoX509& cert, bool nowrap)
+string SecurityHelper::getDEREncoding(const XSECCryptoX509& cert, const char* hash, bool nowrap)
 {
     string ret;
 
@@ -546,31 +690,146 @@ string SecurityHelper::getDEREncoding(const XSECCryptoX509& cert, bool nowrap)
         return ret;
     }
 
+    const EVP_MD* md=nullptr;
+    if (hash) {
+        md = EVP_get_digestbyname(hash);
+        if (!md) {
+            Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hash);
+            return ret;
+        }
+    }
+
     const X509* x = static_cast<const OpenSSLCryptoX509&>(cert).getOpenSSLX509();
     EVP_PKEY* key = X509_get_pubkey(const_cast<X509*>(x));
 
-    BIO* base64 = BIO_new(BIO_f_base64());
+    BIO* chain = BIO_new(BIO_s_mem());
+    BIO* b = BIO_new(BIO_f_base64());
     if (nowrap)
-        BIO_set_flags(base64, BIO_FLAGS_BASE64_NO_NL);
-    BIO* mem = BIO_new(BIO_s_mem());
-    BIO_push(base64, mem);
-    i2d_PUBKEY_bio(base64, key);
+        BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
+    chain = BIO_push(b, chain);
+    if (md) {
+        b = BIO_new(BIO_f_md());
+        BIO_set_md(b, md);
+        chain = BIO_push(b, chain);
+    }
+    i2d_PUBKEY_bio(chain, key);
     EVP_PKEY_free(key);
-    BIO_flush(base64);
-    BUF_MEM* bptr=NULL;
-    BIO_get_mem_ptr(base64, &bptr);
+    BIO_flush(chain);
+    if (md) {
+        char digest[EVP_MAX_MD_SIZE];
+        int len = BIO_gets(chain, digest, EVP_MD_size(md));
+        if (len != EVP_MD_size(md)) {
+            BIO_free_all(chain);
+            return ret;
+        }
+        b = BIO_pop(chain);
+        BIO_free(chain);
+        chain = b;
+        BIO_reset(chain);
+        BIO_write(chain, digest, len);
+        BIO_flush(chain);
+    }
+    BUF_MEM* bptr=nullptr;
+    BIO_get_mem_ptr(chain, &bptr);
     if (bptr && bptr->length > 0)
         ret.append(bptr->data, bptr->length);
-    BIO_free_all(base64);
+    BIO_free_all(chain);
     return ret;
 }
 
-string SecurityHelper::getDEREncoding(const Credential& cred, bool nowrap)
+string SecurityHelper::getDEREncoding(const Credential& cred, const char* hash, bool nowrap)
 {
     const X509Credential* x509 = dynamic_cast<const X509Credential*>(&cred);
     if (x509 && !x509->getEntityCertificateChain().empty())
-        return getDEREncoding(*(x509->getEntityCertificateChain().front()), nowrap);
+        return getDEREncoding(*(x509->getEntityCertificateChain().front()), hash, nowrap);
     else if (cred.getPublicKey())
-        return getDEREncoding(*(cred.getPublicKey()), nowrap);
+        return getDEREncoding(*(cred.getPublicKey()), hash, nowrap);
     return "";
 }
+
+string SecurityHelper::getDEREncoding(const XSECCryptoKey& key, bool hash, bool nowrap)
+{
+    return getDEREncoding(key, hash ? "SHA1" : nullptr, nowrap);
+}
+
+string SecurityHelper::getDEREncoding(const XSECCryptoX509& cert, bool hash, bool nowrap)
+{
+    return getDEREncoding(cert, hash ? "SHA1" : nullptr, nowrap);
+}
+
+string SecurityHelper::getDEREncoding(const Credential& cred, bool hash, bool nowrap)
+{
+    return getDEREncoding(cred, hash ? "SHA1" : nullptr, nowrap);
+}
+
+XSECCryptoKey* SecurityHelper::fromDEREncoding(const char* buf, unsigned long buflen, bool base64)
+{
+    xsecsize_t x;
+    XMLByte* decoded=nullptr;
+    if (base64) {
+        decoded = xercesc::Base64::decode(reinterpret_cast<const XMLByte*>(buf), &x);
+        if (!decoded) {
+            Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("base64 decode failed");
+            return nullptr;
+        }
+    }
+
+    BIO* b = BIO_new_mem_buf((void*)(base64 ? (char*)decoded : buf), (base64 ? x : buflen));
+    EVP_PKEY* pkey = d2i_PUBKEY_bio(b, nullptr);
+    BIO_free(b);
+    if (base64) {
+#ifdef XMLTOOLING_XERCESC_HAS_XMLBYTE_RELEASE
+        XMLString::release(&decoded);
+#else
+        XMLString::release((char**)&decoded);
+#endif
+    }
+
+    if (pkey) {
+        // Now map it to an XSEC wrapper.
+        XSECCryptoKey* ret = nullptr;
+        try {
+            switch (pkey->type) {
+                case EVP_PKEY_RSA:
+                    ret = new OpenSSLCryptoKeyRSA(pkey);
+                    break;
+
+                case EVP_PKEY_DSA:
+                    ret = new OpenSSLCryptoKeyDSA(pkey);
+                    break;
+
+#if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
+                case EVP_PKEY_EC:
+                    ret = new OpenSSLCryptoKeyEC(pkey);
+                    break;
+#endif
+                default:
+                    Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("unsupported public key type");
+            }
+        }
+        catch (XSECCryptoException& ex) {
+            Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error(ex.getMsg());
+        }
+        EVP_PKEY_free(pkey);
+        return ret;
+    }
+
+    return nullptr;
+}
+
+XSECCryptoKey* SecurityHelper::fromDEREncoding(const XMLCh* buf)
+{
+    xsecsize_t x;
+    XMLByte* decoded = xercesc::Base64::decodeToXMLByte(buf, &x);
+    if (!decoded) {
+        Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("base64 decode failed");
+        return nullptr;
+    }
+    XSECCryptoKey* ret = fromDEREncoding((const char*)decoded, x, false);
+#ifdef XMLTOOLING_XERCESC_HAS_XMLBYTE_RELEASE
+    XMLString::release(&decoded);
+#else
+    XMLString::release((char**)&decoded);
+#endif
+    return ret;
+}