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