Address certificate object lifetime with wrapper class.
[shibboleth/cpp-xmltooling.git] / xmltooling / signature / impl / FilesystemCredentialResolver.cpp
1 /*\r
2  *  Copyright 2001-2005 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * FilesystemCredentialResolver.cpp\r
19  * \r
20  * Supplies credentials from local files\r
21  */\r
22 \r
23 #include "internal.h"\r
24 #include "signature/KeyResolver.h"\r
25 #include "signature/OpenSSLCredentialResolver.h"\r
26 #include "util/NDC.h"\r
27 #include "util/XMLHelper.h"\r
28 \r
29 #include <sys/types.h>\r
30 #include <sys/stat.h>\r
31 #include <algorithm>\r
32 #include <openssl/pkcs12.h>\r
33 #include <log4cpp/Category.hh>\r
34 #include <xercesc/util/XMLUniDefs.hpp>\r
35 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>\r
36 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>\r
37 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>\r
38 \r
39 using namespace xmlsignature;\r
40 using namespace xmltooling;\r
41 using namespace log4cpp;\r
42 using namespace std;\r
43 \r
44 // OpenSSL password callback...\r
45 static int passwd_callback(char* buf, int len, int verify, void* passwd)\r
46 {\r
47     if(!verify)\r
48     {\r
49         if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))\r
50         {\r
51             strcpy(buf,reinterpret_cast<char*>(passwd));\r
52             return strlen(buf);\r
53         }\r
54     }  \r
55     return 0;\r
56 }\r
57 \r
58 namespace xmlsignature {\r
59     class FilesystemCredentialResolver : public OpenSSLCredentialResolver, public KeyResolver\r
60     {\r
61     public:\r
62         FilesystemCredentialResolver(const DOMElement* e);\r
63         virtual ~FilesystemCredentialResolver();\r
64 \r
65         Lockable* lock() { return this; }\r
66         void unlock() {}\r
67         \r
68         XSECCryptoKey* loadKey();\r
69         \r
70         XSECCryptoKey* getKey() const { return m_key ? m_key->clone() : NULL; }\r
71         const vector<XSECCryptoX509*>& getCertificates() const { return m_xseccerts; }\r
72         void attach(SSL_CTX* ctx) const;\r
73         \r
74         XSECCryptoKey* resolveKey(const KeyInfo* keyInfo) const { return m_key ? m_key->clone() : NULL; }\r
75         XSECCryptoKey* resolveKey(DSIGKeyInfoList* keyInfo) const { return m_key ? m_key->clone() : NULL; }\r
76         vector<XSECCryptoX509*>::size_type resolveCertificates(const KeyInfo* keyInfo, ResolvedCertificates& certs) const {\r
77             accessCertificates(certs).assign(m_xseccerts.begin(), m_xseccerts.end());\r
78             accessOwned(certs) = false;\r
79             return accessCertificates(certs).size();\r
80         }\r
81         vector<XSECCryptoX509*>::size_type resolveCertificates(DSIGKeyInfoList* keyInfo, ResolvedCertificates& certs) const {\r
82             accessCertificates(certs).assign(m_xseccerts.begin(), m_xseccerts.end());\r
83             accessOwned(certs) = false;\r
84             return accessCertificates(certs).size();\r
85         }\r
86         \r
87     private:\r
88         enum format_t { PEM=SSL_FILETYPE_PEM, DER=SSL_FILETYPE_ASN1, _PKCS12, UNKNOWN };\r
89     \r
90         format_t getEncodingFormat(BIO* in) const;\r
91         string formatToString(format_t format) const;\r
92         format_t xmlFormatToFormat(const XMLCh* format_xml) const;\r
93     \r
94         format_t m_keyformat;\r
95         string m_keypath,m_keypass;\r
96         vector<X509*> m_certs;\r
97         vector<XSECCryptoX509*> m_xseccerts;\r
98         XSECCryptoKey* m_key;\r
99     };\r
100 \r
101     CredentialResolver* XMLTOOL_DLLLOCAL FilesystemCredentialResolverFactory(const DOMElement* const & e)\r
102     {\r
103         return new FilesystemCredentialResolver(e);\r
104     }\r
105 \r
106     KeyResolver* XMLTOOL_DLLLOCAL FilesystemKeyResolverFactory(const DOMElement* const & e)\r
107     {\r
108         return new FilesystemCredentialResolver(e);\r
109     }\r
110 };\r
111 \r
112 static const XMLCh CAPath[] =           UNICODE_LITERAL_6(C,A,P,a,t,h);\r
113 static const XMLCh Certificate[] =      UNICODE_LITERAL_11(C,e,r,t,i,f,i,c,a,t,e);\r
114 static const XMLCh format[] =           UNICODE_LITERAL_6(f,o,r,m,a,t);\r
115 static const XMLCh Key[] =              UNICODE_LITERAL_3(K,e,y);\r
116 static const XMLCh password[] =         UNICODE_LITERAL_8(p,a,s,s,w,o,r,d);\r
117 static const XMLCh Path[] =             UNICODE_LITERAL_4(P,a,t,h);\r
118 \r
119 FilesystemCredentialResolver::FilesystemCredentialResolver(const DOMElement* e) : m_key(NULL)\r
120 {\r
121 #ifdef _DEBUG\r
122     NDC ndc("FilesystemCredentialResolver");\r
123 #endif\r
124     Category& log=Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver");\r
125 \r
126     format_t fformat;\r
127     const XMLCh* format_xml=NULL;\r
128     BIO* in = NULL;\r
129     \r
130     // Move to Key\r
131     const DOMElement* root=e;\r
132     e=XMLHelper::getFirstChildElement(root,Key);\r
133     if (e) {\r
134 \r
135         // Get raw format attrib value, but defer processing til later since may need to \r
136         // determine format dynamically, and we need the Path for that.\r
137         format_xml=e->getAttributeNS(NULL,format);\r
138             \r
139         const XMLCh* password_xml=e->getAttributeNS(NULL,password);\r
140         if (password_xml) {\r
141             auto_ptr_char kp(password_xml);\r
142             m_keypass=kp.get();\r
143         }\r
144         \r
145         e=XMLHelper::getFirstChildElement(e,Path);\r
146         if (e && e->hasChildNodes()) {\r
147             const XMLCh* s=e->getFirstChild()->getNodeValue();\r
148             auto_ptr_char kpath(s);\r
149 #ifdef WIN32\r
150             struct _stat stat_buf;\r
151             if (_stat(kpath.get(), &stat_buf) != 0)\r
152 #else\r
153             struct stat stat_buf;\r
154             if (stat(kpath.get(), &stat_buf) != 0)\r
155 #endif\r
156             {\r
157                 log.error("key file (%s) can't be opened", kpath.get());\r
158                 throw XMLSecurityException("FilesystemCredentialResolver can't access key file ($1)",params(1,kpath.get()));\r
159             }\r
160             m_keypath=kpath.get();\r
161         }\r
162         else {\r
163             log.error("Path element missing inside Key element");\r
164             throw XMLSecurityException("FilesystemCredentialResolver can't access key file, no Path element specified.");\r
165         }\r
166 \r
167         // Determine the key encoding format dynamically, if not explicitly specified\r
168         if (format_xml && *format_xml) {\r
169             fformat = xmlFormatToFormat(format_xml);\r
170             if (fformat != UNKNOWN) {\r
171                 m_keyformat = fformat;\r
172             }\r
173             else {\r
174                 auto_ptr_char unknown(format_xml);\r
175                 log.error("configuration specifies unknown key encoding format (%s)", unknown.get());\r
176                 throw XMLSecurityException("FilesystemCredentialResolver configuration contains unknown key encoding format ($1)",params(1,unknown.get()));\r
177             }\r
178         }\r
179         else {\r
180             in=BIO_new(BIO_s_file_internal());\r
181             if (in && BIO_read_filename(in,m_keypath.c_str())>0) {\r
182                 m_keyformat = getEncodingFormat(in);\r
183                 log.debug("key encoding format for (%s) dynamically resolved as (%s)", m_keypath.c_str(), formatToString(m_keyformat).c_str());\r
184             }\r
185             else {\r
186                 log.error("key file (%s) can't be read to determine encoding format", m_keypath.c_str());\r
187                 throw XMLSecurityException("FilesystemCredentialResolver can't read key file ($1) to determine encoding format",params(1,m_keypath.c_str()));\r
188             }\r
189             if (in)\r
190                 BIO_free(in);\r
191             in = NULL;    \r
192         }\r
193         \r
194         // Load the key.\r
195         m_key = loadKey();\r
196     }\r
197         \r
198     // Check for Certificate\r
199     e=XMLHelper::getFirstChildElement(root,Certificate);\r
200     if (!e)\r
201         return;\r
202     auto_ptr_char certpass(e->getAttributeNS(NULL,password));\r
203     \r
204     DOMElement* ep=XMLHelper::getFirstChildElement(e,Path);\r
205     if (!ep || !ep->hasChildNodes()) {\r
206         log.error("Path element missing inside Certificate element or is empty");\r
207         throw XMLSecurityException("FilesystemCredentialResolver can't access certificate file, missing or empty Path element.");\r
208     }\r
209     \r
210     auto_ptr_char certpath(ep->getFirstChild()->getNodeValue());\r
211     format_xml=e->getAttributeNS(NULL,format);\r
212     if (format_xml && *format_xml) {\r
213         fformat = xmlFormatToFormat(format_xml);\r
214         if (fformat == UNKNOWN) {\r
215             auto_ptr_char unknown(format_xml);\r
216             log.error("configuration specifies unknown certificate encoding format (%s)", unknown.get());\r
217             throw XMLSecurityException("FilesystemCredentialResolver configuration contains unknown certificate encoding format ($1)",params(1,unknown.get()));\r
218         }\r
219     }\r
220     \r
221     try {\r
222         X509* x=NULL;\r
223         PKCS12* p12=NULL;\r
224         in=BIO_new(BIO_s_file_internal());\r
225         if (in && BIO_read_filename(in,certpath.get())>0) {\r
226             if (!format_xml || !*format_xml) {\r
227                 // Determine the cert encoding format dynamically, if not explicitly specified\r
228                 fformat = getEncodingFormat(in);\r
229                 log.debug("certificate encoding format for (%s) dynamically resolved as (%s)", certpath.get(), formatToString(fformat).c_str());\r
230             }\r
231 \r
232             switch(fformat) {\r
233                 case PEM:\r
234                     while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get())))\r
235                         m_certs.push_back(x);\r
236                     break;\r
237                                 \r
238                 case DER:\r
239                     x=d2i_X509_bio(in,NULL);\r
240                     if (x)\r
241                         m_certs.push_back(x);\r
242                     else {\r
243                         log_openssl();\r
244                         BIO_free(in);\r
245                         throw XMLSecurityException("FilesystemCredentialResolver unable to load DER certificate from file ($1)",params(1,certpath.get()));\r
246                     }\r
247                     break;\r
248 \r
249                 case _PKCS12:\r
250                     p12=d2i_PKCS12_bio(in,NULL);\r
251                     if (p12) {\r
252                         PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);\r
253                         PKCS12_free(p12);\r
254                     }\r
255                     if (x) {\r
256                         m_certs.push_back(x);\r
257                         x=NULL;\r
258                     } else {\r
259                         log_openssl();\r
260                         BIO_free(in);\r
261                         throw XMLSecurityException("FilesystemCredentialResolver unable to load PKCS12 certificate from file ($1)",params(1,certpath.get()));\r
262                     }\r
263                     break;\r
264             } // end switch\r
265 \r
266         } else {\r
267             log_openssl();\r
268             if (in) {\r
269                 BIO_free(in);\r
270                 in=NULL;\r
271             }\r
272             throw XMLSecurityException("FilesystemCredentialResolver unable to load certificate(s) from file ($1)",params(1,certpath.get()));\r
273         }\r
274         if (in) {\r
275             BIO_free(in);\r
276             in=NULL;\r
277         }\r
278 \r
279         if (m_certs.empty()) {\r
280             throw XMLSecurityException("FilesystemCredentialResolver unable to load any certificate(s)");\r
281         }\r
282 \r
283         // Load any extra CA files.\r
284         DOMElement* extra=XMLHelper::getFirstChildElement(e,CAPath);\r
285         while (extra) {\r
286             if (!extra->hasChildNodes()) {\r
287                 log.warn("skipping empty CAPath element");\r
288                 extra = XMLHelper::getNextSiblingElement(extra,CAPath);\r
289                 continue;\r
290             }\r
291             auto_ptr_char capath(extra->getFirstChild()->getNodeValue());\r
292             x=NULL;\r
293             p12=NULL;\r
294             in=BIO_new(BIO_s_file_internal());\r
295             if (in && BIO_read_filename(in,capath.get())>0) {\r
296                 if (!format_xml || !*format_xml) {\r
297                     // Determine the cert encoding format dynamically, if not explicitly specified\r
298                     fformat = getEncodingFormat(in);\r
299                     log.debug("CA certificate encoding format for (%s) dynamically resolved as (%s)", certpath.get(), formatToString(fformat).c_str());\r
300                 }\r
301 \r
302                 switch (fformat) {\r
303                     case PEM:\r
304                         while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get())))\r
305                             m_certs.push_back(x);\r
306                         break;\r
307 \r
308                     case DER:\r
309                         x=d2i_X509_bio(in,NULL);\r
310                         if (x)\r
311                             m_certs.push_back(x);\r
312                         else {\r
313                             log_openssl();\r
314                             BIO_free(in);\r
315                             throw XMLSecurityException("FilesystemCredentialResolver unable to load DER CA certificate from file ($1)",params(1,capath.get()));\r
316                         }\r
317                         break;\r
318 \r
319                     case _PKCS12:\r
320                         p12 = d2i_PKCS12_bio(in, NULL);\r
321                         if (p12) {\r
322                             PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);\r
323                             PKCS12_free(p12);\r
324                         }\r
325                         if (x) {\r
326                             m_certs.push_back(x);\r
327                             x=NULL;\r
328                         }\r
329                         else {\r
330                             log_openssl();\r
331                             BIO_free(in);\r
332                             throw XMLSecurityException("FilesystemCredentialResolver unable to load PKCS12 CA certificate from file ($1)",params(1,capath.get()));\r
333                         }\r
334                         break;\r
335                 } //end switch\r
336 \r
337                 BIO_free(in);\r
338             }\r
339             else {\r
340                 if (in)\r
341                     BIO_free(in);\r
342                 log_openssl();\r
343                 log.error("CA file (%s) can't be opened", capath.get());\r
344                 throw XMLSecurityException("FilesystemCredentialResolver can't open CA file ($1)",params(1,capath.get()));\r
345             }\r
346             \r
347             extra = XMLHelper::getNextSiblingElement(extra,CAPath);\r
348         }\r
349     }\r
350     catch (XMLToolingException&) {\r
351         for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++)\r
352             X509_free(*j);\r
353         throw;\r
354     }\r
355 \r
356     // Reflect certs over to XSEC form.\r
357     for (vector<X509*>::iterator j=m_certs.begin(); j!=m_certs.end(); j++)\r
358         m_xseccerts.push_back(new OpenSSLCryptoX509(*j));\r
359 }\r
360 \r
361 XSECCryptoKey* FilesystemCredentialResolver::loadKey()\r
362 {\r
363 #ifdef _DEBUG\r
364     NDC ndc("loadKey");\r
365 #endif\r
366 \r
367     // Get a EVP_PKEY.\r
368     EVP_PKEY* pkey=NULL;\r
369     BIO* in=BIO_new(BIO_s_file_internal());\r
370     if (in && BIO_read_filename(in,m_keypath.c_str())>0) {\r
371         switch (m_keyformat) {\r
372             case PEM:\r
373                 pkey=PEM_read_bio_PrivateKey(in, NULL, passwd_callback, const_cast<char*>(m_keypass.c_str()));\r
374                 break;\r
375             \r
376             case DER:\r
377                 pkey=d2i_PrivateKey_bio(in, NULL);\r
378                 break;\r
379                 \r
380             default: {\r
381                 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);\r
382                 if (p12) {\r
383                     PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);\r
384                     PKCS12_free(p12);\r
385                 }\r
386             }\r
387         }\r
388     }\r
389     if (in)\r
390         BIO_free(in);\r
391     \r
392     // Now map it to an XSEC wrapper.\r
393     if (pkey) {\r
394         XSECCryptoKey* ret=NULL;\r
395         switch (pkey->type) {\r
396             case EVP_PKEY_RSA:\r
397                 ret=new OpenSSLCryptoKeyRSA(pkey);\r
398                 break;\r
399                 \r
400             case EVP_PKEY_DSA:\r
401                 ret=new OpenSSLCryptoKeyDSA(pkey);\r
402                 break;\r
403             \r
404             default:\r
405                 Category::getInstance(XMLTOOLING_LOGCAT".CredentialResolver").error("unsupported private key type");\r
406         }\r
407         EVP_PKEY_free(pkey);\r
408         if (ret)\r
409             return ret;\r
410     }\r
411 \r
412     log_openssl();\r
413     throw XMLSecurityException("FilesystemCredentialResolver unable to load private key from file."); \r
414 }\r
415 \r
416 FilesystemCredentialResolver::~FilesystemCredentialResolver()\r
417 {\r
418     delete m_key;\r
419     for_each(m_certs.begin(),m_certs.end(),X509_free);\r
420     for_each(m_xseccerts.begin(),m_xseccerts.end(),xmltooling::cleanup<XSECCryptoX509>());\r
421 }\r
422 \r
423 void FilesystemCredentialResolver::attach(SSL_CTX* ctx) const\r
424 {\r
425 #ifdef _DEBUG\r
426     NDC ndc("attach");\r
427 #endif\r
428     \r
429     // Attach key.\r
430     SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);\r
431     SSL_CTX_set_default_passwd_cb_userdata(ctx, const_cast<char*>(m_keypass.c_str()));\r
432 \r
433     int ret=0;\r
434     switch (m_keyformat) {\r
435         case PEM:\r
436             ret=SSL_CTX_use_PrivateKey_file(ctx, m_keypath.c_str(), m_keyformat);\r
437             break;\r
438             \r
439         case DER:\r
440             ret=SSL_CTX_use_RSAPrivateKey_file(ctx, m_keypath.c_str(), m_keyformat);\r
441             break;\r
442             \r
443         default: {\r
444             BIO* in=BIO_new(BIO_s_file_internal());\r
445             if (in && BIO_read_filename(in,m_keypath.c_str())>0) {\r
446                 EVP_PKEY* pkey=NULL;\r
447                 PKCS12* p12 = d2i_PKCS12_bio(in, NULL);\r
448                 if (p12) {\r
449                     PKCS12_parse(p12, const_cast<char*>(m_keypass.c_str()), &pkey, NULL, NULL);\r
450                     PKCS12_free(p12);\r
451                     if (pkey) {\r
452                         ret=SSL_CTX_use_PrivateKey(ctx, pkey);\r
453                         EVP_PKEY_free(pkey);\r
454                     }\r
455                 }\r
456             }\r
457             if (in)\r
458                 BIO_free(in);\r
459         }\r
460     }\r
461     \r
462     if (ret!=1) {\r
463         log_openssl();\r
464         throw XMLSecurityException("Unable to attach private key to SSL context.");\r
465     }\r
466 \r
467     // Attach certs.\r
468     for (vector<X509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++) {\r
469         if (i==m_certs.begin()) {\r
470             if (SSL_CTX_use_certificate(ctx, *i) != 1) {\r
471                 log_openssl();\r
472                 throw XMLSecurityException("Unable to attach client certificate to SSL context.");\r
473             }\r
474         }\r
475         else {\r
476             // When we add certs, they don't get ref counted, so we need to duplicate them.\r
477             X509* dup = X509_dup(*i);\r
478             if (SSL_CTX_add_extra_chain_cert(ctx, dup) != 1) {\r
479                 X509_free(dup);\r
480                 log_openssl();\r
481                 throw XMLSecurityException("Unable to attach CA certificate to SSL context.");\r
482             }\r
483         }\r
484     }\r
485 }\r
486 \r
487 // Used to determine the encoding format of credentials files\r
488 // dynamically. Supports: PEM, DER, PKCS12.\r
489 FilesystemCredentialResolver::format_t FilesystemCredentialResolver::getEncodingFormat(BIO* in) const\r
490 {\r
491     PKCS12* p12 = NULL;\r
492     format_t format;\r
493 \r
494     const int READSIZE = 1;\r
495     char buf[READSIZE];\r
496     char b1;\r
497     int mark;\r
498 \r
499     try {\r
500         if ( (mark = BIO_tell(in)) < 0 ) \r
501             throw XMLSecurityException("getEncodingFormat: BIO_tell() can't get the file position");\r
502         if ( BIO_read(in, buf, READSIZE) <= 0 ) \r
503             throw XMLSecurityException("getEncodingFormat: BIO_read() can't read from the stream");\r
504         if ( BIO_seek(in, mark) < 0 ) \r
505             throw XMLSecurityException("getEncodingFormat: BIO_seek() can't reset the file position");\r
506     }\r
507     catch (...) {\r
508         log_openssl();\r
509         throw;\r
510     }\r
511 \r
512     b1 = buf[0];\r
513 \r
514     // This is a slight variation of the Java code by Chad La Joie.\r
515     //\r
516     // Check the first byte of the file.  If it's some kind of\r
517     // DER-encoded structure (including PKCS12), it will begin with ASCII 048.\r
518     // Otherwise, assume it's PEM.\r
519     if (b1 !=  48) {\r
520         format = PEM;\r
521     } else {\r
522         // Here we know it's DER-encoded, now try to parse it as a PKCS12\r
523         // ASN.1 structure.  If it fails, must be another kind of DER-encoded\r
524         // key/cert structure.  A little inefficient...but it works.\r
525         if ( (p12=d2i_PKCS12_bio(in,NULL)) == NULL ) {\r
526             format = DER;\r
527         } else {\r
528             format = _PKCS12;\r
529         }\r
530         if (p12)\r
531             PKCS12_free(p12);    \r
532         if ( BIO_seek(in, mark) < 0 ) {\r
533             log_openssl();\r
534             throw XMLSecurityException("getEncodingFormat: BIO_seek() can't reset the file position");\r
535         }\r
536     }\r
537 \r
538     return format;\r
539 }\r
540 \r
541 // Convert key/cert format_t types to a human-meaningful string for debug output\r
542 string FilesystemCredentialResolver::formatToString(format_t format) const\r
543 {\r
544     switch(format) {\r
545         case PEM:\r
546             return "PEM";\r
547         case DER:\r
548             return "DER";\r
549         case _PKCS12:\r
550             return "PKCS12";\r
551         default:\r
552             return "UNKNOWN";\r
553     }\r
554 }\r
555 \r
556 // Convert key/cert raw XML format attribute (XMLCh[]) to format_t type\r
557 FilesystemCredentialResolver::format_t FilesystemCredentialResolver::xmlFormatToFormat(const XMLCh* format_xml) const\r
558 {\r
559     static const XMLCh cPEM[] = UNICODE_LITERAL_3(P,E,M);\r
560     static const XMLCh cDER[] = UNICODE_LITERAL_3(D,E,R);\r
561     static const XMLCh cPKCS12[] = { chLatin_P, chLatin_K, chLatin_C, chLatin_S, chDigit_1, chDigit_2, chNull };\r
562     format_t format;\r
563 \r
564     if (!XMLString::compareString(format_xml,cPEM))\r
565         format=PEM;\r
566     else if (!XMLString::compareString(format_xml,cDER))\r
567         format=DER;\r
568     else if (!XMLString::compareString(format_xml,cPKCS12))\r
569         format=_PKCS12;\r
570     else\r
571         format=UNKNOWN;\r
572 \r
573     return format;\r
574 }\r