54e90fbd5166d64d904f4d315157dc134070ec0e
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / SecurityHelper.cpp
1 /*
2  *  Copyright 2001-2010 Internet2
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * SecurityHelper.cpp
19  *
20  * A helper class for working with keys, certificates, etc.
21  */
22
23 #include "internal.h"
24 #include "logging.h"
25 #include "io/HTTPResponse.h"
26 #include "security/OpenSSLCryptoX509CRL.h"
27 #include "security/SecurityHelper.h"
28 #include "security/X509Credential.h"
29 #include "soap/HTTPSOAPTransport.h"
30 #include "util/NDC.h"
31
32 #include <fstream>
33 #include <openssl/evp.h>
34 #include <openssl/pem.h>
35 #include <openssl/pkcs12.h>
36 #include <xsec/enc/XSECCryptoException.hpp>
37 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
38 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
39 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
40 #include <xercesc/util/Base64.hpp>
41
42 #ifdef WIN32
43 # if (OPENSSL_VERSION_NUMBER >= 0x00907000)
44 #  define XMLTOOLING_OPENSSL_HAVE_EC 1
45 # endif
46 #endif
47
48 #if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
49 # include <xsec/enc/OpenSSL/OpenSSLCryptoKeyEC.hpp>
50 #endif
51
52 using namespace xmltooling::logging;
53 using namespace xmltooling;
54 using namespace std;
55
56 // OpenSSL password callback...
57 static int passwd_callback(char* buf, int len, int verify, void* passwd)
58 {
59     if(!verify)
60     {
61         if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))
62         {
63             strcpy(buf,reinterpret_cast<char*>(passwd));
64             return strlen(buf);
65         }
66     }
67     return 0;
68 }
69
70 const char* SecurityHelper::guessEncodingFormat(const char* pathname)
71 {
72     const char* format=nullptr;
73     BIO* in=BIO_new(BIO_s_file_internal());
74     if (in && BIO_read_filename(in, pathname)>0) {
75         const int READSIZE = 1;
76         char buf[READSIZE];
77         int mark;
78
79         // Examine the first byte.
80         try {
81             if ((mark = BIO_tell(in)) < 0)
82                 throw XMLSecurityException("Error loading file: BIO_tell() can't get the file position.");
83             if (BIO_read(in, buf, READSIZE) <= 0)
84                 throw XMLSecurityException("Error loading file: BIO_read() can't read from the stream.");
85             if (BIO_seek(in, mark) < 0)
86                 throw XMLSecurityException("Error loading file: BIO_seek() can't reset the file position.");
87         }
88         catch (exception&) {
89             log_openssl();
90             BIO_free(in);
91             throw;
92         }
93
94         // Check the first byte of the file.  If it's some kind of DER-encoded structure
95         // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
96         if (buf[0] != 48) {
97             format = "PEM";
98         }
99         else {
100             // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
101             // If it fails, must be another kind of DER-encoded structure.
102             PKCS12* p12;
103             if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
104                 format = "DER";
105             }
106             else {
107                 format = "PKCS12";
108                 PKCS12_free(p12);
109             }
110         }
111     }
112     if (in)
113         BIO_free(in);
114     if (format)
115         return format;
116     throw XMLSecurityException("Unable to determine encoding for file ($1).", params(1,pathname));
117 }
118
119 XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char* format, const char* password)
120 {
121 #ifdef _DEBUG
122     NDC ndc("loadKeyFromFile");
123 #endif
124     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper");
125     log.info("loading private key from file (%s)", pathname);
126
127     // Native objects.
128     PKCS12* p12=nullptr;
129     EVP_PKEY* pkey=nullptr;
130
131     BIO* in=BIO_new(BIO_s_file_internal());
132     if (in && BIO_read_filename(in, pathname)>0) {
133         // If the format isn't set, try and guess it.
134         if (!format || !*format) {
135             const int READSIZE = 1;
136             char buf[READSIZE];
137             int mark;
138
139             // Examine the first byte.
140             try {
141                 if ((mark = BIO_tell(in)) < 0)
142                     throw XMLSecurityException("Error loading key: BIO_tell() can't get the file position.");
143                 if (BIO_read(in, buf, READSIZE) <= 0)
144                     throw XMLSecurityException("Error loading key: BIO_read() can't read from the stream.");
145                 if (BIO_seek(in, mark) < 0)
146                     throw XMLSecurityException("Error loading key: BIO_seek() can't reset the file position.");
147             }
148             catch (exception&) {
149                 log_openssl();
150                 BIO_free(in);
151                 throw;
152             }
153
154             // Check the first byte of the file.  If it's some kind of DER-encoded structure
155             // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
156             if (buf[0] != 48) {
157                 format = "PEM";
158             }
159             else {
160                 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
161                 // If it fails, must be another kind of DER-encoded structure.
162                 if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
163                     format = "DER";
164                     if (BIO_seek(in, mark) < 0) {
165                         log_openssl();
166                         BIO_free(in);
167                         throw XMLSecurityException("Error loading key: BIO_seek() can't reset the file position.");
168                     }
169                 }
170                 else {
171                     format = "PKCS12";
172                 }
173             }
174             log.debug("key encoding format for (%s) dynamically resolved as (%s)", pathname, format);
175         }
176
177         // The format should be known, so parse accordingly.
178         if (!strcmp(format, "PEM")) {
179             pkey = PEM_read_bio_PrivateKey(in, nullptr, passwd_callback, const_cast<char*>(password));
180         }
181         else if (!strcmp(format, "DER")) {
182             pkey=d2i_PrivateKey_bio(in, nullptr);
183         }
184         else if (!strcmp(format, "PKCS12")) {
185             if (!p12)
186                 p12 = d2i_PKCS12_bio(in, nullptr);
187             if (p12) {
188                 X509* x=nullptr;
189                 PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, nullptr);
190                 PKCS12_free(p12);
191                 X509_free(x);
192             }
193         }
194         else {
195             log.error("unknown key encoding format (%s)", format);
196         }
197     }
198     if (in)
199         BIO_free(in);
200
201     // Now map it to an XSEC wrapper.
202     if (pkey) {
203         XSECCryptoKey* ret=nullptr;
204         switch (pkey->type) {
205             case EVP_PKEY_RSA:
206                 ret=new OpenSSLCryptoKeyRSA(pkey);
207                 break;
208
209             case EVP_PKEY_DSA:
210                 ret=new OpenSSLCryptoKeyDSA(pkey);
211                 break;
212
213 #if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
214             case EVP_PKEY_EC:
215                 ret=new OpenSSLCryptoKeyEC(pkey);
216                 break;
217 #endif
218             default:
219                 log.error("unsupported private key type");
220         }
221         EVP_PKEY_free(pkey);
222         if (ret)
223             return ret;
224     }
225
226     log_openssl();
227     throw XMLSecurityException("Unable to load private key from file ($1).", params(1, pathname));
228 }
229
230 vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromFile(
231     vector<XSECCryptoX509*>& certs, const char* pathname, const char* format, const char* password
232     )
233 {
234 #ifdef _DEBUG
235     NDC ndc("loadCertificatesFromFile");
236 #endif
237     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper");
238     log.info("loading certificate(s) from file (%s)", pathname);
239
240     vector<XSECCryptoX509*>::size_type count = certs.size();
241
242     // Native objects.
243     X509* x=nullptr;
244     PKCS12* p12=nullptr;
245
246     BIO* in=BIO_new(BIO_s_file_internal());
247     if (in && BIO_read_filename(in, pathname)>0) {
248         // If the format isn't set, try and guess it.
249         if (!format || !*format) {
250             const int READSIZE = 1;
251             char buf[READSIZE];
252             int mark;
253
254             // Examine the first byte.
255             try {
256                 if ((mark = BIO_tell(in)) < 0)
257                     throw XMLSecurityException("Error loading certificate: BIO_tell() can't get the file position.");
258                 if (BIO_read(in, buf, READSIZE) <= 0)
259                     throw XMLSecurityException("Error loading certificate: BIO_read() can't read from the stream.");
260                 if (BIO_seek(in, mark) < 0)
261                     throw XMLSecurityException("Error loading certificate: BIO_seek() can't reset the file position.");
262             }
263             catch (exception&) {
264                 log_openssl();
265                 BIO_free(in);
266                 throw;
267             }
268
269             // Check the first byte of the file.  If it's some kind of DER-encoded structure
270             // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
271             if (buf[0] != 48) {
272                 format = "PEM";
273             }
274             else {
275                 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
276                 // If it fails, must be another kind of DER-encoded structure.
277                 if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
278                     format = "DER";
279                     if (BIO_seek(in, mark) < 0) {
280                         log_openssl();
281                         BIO_free(in);
282                         throw XMLSecurityException("Error loading certificate: BIO_seek() can't reset the file position.");
283                     }
284                 }
285                 else {
286                     format = "PKCS12";
287                 }
288             }
289         }
290
291         // The format should be known, so parse accordingly.
292         if (!strcmp(format, "PEM")) {
293             while (x=PEM_read_bio_X509(in, nullptr, nullptr, nullptr)) {
294                 certs.push_back(new OpenSSLCryptoX509(x));
295                 X509_free(x);
296             }
297         }
298         else if (!strcmp(format, "DER")) {
299             x=d2i_X509_bio(in, nullptr);
300             if (x) {
301                 certs.push_back(new OpenSSLCryptoX509(x));
302                 X509_free(x);
303             }
304         }
305         else if (!strcmp(format, "PKCS12")) {
306             if (!p12)
307                 p12 = d2i_PKCS12_bio(in, nullptr);
308             if (p12) {
309                 EVP_PKEY* pkey=nullptr;
310                 STACK_OF(X509)* CAstack = sk_X509_new_null();
311                 PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, &CAstack);
312                 PKCS12_free(p12);
313                 EVP_PKEY_free(pkey);
314                 if (x) {
315                     certs.push_back(new OpenSSLCryptoX509(x));
316                     X509_free(x);
317                 }
318                 x = sk_X509_pop(CAstack);
319                 while (x) {
320                     certs.push_back(new OpenSSLCryptoX509(x));
321                     X509_free(x);
322                     x = sk_X509_pop(CAstack);
323                 }
324                 sk_X509_free(CAstack);
325             }
326         }
327     }
328     if (in)
329         BIO_free(in);
330
331     if (certs.size() == count) {
332         log_openssl();
333         throw XMLSecurityException("Unable to load certificate(s) from file ($1).", params(1, pathname));
334     }
335
336     return certs.size();
337 }
338
339 vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromFile(
340     vector<XSECCryptoX509CRL*>& crls, const char* pathname, const char* format
341     )
342 {
343 #ifdef _DEBUG
344     NDC ndc("loadCRLsFromFile");
345 #endif
346     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper");
347     log.info("loading CRL(s) from file (%s)", pathname);
348
349     vector<XSECCryptoX509CRL*>::size_type count = crls.size();
350
351     BIO* in=BIO_new(BIO_s_file_internal());
352     if (in && BIO_read_filename(in, pathname)>0) {
353         // If the format isn't set, try and guess it.
354         if (!format || !*format) {
355             const int READSIZE = 1;
356             char buf[READSIZE];
357             int mark;
358
359             // Examine the first byte.
360             try {
361                 if ((mark = BIO_tell(in)) < 0)
362                     throw XMLSecurityException("Error loading CRL: BIO_tell() can't get the file position.");
363                 if (BIO_read(in, buf, READSIZE) <= 0)
364                     throw XMLSecurityException("Error loading CRL: BIO_read() can't read from the stream.");
365                 if (BIO_seek(in, mark) < 0)
366                     throw XMLSecurityException("Error loading CRL: BIO_seek() can't reset the file position.");
367             }
368             catch (exception&) {
369                 log_openssl();
370                 BIO_free(in);
371                 throw;
372             }
373
374             // Check the first byte of the file.  If it's some kind of DER-encoded structure
375             // it will begin with ASCII 048. Otherwise, assume it's PEM.
376             if (buf[0] != 48) {
377                 format = "PEM";
378             }
379             else {
380                 format = "DER";
381             }
382             log.debug("CRL encoding format for (%s) dynamically resolved as (%s)", pathname, format);
383         }
384
385         X509_CRL* crl=nullptr;
386         if (!strcmp(format, "PEM")) {
387             while (crl=PEM_read_bio_X509_CRL(in, nullptr, nullptr, nullptr)) {
388                 crls.push_back(new OpenSSLCryptoX509CRL(crl));
389                 X509_CRL_free(crl);
390             }
391         }
392         else if (!strcmp(format, "DER")) {
393             crl=d2i_X509_CRL_bio(in, nullptr);
394             if (crl) {
395                 crls.push_back(new OpenSSLCryptoX509CRL(crl));
396                 X509_CRL_free(crl);
397             }
398         }
399         else {
400             log.error("unknown CRL encoding format (%s)", format);
401         }
402     }
403     if (in)
404         BIO_free(in);
405
406     if (crls.size() == count) {
407         log_openssl();
408         throw XMLSecurityException("Unable to load CRL(s) from file ($1).", params(1, pathname));
409     }
410
411     return crls.size();
412 }
413
414 XSECCryptoKey* SecurityHelper::loadKeyFromURL(SOAPTransport& transport, const char* backing, const char* format, const char* password)
415 {
416     // Fetch the data.
417     transport.send();
418     istream& msg = transport.receive();
419
420     // Check for "not modified" status.
421     if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
422         throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
423
424     // Dump to output file.
425     ofstream out(backing, fstream::trunc|fstream::binary);
426     out << msg.rdbuf();
427     out.close();
428
429     return loadKeyFromFile(backing, format, password);
430 }
431
432 vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromURL(
433     vector<XSECCryptoX509*>& certs, SOAPTransport& transport, const char* backing, const char* format, const char* password
434     )
435 {
436     transport.send();
437     istream& msg = transport.receive();
438
439     // Check for "not modified" status.
440     if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
441         throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
442
443     // Dump to output file.
444     ofstream out(backing, fstream::trunc|fstream::binary);
445     out << msg.rdbuf();
446     out.close();
447
448     return loadCertificatesFromFile(certs, backing, format, password);
449 }
450
451 vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromURL(
452     vector<XSECCryptoX509CRL*>& crls, SOAPTransport& transport, const char* backing, const char* format
453     )
454 {
455     // Fetch the data.
456     transport.send();
457     istream& msg = transport.receive();
458
459     // Check for "not modified" status.
460     if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
461         throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
462
463     // Dump to output file.
464     ofstream out(backing, fstream::trunc|fstream::binary);
465     out << msg.rdbuf();
466     out.close();
467
468     return loadCRLsFromFile(crls, backing, format);
469 }
470
471 bool SecurityHelper::matches(const XSECCryptoKey& key1, const XSECCryptoKey& key2)
472 {
473     if (key1.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL ||
474         key2.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
475         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("comparison of non-OpenSSL keys not supported");
476         return false;
477     }
478
479     // If one key is public or both, just compare the public key half.
480     if (key1.getKeyType()==XSECCryptoKey::KEY_RSA_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_RSA_PAIR) {
481         if (key2.getKeyType()!=XSECCryptoKey::KEY_RSA_PUBLIC && key2.getKeyType()!=XSECCryptoKey::KEY_RSA_PAIR)
482             return false;
483         const RSA* rsa1 = static_cast<const OpenSSLCryptoKeyRSA&>(key1).getOpenSSLRSA();
484         const RSA* rsa2 = static_cast<const OpenSSLCryptoKeyRSA&>(key2).getOpenSSLRSA();
485         return (rsa1 && rsa2 && BN_cmp(rsa1->n,rsa2->n) == 0 && BN_cmp(rsa1->e,rsa2->e) == 0);
486     }
487
488     // For a private key, compare the private half.
489     if (key1.getKeyType()==XSECCryptoKey::KEY_RSA_PRIVATE) {
490         if (key2.getKeyType()!=XSECCryptoKey::KEY_RSA_PRIVATE && key2.getKeyType()!=XSECCryptoKey::KEY_RSA_PAIR)
491             return false;
492         const RSA* rsa1 = static_cast<const OpenSSLCryptoKeyRSA&>(key1).getOpenSSLRSA();
493         const RSA* rsa2 = static_cast<const OpenSSLCryptoKeyRSA&>(key2).getOpenSSLRSA();
494         return (rsa1 && rsa2 && BN_cmp(rsa1->n,rsa2->n) == 0 && BN_cmp(rsa1->d,rsa2->d) == 0);
495     }
496
497     // If one key is public or both, just compare the public key half.
498     if (key1.getKeyType()==XSECCryptoKey::KEY_DSA_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_DSA_PAIR) {
499         if (key2.getKeyType()!=XSECCryptoKey::KEY_DSA_PUBLIC && key2.getKeyType()!=XSECCryptoKey::KEY_DSA_PAIR)
500             return false;
501         const DSA* dsa1 = static_cast<const OpenSSLCryptoKeyDSA&>(key1).getOpenSSLDSA();
502         const DSA* dsa2 = static_cast<const OpenSSLCryptoKeyDSA&>(key2).getOpenSSLDSA();
503         return (dsa1 && dsa2 && BN_cmp(dsa1->pub_key,dsa2->pub_key) == 0);
504     }
505
506     // For a private key, compare the private half.
507     if (key1.getKeyType()==XSECCryptoKey::KEY_DSA_PRIVATE) {
508         if (key2.getKeyType()!=XSECCryptoKey::KEY_DSA_PRIVATE && key2.getKeyType()!=XSECCryptoKey::KEY_DSA_PAIR)
509             return false;
510         const DSA* dsa1 = static_cast<const OpenSSLCryptoKeyDSA&>(key1).getOpenSSLDSA();
511         const DSA* dsa2 = static_cast<const OpenSSLCryptoKeyDSA&>(key2).getOpenSSLDSA();
512         return (dsa1 && dsa2 && BN_cmp(dsa1->priv_key,dsa2->priv_key) == 0);
513     }
514
515 #if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
516     // If one key is public or both, just compare the public key half.
517     if (key1.getKeyType()==XSECCryptoKey::KEY_EC_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_EC_PAIR) {
518         if (key2.getKeyType()!=XSECCryptoKey::KEY_EC_PUBLIC && key2.getKeyType()!=XSECCryptoKey::KEY_EC_PAIR)
519             return false;
520         const EC_KEY* ec1 = static_cast<const OpenSSLCryptoKeyEC&>(key1).getOpenSSLEC();
521         const EC_KEY* ec2 = static_cast<const OpenSSLCryptoKeyEC&>(key2).getOpenSSLEC();
522         if (!ec1 || !ec2)
523             return false;
524         if (EC_GROUP_cmp(EC_KEY_get0_group(ec1), EC_KEY_get0_group(ec2), nullptr) != 0)
525             return false;
526         return (EC_POINT_cmp(EC_KEY_get0_group(ec1), EC_KEY_get0_public_key(ec1), EC_KEY_get0_public_key(ec2), nullptr) == 0);
527     }
528
529     // For a private key, compare the private half.
530     if (key1.getKeyType()==XSECCryptoKey::KEY_EC_PRIVATE) {
531         if (key2.getKeyType()!=XSECCryptoKey::KEY_EC_PRIVATE && key2.getKeyType()!=XSECCryptoKey::KEY_EC_PAIR)
532             return false;
533         const EC_KEY* ec1 = static_cast<const OpenSSLCryptoKeyEC&>(key1).getOpenSSLEC();
534         const EC_KEY* ec2 = static_cast<const OpenSSLCryptoKeyEC&>(key2).getOpenSSLEC();
535         return (ec1 && ec2 && BN_cmp(EC_KEY_get0_private_key(ec1), EC_KEY_get0_private_key(ec2)) == 0);
536     }
537 #endif
538
539     Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("unsupported key type for comparison");
540     return false;
541 }
542
543 string SecurityHelper::doHash(const char* hashAlg, const char* buf, unsigned long buflen, bool toHex)
544 {
545     static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
546     string ret;
547
548     const EVP_MD* md = EVP_get_digestbyname(hashAlg);
549     if (!md) {
550         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hashAlg);
551         return ret;
552     }
553
554     BIO* chain = BIO_new(BIO_s_mem());
555     BIO* b = BIO_new(BIO_f_md());
556     BIO_set_md(b, md);
557     chain = BIO_push(b, chain);
558     BIO_write(chain, buf, buflen);
559     BIO_flush(chain);
560
561     char digest[EVP_MAX_MD_SIZE];
562     int len = BIO_gets(chain, digest, EVP_MD_size(md));
563     BIO_free_all(chain);
564     if (len != EVP_MD_size(md)) {
565         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error(
566             "hash result length (%d) did not match expected value (%d)", len, EVP_MD_size(md)
567             );
568         return ret;
569     }
570     if (toHex) {
571         for (int i=0; i < len; ++i) {
572             ret += (DIGITS[((unsigned char)(0xF0 & digest[i])) >> 4 ]);
573             ret += (DIGITS[0x0F & digest[i]]);
574         }
575     }
576     else {
577         for (int i=0; i < len; ++i) {
578             ret += digest[i];
579         }
580     }
581     return ret;
582 }
583
584 string SecurityHelper::getDEREncoding(const XSECCryptoKey& key, const char* hash, bool nowrap)
585 {
586     string ret;
587
588     if (key.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
589         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("encoding of non-OpenSSL keys not supported");
590         return ret;
591     }
592
593     const RSA* rsa = nullptr;
594     const DSA* dsa = nullptr;
595 #if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
596     const EC_KEY* ec = nullptr;
597 #endif
598
599     if (key.getKeyType() == XSECCryptoKey::KEY_RSA_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_RSA_PAIR) {
600         rsa = static_cast<const OpenSSLCryptoKeyRSA&>(key).getOpenSSLRSA();
601         if (!rsa) {
602             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("key was not populated");
603             return ret;
604         }
605     }
606     else if (key.getKeyType() == XSECCryptoKey::KEY_DSA_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_DSA_PAIR) {
607         dsa = static_cast<const OpenSSLCryptoKeyDSA&>(key).getOpenSSLDSA();
608         if (!dsa) {
609             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("key was not populated");
610             return ret;
611         }
612     }
613 #if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
614     else if (key.getKeyType() == XSECCryptoKey::KEY_EC_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_EC_PAIR) {
615         ec = static_cast<const OpenSSLCryptoKeyEC&>(key).getOpenSSLEC();
616         if (!ec) {
617             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("key was not populated");
618             return ret;
619         }
620     }
621 #endif
622     else {
623         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("public key type not supported");
624         return ret;
625     }
626
627     const EVP_MD* md=nullptr;
628     if (hash) {
629         md = EVP_get_digestbyname(hash);
630         if (!md) {
631             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hash);
632             return ret;
633         }
634     }
635
636     BIO* chain = BIO_new(BIO_s_mem());
637     BIO* b = BIO_new(BIO_f_base64());
638     if (nowrap)
639         BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
640     chain = BIO_push(b, chain);
641     if (md) {
642         b = BIO_new(BIO_f_md());
643         BIO_set_md(b, md);
644         chain = BIO_push(b, chain);
645     }
646
647     if (rsa)
648         i2d_RSA_PUBKEY_bio(chain, const_cast<RSA*>(rsa));
649     else if (dsa)
650         i2d_DSA_PUBKEY_bio(chain, const_cast<DSA*>(dsa));
651 #if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
652     else
653         i2d_EC_PUBKEY_bio(chain, const_cast<EC_KEY*>(ec));
654 #endif
655
656     BIO_flush(chain);
657     if (md) {
658         char digest[EVP_MAX_MD_SIZE];
659         int len = BIO_gets(chain, digest, EVP_MD_size(md));
660         if (len != EVP_MD_size(md)) {
661             BIO_free_all(chain);
662             return ret;
663         }
664         b = BIO_pop(chain);
665         BIO_free(chain);
666         chain = b;
667         BIO_reset(chain);
668         BIO_write(chain, digest, len);
669         BIO_flush(chain);
670     }
671     BUF_MEM* bptr=nullptr;
672     BIO_get_mem_ptr(chain, &bptr);
673     if (bptr && bptr->length > 0)
674         ret.append(bptr->data, bptr->length);
675     BIO_free_all(chain);
676
677     return ret;
678 }
679
680 string SecurityHelper::getDEREncoding(const XSECCryptoX509& cert, const char* hash, bool nowrap)
681 {
682     string ret;
683
684     if (cert.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
685         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("encoding of non-OpenSSL keys not supported");
686         return ret;
687     }
688
689     const EVP_MD* md=nullptr;
690     if (hash) {
691         md = EVP_get_digestbyname(hash);
692         if (!md) {
693             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hash);
694             return ret;
695         }
696     }
697
698     const X509* x = static_cast<const OpenSSLCryptoX509&>(cert).getOpenSSLX509();
699     EVP_PKEY* key = X509_get_pubkey(const_cast<X509*>(x));
700
701     BIO* chain = BIO_new(BIO_s_mem());
702     BIO* b = BIO_new(BIO_f_base64());
703     if (nowrap)
704         BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
705     chain = BIO_push(b, chain);
706     if (md) {
707         b = BIO_new(BIO_f_md());
708         BIO_set_md(b, md);
709         chain = BIO_push(b, chain);
710     }
711     i2d_PUBKEY_bio(chain, key);
712     EVP_PKEY_free(key);
713     BIO_flush(chain);
714     if (md) {
715         char digest[EVP_MAX_MD_SIZE];
716         int len = BIO_gets(chain, digest, EVP_MD_size(md));
717         if (len != EVP_MD_size(md)) {
718             BIO_free_all(chain);
719             return ret;
720         }
721         b = BIO_pop(chain);
722         BIO_free(chain);
723         chain = b;
724         BIO_reset(chain);
725         BIO_write(chain, digest, len);
726         BIO_flush(chain);
727     }
728     BUF_MEM* bptr=nullptr;
729     BIO_get_mem_ptr(chain, &bptr);
730     if (bptr && bptr->length > 0)
731         ret.append(bptr->data, bptr->length);
732     BIO_free_all(chain);
733     return ret;
734 }
735
736 string SecurityHelper::getDEREncoding(const Credential& cred, const char* hash, bool nowrap)
737 {
738     const X509Credential* x509 = dynamic_cast<const X509Credential*>(&cred);
739     if (x509 && !x509->getEntityCertificateChain().empty())
740         return getDEREncoding(*(x509->getEntityCertificateChain().front()), hash, nowrap);
741     else if (cred.getPublicKey())
742         return getDEREncoding(*(cred.getPublicKey()), hash, nowrap);
743     return "";
744 }
745
746 string SecurityHelper::getDEREncoding(const XSECCryptoKey& key, bool hash, bool nowrap)
747 {
748     return getDEREncoding(key, hash ? "SHA1" : nullptr, nowrap);
749 }
750
751 string SecurityHelper::getDEREncoding(const XSECCryptoX509& cert, bool hash, bool nowrap)
752 {
753     return getDEREncoding(cert, hash ? "SHA1" : nullptr, nowrap);
754 }
755
756 string SecurityHelper::getDEREncoding(const Credential& cred, bool hash, bool nowrap)
757 {
758     return getDEREncoding(cred, hash ? "SHA1" : nullptr, nowrap);
759 }
760
761 XSECCryptoKey* SecurityHelper::fromDEREncoding(const char* buf, unsigned long buflen, bool base64)
762 {
763     xsecsize_t x;
764     XMLByte* decoded=nullptr;
765     if (base64) {
766         decoded = xercesc::Base64::decode(reinterpret_cast<const XMLByte*>(buf), &x);
767         if (!decoded) {
768             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("base64 decode failed");
769             return nullptr;
770         }
771     }
772
773     BIO* b = BIO_new_mem_buf((void*)(base64 ? (char*)decoded : buf), (base64 ? x : buflen));
774     EVP_PKEY* pkey = d2i_PUBKEY_bio(b, nullptr);
775     BIO_free(b);
776     if (base64) {
777 #ifdef XMLTOOLING_XERCESC_HAS_XMLBYTE_RELEASE
778         XMLString::release(&decoded);
779 #else
780         XMLString::release((char**)&decoded);
781 #endif
782     }
783
784     if (pkey) {
785         // Now map it to an XSEC wrapper.
786         XSECCryptoKey* ret = nullptr;
787         try {
788             switch (pkey->type) {
789                 case EVP_PKEY_RSA:
790                     ret = new OpenSSLCryptoKeyRSA(pkey);
791                     break;
792
793                 case EVP_PKEY_DSA:
794                     ret = new OpenSSLCryptoKeyDSA(pkey);
795                     break;
796
797 #if defined(XMLTOOLING_XMLSEC_ECC) && defined(XMLTOOLING_OPENSSL_HAVE_EC)
798                 case EVP_PKEY_EC:
799                     ret = new OpenSSLCryptoKeyEC(pkey);
800                     break;
801 #endif
802                 default:
803                     Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("unsupported public key type");
804             }
805         }
806         catch (XSECCryptoException& ex) {
807             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error(ex.getMsg());
808         }
809         EVP_PKEY_free(pkey);
810         return ret;
811     }
812
813     return nullptr;
814 }
815
816 XSECCryptoKey* SecurityHelper::fromDEREncoding(const XMLCh* buf)
817 {
818     xsecsize_t x;
819     XMLByte* decoded = xercesc::Base64::decodeToXMLByte(buf, &x);
820     if (!decoded) {
821         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("base64 decode failed");
822         return nullptr;
823     }
824     XSECCryptoKey* ret = fromDEREncoding((const char*)decoded, x, false);
825 #ifdef XMLTOOLING_XERCESC_HAS_XMLBYTE_RELEASE
826     XMLString::release(&decoded);
827 #else
828     XMLString::release((char**)&decoded);
829 #endif
830     return ret;
831 }