Support alternate hash algorithms in helper routines.
[shibboleth/cpp-xmltooling.git] / xmltooling / security / impl / SecurityHelper.cpp
1 /*
2  *  Copyright 2001-2009 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 "security/OpenSSLCryptoX509CRL.h"
26 #include "security/SecurityHelper.h"
27 #include "security/X509Credential.h"
28 #include "util/NDC.h"
29
30 #include <fstream>
31 #include <openssl/pem.h>
32 #include <openssl/pkcs12.h>
33 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
34 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
35 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
36
37 using namespace xmltooling::logging;
38 using namespace xmltooling;
39 using namespace std;
40
41 // OpenSSL password callback...
42 static int passwd_callback(char* buf, int len, int verify, void* passwd)
43 {
44     if(!verify)
45     {
46         if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))
47         {
48             strcpy(buf,reinterpret_cast<char*>(passwd));
49             return strlen(buf);
50         }
51     }
52     return 0;
53 }
54
55 const char* SecurityHelper::guessEncodingFormat(const char* pathname)
56 {
57     const char* format=NULL;
58     BIO* in=BIO_new(BIO_s_file_internal());
59     if (in && BIO_read_filename(in, pathname)>0) {
60         const int READSIZE = 1;
61         char buf[READSIZE];
62         int mark;
63
64         // Examine the first byte.
65         try {
66             if ((mark = BIO_tell(in)) < 0)
67                 throw XMLSecurityException("Error loading file: BIO_tell() can't get the file position.");
68             if (BIO_read(in, buf, READSIZE) <= 0)
69                 throw XMLSecurityException("Error loading file: BIO_read() can't read from the stream.");
70             if (BIO_seek(in, mark) < 0)
71                 throw XMLSecurityException("Error loading file: BIO_seek() can't reset the file position.");
72         }
73         catch (exception&) {
74             log_openssl();
75             BIO_free(in);
76             throw;
77         }
78
79         // Check the first byte of the file.  If it's some kind of DER-encoded structure
80         // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
81         if (buf[0] != 48) {
82             format = "PEM";
83         }
84         else {
85             // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
86             // If it fails, must be another kind of DER-encoded structure.
87             PKCS12* p12;
88             if ((p12=d2i_PKCS12_bio(in, NULL)) == NULL) {
89                 format = "DER";
90             }
91             else {
92                 format = "PKCS12";
93                 PKCS12_free(p12);
94             }
95         }
96     }
97     if (in)
98         BIO_free(in);
99     if (format)
100         return format;
101     throw XMLSecurityException("Unable to determine encoding for file ($1).", params(1,pathname));
102 }
103
104 XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char* format, const char* password)
105 {
106 #ifdef _DEBUG
107     NDC ndc("loadKeyFromFile");
108 #endif
109     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper");
110     log.info("loading private key from file (%s)", pathname);
111
112     // Native objects.
113     PKCS12* p12=NULL;
114     EVP_PKEY* pkey=NULL;
115
116     BIO* in=BIO_new(BIO_s_file_internal());
117     if (in && BIO_read_filename(in, pathname)>0) {
118         // If the format isn't set, try and guess it.
119         if (!format || !*format) {
120             const int READSIZE = 1;
121             char buf[READSIZE];
122             int mark;
123
124             // Examine the first byte.
125             try {
126                 if ((mark = BIO_tell(in)) < 0)
127                     throw XMLSecurityException("Error loading key: BIO_tell() can't get the file position.");
128                 if (BIO_read(in, buf, READSIZE) <= 0)
129                     throw XMLSecurityException("Error loading key: BIO_read() can't read from the stream.");
130                 if (BIO_seek(in, mark) < 0)
131                     throw XMLSecurityException("Error loading key: BIO_seek() can't reset the file position.");
132             }
133             catch (exception&) {
134                 log_openssl();
135                 BIO_free(in);
136                 throw;
137             }
138
139             // Check the first byte of the file.  If it's some kind of DER-encoded structure
140             // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
141             if (buf[0] != 48) {
142                 format = "PEM";
143             }
144             else {
145                 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
146                 // If it fails, must be another kind of DER-encoded structure.
147                 if ((p12=d2i_PKCS12_bio(in, NULL)) == NULL) {
148                     format = "DER";
149                     if (BIO_seek(in, mark) < 0) {
150                         log_openssl();
151                         BIO_free(in);
152                         throw XMLSecurityException("Error loading key: BIO_seek() can't reset the file position.");
153                     }
154                 }
155                 else {
156                     format = "PKCS12";
157                 }
158             }
159             log.debug("key encoding format for (%s) dynamically resolved as (%s)", pathname, format);
160         }
161
162         // The format should be known, so parse accordingly.
163         if (!strcmp(format, "PEM")) {
164             pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback, const_cast<char*>(password));
165         }
166         else if (!strcmp(format, "DER")) {
167             pkey=d2i_PrivateKey_bio(in, NULL);
168         }
169         else if (!strcmp(format, "PKCS12")) {
170             if (!p12)
171                 p12 = d2i_PKCS12_bio(in, NULL);
172             if (p12) {
173                 X509* x=NULL;
174                 PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, NULL);
175                 PKCS12_free(p12);
176                 X509_free(x);
177             }
178         }
179         else {
180             log.error("unknown key encoding format (%s)", format);
181         }
182     }
183     if (in)
184         BIO_free(in);
185
186     // Now map it to an XSEC wrapper.
187     if (pkey) {
188         XSECCryptoKey* ret=NULL;
189         switch (pkey->type) {
190             case EVP_PKEY_RSA:
191                 ret=new OpenSSLCryptoKeyRSA(pkey);
192                 break;
193
194             case EVP_PKEY_DSA:
195                 ret=new OpenSSLCryptoKeyDSA(pkey);
196                 break;
197
198             default:
199                 log.error("unsupported private key type");
200         }
201         EVP_PKEY_free(pkey);
202         if (ret)
203             return ret;
204     }
205
206     log_openssl();
207     throw XMLSecurityException("Unable to load private key from file ($1).", params(1, pathname));
208 }
209
210 vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromFile(
211     vector<XSECCryptoX509*>& certs, const char* pathname, const char* format, const char* password
212     )
213 {
214 #ifdef _DEBUG
215     NDC ndc("loadCertificatesFromFile");
216 #endif
217     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper");
218     log.info("loading certificate(s) from file (%s)", pathname);
219
220     vector<XSECCryptoX509*>::size_type count = certs.size();
221
222     // Native objects.
223     X509* x=NULL;
224     PKCS12* p12=NULL;
225
226     BIO* in=BIO_new(BIO_s_file_internal());
227     if (in && BIO_read_filename(in, pathname)>0) {
228         // If the format isn't set, try and guess it.
229         if (!format || !*format) {
230             const int READSIZE = 1;
231             char buf[READSIZE];
232             int mark;
233
234             // Examine the first byte.
235             try {
236                 if ((mark = BIO_tell(in)) < 0)
237                     throw XMLSecurityException("Error loading certificate: BIO_tell() can't get the file position.");
238                 if (BIO_read(in, buf, READSIZE) <= 0)
239                     throw XMLSecurityException("Error loading certificate: BIO_read() can't read from the stream.");
240                 if (BIO_seek(in, mark) < 0)
241                     throw XMLSecurityException("Error loading certificate: BIO_seek() can't reset the file position.");
242             }
243             catch (exception&) {
244                 log_openssl();
245                 BIO_free(in);
246                 throw;
247             }
248
249             // Check the first byte of the file.  If it's some kind of DER-encoded structure
250             // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
251             if (buf[0] != 48) {
252                 format = "PEM";
253             }
254             else {
255                 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
256                 // If it fails, must be another kind of DER-encoded structure.
257                 if ((p12=d2i_PKCS12_bio(in, NULL)) == NULL) {
258                     format = "DER";
259                     if (BIO_seek(in, mark) < 0) {
260                         log_openssl();
261                         BIO_free(in);
262                         throw XMLSecurityException("Error loading certificate: BIO_seek() can't reset the file position.");
263                     }
264                 }
265                 else {
266                     format = "PKCS12";
267                 }
268             }
269         }
270
271         // The format should be known, so parse accordingly.
272         if (!strcmp(format, "PEM")) {
273             while (x=PEM_read_bio_X509(in, NULL, NULL, NULL)) {
274                 certs.push_back(new OpenSSLCryptoX509(x));
275                 X509_free(x);
276             }
277         }
278         else if (!strcmp(format, "DER")) {
279             x=d2i_X509_bio(in, NULL);
280             if (x) {
281                 certs.push_back(new OpenSSLCryptoX509(x));
282                 X509_free(x);
283             }
284         }
285         else if (!strcmp(format, "PKCS12")) {
286             if (!p12)
287                 p12 = d2i_PKCS12_bio(in, NULL);
288             if (p12) {
289                 EVP_PKEY* pkey=NULL;
290                 STACK_OF(X509)* CAstack = sk_X509_new_null();
291                 PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, &CAstack);
292                 PKCS12_free(p12);
293                 EVP_PKEY_free(pkey);
294                 if (x) {
295                     certs.push_back(new OpenSSLCryptoX509(x));
296                     X509_free(x);
297                 }
298                 x = sk_X509_pop(CAstack);
299                 while (x) {
300                     certs.push_back(new OpenSSLCryptoX509(x));
301                     X509_free(x);
302                     x = sk_X509_pop(CAstack);
303                 }
304                 sk_X509_free(CAstack);
305             }
306         }
307     }
308     if (in)
309         BIO_free(in);
310
311     if (certs.size() == count) {
312         log_openssl();
313         throw XMLSecurityException("Unable to load certificate(s) from file ($1).", params(1, pathname));
314     }
315
316     return certs.size();
317 }
318
319 vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromFile(
320     vector<XSECCryptoX509CRL*>& crls, const char* pathname, const char* format
321     )
322 {
323 #ifdef _DEBUG
324     NDC ndc("loadCRLsFromFile");
325 #endif
326     Category& log = Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper");
327     log.info("loading CRL(s) from file (%s)", pathname);
328
329     vector<XSECCryptoX509CRL*>::size_type count = crls.size();
330
331     BIO* in=BIO_new(BIO_s_file_internal());
332     if (in && BIO_read_filename(in, pathname)>0) {
333         // If the format isn't set, try and guess it.
334         if (!format || !*format) {
335             const int READSIZE = 1;
336             char buf[READSIZE];
337             int mark;
338
339             // Examine the first byte.
340             try {
341                 if ((mark = BIO_tell(in)) < 0)
342                     throw XMLSecurityException("Error loading CRL: BIO_tell() can't get the file position.");
343                 if (BIO_read(in, buf, READSIZE) <= 0)
344                     throw XMLSecurityException("Error loading CRL: BIO_read() can't read from the stream.");
345                 if (BIO_seek(in, mark) < 0)
346                     throw XMLSecurityException("Error loading CRL: BIO_seek() can't reset the file position.");
347             }
348             catch (exception&) {
349                 log_openssl();
350                 BIO_free(in);
351                 throw;
352             }
353
354             // Check the first byte of the file.  If it's some kind of DER-encoded structure
355             // it will begin with ASCII 048. Otherwise, assume it's PEM.
356             if (buf[0] != 48) {
357                 format = "PEM";
358             }
359             else {
360                 format = "DER";
361             }
362             log.debug("CRL encoding format for (%s) dynamically resolved as (%s)", pathname, format);
363         }
364
365         X509_CRL* crl=NULL;
366         if (!strcmp(format, "PEM")) {
367             while (crl=PEM_read_bio_X509_CRL(in, NULL, NULL, NULL)) {
368                 crls.push_back(new OpenSSLCryptoX509CRL(crl));
369                 X509_CRL_free(crl);
370             }
371         }
372         else if (!strcmp(format, "DER")) {
373             crl=d2i_X509_CRL_bio(in, NULL);
374             if (crl) {
375                 crls.push_back(new OpenSSLCryptoX509CRL(crl));
376                 X509_CRL_free(crl);
377             }
378         }
379         else {
380             log.error("unknown CRL encoding format (%s)", format);
381         }
382     }
383     if (in)
384         BIO_free(in);
385
386     if (crls.size() == count) {
387         log_openssl();
388         throw XMLSecurityException("Unable to load CRL(s) from file ($1).", params(1, pathname));
389     }
390
391     return crls.size();
392 }
393
394 XSECCryptoKey* SecurityHelper::loadKeyFromURL(SOAPTransport& transport, const char* backing, const char* format, const char* password)
395 {
396     // Fetch the data.
397     transport.send();
398     istream& msg = transport.receive();
399
400     // Dump to output file.
401     ofstream out(backing, fstream::trunc|fstream::binary);
402     out << msg.rdbuf();
403     out.close();
404
405     return loadKeyFromFile(backing, format, password);
406 }
407
408 vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromURL(
409     vector<XSECCryptoX509*>& certs, SOAPTransport& transport, const char* backing, const char* format, const char* password
410     )
411 {
412     transport.send();
413     istream& msg = transport.receive();
414
415     // Dump to output file.
416     ofstream out(backing, fstream::trunc|fstream::binary);
417     out << msg.rdbuf();
418     out.close();
419
420     return loadCertificatesFromFile(certs, backing, format, password);
421 }
422
423 vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromURL(
424     vector<XSECCryptoX509CRL*>& crls, SOAPTransport& transport, const char* backing, const char* format
425     )
426 {
427     // Fetch the data.
428     transport.send();
429     istream& msg = transport.receive();
430
431     // Dump to output file.
432     ofstream out(backing, fstream::trunc|fstream::binary);
433     out << msg.rdbuf();
434     out.close();
435
436     return loadCRLsFromFile(crls, backing, format);
437 }
438
439 bool SecurityHelper::matches(const XSECCryptoKey& key1, const XSECCryptoKey& key2)
440 {
441     if (key1.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL ||
442         key2.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
443         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("comparison of non-OpenSSL keys not supported");
444         return false;
445     }
446
447     // If one key is public or both, just compare the public key half.
448     if (key1.getKeyType()==XSECCryptoKey::KEY_RSA_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_RSA_PAIR) {
449         if (key2.getKeyType()!=XSECCryptoKey::KEY_RSA_PUBLIC && key2.getKeyType()!=XSECCryptoKey::KEY_RSA_PAIR)
450             return false;
451         const RSA* rsa1 = static_cast<const OpenSSLCryptoKeyRSA&>(key1).getOpenSSLRSA();
452         const RSA* rsa2 = static_cast<const OpenSSLCryptoKeyRSA&>(key2).getOpenSSLRSA();
453         return (rsa1 && rsa2 && BN_cmp(rsa1->n,rsa2->n) == 0 && BN_cmp(rsa1->e,rsa2->e) == 0);
454     }
455
456     // For a private key, compare the private half.
457     if (key1.getKeyType()==XSECCryptoKey::KEY_RSA_PRIVATE) {
458         if (key2.getKeyType()!=XSECCryptoKey::KEY_RSA_PRIVATE && key2.getKeyType()!=XSECCryptoKey::KEY_RSA_PAIR)
459             return false;
460         const RSA* rsa1 = static_cast<const OpenSSLCryptoKeyRSA&>(key1).getOpenSSLRSA();
461         const RSA* rsa2 = static_cast<const OpenSSLCryptoKeyRSA&>(key2).getOpenSSLRSA();
462         return (rsa1 && rsa2 && BN_cmp(rsa1->n,rsa2->n) == 0 && BN_cmp(rsa1->d,rsa2->d) == 0);
463     }
464
465     // If one key is public or both, just compare the public key half.
466     if (key1.getKeyType()==XSECCryptoKey::KEY_DSA_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_DSA_PAIR) {
467         if (key2.getKeyType()!=XSECCryptoKey::KEY_DSA_PUBLIC && key2.getKeyType()!=XSECCryptoKey::KEY_DSA_PAIR)
468             return false;
469         const DSA* dsa1 = static_cast<const OpenSSLCryptoKeyDSA&>(key1).getOpenSSLDSA();
470         const DSA* dsa2 = static_cast<const OpenSSLCryptoKeyDSA&>(key2).getOpenSSLDSA();
471         return (dsa1 && dsa2 && BN_cmp(dsa1->pub_key,dsa2->pub_key) == 0);
472     }
473
474     // For a private key, compare the private half.
475     if (key1.getKeyType()==XSECCryptoKey::KEY_DSA_PRIVATE) {
476         if (key2.getKeyType()!=XSECCryptoKey::KEY_DSA_PRIVATE && key2.getKeyType()!=XSECCryptoKey::KEY_DSA_PAIR)
477             return false;
478         const DSA* dsa1 = static_cast<const OpenSSLCryptoKeyDSA&>(key1).getOpenSSLDSA();
479         const DSA* dsa2 = static_cast<const OpenSSLCryptoKeyDSA&>(key2).getOpenSSLDSA();
480         return (dsa1 && dsa2 && BN_cmp(dsa1->priv_key,dsa2->priv_key) == 0);
481     }
482
483     Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("unsupported key type for comparison");
484     return false;
485 }
486
487 string SecurityHelper::getDEREncoding(const XSECCryptoKey& key, bool hash, bool nowrap, const char* hashAlg)
488 {
489     string ret;
490
491     if (key.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
492         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("encoding of non-OpenSSL keys not supported");
493         return ret;
494     }
495
496     if (key.getKeyType() == XSECCryptoKey::KEY_RSA_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_RSA_PAIR) {
497         const RSA* rsa = static_cast<const OpenSSLCryptoKeyRSA&>(key).getOpenSSLRSA();
498         if (!rsa) {
499             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("key was not populated");
500             return ret;
501         }
502         const EVP_MD* md=NULL;
503         if (hash && hashAlg) {
504             md = EVP_get_digestbyname(hashAlg);
505             if (!md) {
506                 Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hashAlg);
507                 return ret;
508             }
509         }
510         BIO* chain = BIO_new(BIO_s_mem());
511         BIO* b = BIO_new(BIO_f_base64());
512         if (nowrap)
513             BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
514         chain = BIO_push(b, chain);
515         if (md) {
516             b = BIO_new(BIO_f_md());
517             BIO_set_md(b, md);
518             chain = BIO_push(b, chain);
519         }
520         i2d_RSA_PUBKEY_bio(chain, const_cast<RSA*>(rsa));
521         BIO_flush(chain);
522         if (md) {
523             char digest[EVP_MAX_MD_SIZE];
524             int len = BIO_gets(chain, digest, EVP_MD_size(md));
525             if (len != EVP_MD_size(md)) {
526                 BIO_free_all(chain);
527                 return ret;
528             }
529             b = BIO_pop(chain);
530             BIO_free(chain);
531             chain = b;
532             BIO_reset(chain);
533             BIO_write(chain, digest, len);
534             BIO_flush(chain);
535         }
536         BUF_MEM* bptr=NULL;
537         BIO_get_mem_ptr(chain, &bptr);
538         if (bptr && bptr->length > 0)
539             ret.append(bptr->data, bptr->length);
540         BIO_free_all(chain);
541     }
542     else if (key.getKeyType() == XSECCryptoKey::KEY_DSA_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_DSA_PAIR) {
543         const DSA* dsa = static_cast<const OpenSSLCryptoKeyDSA&>(key).getOpenSSLDSA();
544         if (!dsa) {
545             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("key was not populated");
546             return ret;
547         }
548         const EVP_MD* md=NULL;
549         if (hash && hashAlg) {
550             md = EVP_get_digestbyname(hashAlg);
551             if (!md) {
552                 Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hashAlg);
553                 return ret;
554             }
555         }
556         BIO* chain = BIO_new(BIO_s_mem());
557         BIO* b = BIO_new(BIO_f_base64());
558         if (nowrap)
559             BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
560         chain = BIO_push(b, chain);
561         if (md) {
562             b = BIO_new(BIO_f_md());
563             BIO_set_md(b, md);
564             chain = BIO_push(b, chain);
565         }
566         i2d_DSA_PUBKEY_bio(chain, const_cast<DSA*>(dsa));
567         BIO_flush(chain);
568         if (md) {
569             char digest[EVP_MAX_MD_SIZE];
570             int len = BIO_gets(chain, digest, EVP_MD_size(md));
571             if (len != EVP_MD_size(md)) {
572                 BIO_free_all(chain);
573                 return ret;
574             }
575             b = BIO_pop(chain);
576             BIO_free(chain);
577             chain = b;
578             BIO_reset(chain);
579             BIO_write(chain, digest, len);
580             BIO_flush(chain);
581         }
582         BUF_MEM* bptr=NULL;
583         BIO_get_mem_ptr(chain, &bptr);
584         if (bptr && bptr->length > 0)
585             ret.append(bptr->data, bptr->length);
586         BIO_free_all(chain);
587     }
588     else {
589         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("encoding of non-RSA/DSA public keys not supported");
590     }
591     return ret;
592 }
593
594 string SecurityHelper::getDEREncoding(const XSECCryptoX509& cert, bool hash, bool nowrap, const char* hashAlg)
595 {
596     string ret;
597
598     if (cert.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
599         Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").warn("encoding of non-OpenSSL keys not supported");
600         return ret;
601     }
602
603     const EVP_MD* md=NULL;
604     if (hash && hashAlg) {
605         md = EVP_get_digestbyname(hashAlg);
606         if (!md) {
607             Category::getInstance(XMLTOOLING_LOGCAT".SecurityHelper").error("hash algorithm (%s) not available", hashAlg);
608             return ret;
609         }
610     }
611
612     const X509* x = static_cast<const OpenSSLCryptoX509&>(cert).getOpenSSLX509();
613     EVP_PKEY* key = X509_get_pubkey(const_cast<X509*>(x));
614
615     BIO* chain = BIO_new(BIO_s_mem());
616     BIO* b = BIO_new(BIO_f_base64());
617     if (nowrap)
618         BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
619     chain = BIO_push(b, chain);
620     if (md) {
621         b = BIO_new(BIO_f_md());
622         BIO_set_md(b, md);
623         chain = BIO_push(b, chain);
624     }
625     i2d_PUBKEY_bio(chain, key);
626     EVP_PKEY_free(key);
627     BIO_flush(chain);
628     if (md) {
629         char digest[EVP_MAX_MD_SIZE];
630         int len = BIO_gets(chain, digest, EVP_MD_size(md));
631         if (len != EVP_MD_size(md)) {
632             BIO_free_all(chain);
633             return ret;
634         }
635         b = BIO_pop(chain);
636         BIO_free(chain);
637         chain = b;
638         BIO_reset(chain);
639         BIO_write(chain, digest, len);
640         BIO_flush(chain);
641     }
642     BUF_MEM* bptr=NULL;
643     BIO_get_mem_ptr(chain, &bptr);
644     if (bptr && bptr->length > 0)
645         ret.append(bptr->data, bptr->length);
646     BIO_free_all(chain);
647     return ret;
648 }
649
650 string SecurityHelper::getDEREncoding(const Credential& cred, bool hash, bool nowrap, const char* hashAlg)
651 {
652     const X509Credential* x509 = dynamic_cast<const X509Credential*>(&cred);
653     if (x509 && !x509->getEntityCertificateChain().empty())
654         return getDEREncoding(*(x509->getEntityCertificateChain().front()), hash, nowrap, hashAlg);
655     else if (cred.getPublicKey())
656         return getDEREncoding(*(cred.getPublicKey()), hash, nowrap, hashAlg);
657     return "";
658 }