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