Next integration phase, metadata and trust conversion.
[shibboleth/cpp-sp.git] / xmlproviders / CredResolvers.cpp
index 4583c86..2c98ded 100644 (file)
@@ -1,50 +1,17 @@
-/* 
- * The Shibboleth License, Version 1. 
- * Copyright (c) 2002 
- * University Corporation for Advanced Internet Development, Inc. 
- * All rights reserved
+/*
+ *  Copyright 2001-2005 Internet2
  * 
- * 
- * Redistribution and use in source and binary forms, with or without 
- * modification, are permitted provided that the following conditions are met:
- * 
- * Redistributions of source code must retain the above copyright notice, this 
- * list of conditions and the following disclaimer.
- * 
- * Redistributions in binary form must reproduce the above copyright notice, 
- * this list of conditions and the following disclaimer in the documentation 
- * and/or other materials provided with the distribution, if any, must include 
- * the following acknowledgment: "This product includes software developed by 
- * the University Corporation for Advanced Internet Development 
- * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement 
- * may appear in the software itself, if and wherever such third-party 
- * acknowledgments normally appear.
- * 
- * Neither the name of Shibboleth nor the names of its contributors, nor 
- * Internet2, nor the University Corporation for Advanced Internet Development, 
- * Inc., nor UCAID may be used to endorse or promote products derived from this 
- * software without specific prior written permission. For written permission, 
- * please contact shibboleth@shibboleth.org
- * 
- * Products derived from this software may not be called Shibboleth, Internet2, 
- * UCAID, or the University Corporation for Advanced Internet Development, nor 
- * may Shibboleth appear in their name, without prior written permission of the 
- * University Corporation for Advanced Internet Development.
- * 
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
- * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
- * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK 
- * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. 
- * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY 
- * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, 
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 /* CredResolvers.cpp - implementations of the ICredResolver interface
 
 #include "internal.h"
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <algorithm>
 #include <openssl/pkcs12.h>
 #include <log4cpp/Category.hh>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
+#include <xmltooling/util/NDC.h>
 
-using namespace saml;
 using namespace shibboleth;
+using namespace xmltooling;
 using namespace log4cpp;
 using namespace std;
 
 // OpenSSL password callback...
-int passwd_callback(char* buf, int len, int verify, void* passwd)
+static int passwd_callback(char* buf, int len, int verify, void* passwd)
 {
     if(!verify)
     {
@@ -94,131 +65,160 @@ public:
     virtual saml::Iterator<XSECCryptoX509*> getCertificates() const { return m_xseccerts; }
     virtual void dump(FILE* f) const;
     
-protected:
-    enum format_t { DER=SSL_FILETYPE_ASN1, PEM=SSL_FILETYPE_PEM, _PKCS12 };
+private:
+    enum format_t { PEM=SSL_FILETYPE_PEM, DER=SSL_FILETYPE_ASN1, _PKCS12, UNKNOWN };
+
+    format_t getEncodingFormat(BIO* in) const;
+    string formatToString(format_t format) const;
+    format_t xmlFormatToFormat(const XMLCh* format_xml) const;
+
     format_t m_keyformat;
     string m_keypath,m_keypass;
     vector<X509*> m_certs;
     vector<XSECCryptoX509*> m_xseccerts;
 };
 
-IPlugIn* FileCredResolverFactory(const DOMElement* e)
+saml::IPlugIn* FileCredResolverFactory(const DOMElement* e)
 {
     return new FileResolver(e);
 }
 
 FileResolver::FileResolver(const DOMElement* e)
 {
-    saml::NDC ndc("FileResolver");
-    static const XMLCh cPEM[] = { chLatin_P, chLatin_E, chLatin_M, chNull };
-    static const XMLCh cDER[] = { chLatin_D, chLatin_E, chLatin_R, chNull };
+#ifdef _DEBUG
+    xmltooling::NDC ndc("FileResolver");
+#endif
+    Category& log=Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers");
+
+    format_t format;
+    BIO* in = NULL;
     
     // Move to Key
-    e=saml::XML::getFirstChildElement(e);
-    const XMLCh* format=e->getAttributeNS(NULL,SHIB_L(format));
-    if (!format || !*format || !XMLString::compareString(format,cPEM))
-        m_keyformat=PEM;
-    else if (!XMLString::compareString(format,cDER))
-        m_keyformat=DER;
-    else
-        m_keyformat=_PKCS12;
+    const DOMElement* root=e;
+    e=saml::XML::getFirstChildElement(root,::XML::CREDS_NS,SHIB_L(Key));
+    if (e) {
+
+        // Get raw format attrib value, but defer processing til later since may need to 
+        // determine format dynamically, and we need the Path for that.
+        const XMLCh* format_xml=e->getAttributeNS(NULL,SHIB_L(format));
+            
+        const XMLCh* password=e->getAttributeNS(NULL,SHIB_L(password));
+        if (password) {
+            auto_ptr_char kp(password);
+            m_keypass=kp.get();
+        }
         
-    const XMLCh* password=e->getAttributeNS(NULL,SHIB_L(password));
-    if (password) {
-        auto_ptr_char kp(password);
-        m_keypass=kp.get();
-    }
-    
-    const XMLCh* s=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(Path))->getFirstChild()->getNodeValue();
-    auto_ptr_char kpath(s);
-    
+        e=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(Path));
+        if (e && e->hasChildNodes()) {
+            const XMLCh* s=e->getFirstChild()->getNodeValue();
+            auto_ptr_char kpath(s);
 #ifdef WIN32
-    struct _stat stat_buf;
-    if (_stat(kpath.get(), &stat_buf) != 0)
+            struct _stat stat_buf;
+            if (_stat(kpath.get(), &stat_buf) != 0)
 #else
-    struct stat stat_buf;
-    if (stat(kpath.get(), &stat_buf) != 0)
+            struct stat stat_buf;
+            if (stat(kpath.get(), &stat_buf) != 0)
 #endif
-    {
-        Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("key file '%s' can't be opened", kpath.get());
-        throw CredentialException("FileResolver() can't access key file");
-    }
-    m_keypath=kpath.get();
-    
-    // Check for Certificate
-    e=saml::XML::getNextSiblingElement(e);
-    password=e->getAttributeNS(NULL,SHIB_L(password));
-    auto_ptr_char certpass(password);
-    s=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(Path))->getFirstChild()->getNodeValue();
-    auto_ptr_char certpath(s);
+            {
+                log.error("key file (%s) can't be opened", kpath.get());
+                throw IOException("FileResolver can't access key file ($1)",params(1,kpath.get()));
+            }
+            m_keypath=kpath.get();
+        }
+        else {
+            log.error("Path element missing inside Key element");
+            throw IOException("FileResolver can't access key file, no Path element specified.");
+        }
 
-    try {
-        X509* x=NULL;
-        BIO* in=BIO_new(BIO_s_file_internal());
-        if (in && BIO_read_filename(in,certpath.get())>0) {
-            format=e->getAttributeNS(NULL,SHIB_L(format));
-            if (!format || !*format || !XMLString::compareString(format,cPEM)) {
-                while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get()))) {
-                    m_certs.push_back(x);
+        // Determine the key encoding format dynamically, if not explicitly specified
+        try {
+            if (format_xml && *format_xml) {
+                format = xmlFormatToFormat(format_xml);
+                if (format != UNKNOWN) {
+                    m_keyformat = format;
                 }
-            }
-            else if (!XMLString::compareString(format,cDER)) {
-                x=d2i_X509_bio(in,NULL);
-                if (x)
-                    m_certs.push_back(x);
                 else {
-                    log_openssl();
-                    BIO_free(in);
-                    throw CredentialException("FileResolver() unable to load DER certificate from file");
+                    auto_ptr_char unknown(format_xml);
+                    log.error("Configuration specifies unknown key encoding format (%s)", unknown.get());
+                    throw IOException("FileResolver configuration contains unknown key encoding format ($1)",params(1,unknown.get()));
                 }
             }
             else {
-                PKCS12* p12=d2i_PKCS12_bio(in,NULL);
-                if (p12) {
-                    PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
-                    PKCS12_free(p12);
-                }
-                if (x) {
-                    m_certs.push_back(x);
-                    x=NULL;
+                in=BIO_new(BIO_s_file_internal());
+                if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
+                    m_keyformat = getEncodingFormat(in);
+                    log.debug("Key encoding format for (%s) dynamically resolved as (%s)", m_keypath.c_str(), formatToString(m_keyformat).c_str());
                 }
                 else {
-                    log_openssl();
-                    BIO_free(in);
-                    throw CredentialException("FileResolver() unable to load PKCS12 certificate from file");
+                    log.error("Key file (%s) can't be read to determine encoding format", m_keypath.c_str());
+                    throw IOException("FileResolver can't read key file ($1) to determine encoding format",params(1,m_keypath.c_str()));
                 }
+                if (in)
+                    BIO_free(in);
+                in = NULL;    
             }
         }
-        if (in) {
-            BIO_free(in);
-            in=NULL;
+        catch (...) {
+            log.error("Error determining key encoding format");
+            throw;
         }
 
-        // Load any extra CA files.
-        DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::CREDS_NS,SHIB_L(CAPath));
-        for (int i=0; nlist && i<nlist->getLength(); i++) {
-            s=static_cast<DOMElement*>(nlist->item(i))->getFirstChild()->getNodeValue();
-            auto_ptr_char capath(s);
-            x=NULL;
-            in=BIO_new(BIO_s_file_internal());
-            if (in && BIO_read_filename(in,capath.get())>0) {
-                if (!format || !*format || !XMLString::compareString(format,cPEM)) {
+    }
+        
+    // Check for Certificate
+    e=saml::XML::getFirstChildElement(root,::XML::CREDS_NS,SHIB_L(Certificate));
+    if (!e)
+        return;
+    auto_ptr_char certpass(e->getAttributeNS(NULL,SHIB_L(password)));
+    
+    DOMElement* ep=saml::XML::getFirstChildElement(e,::XML::CREDS_NS,SHIB_L(Path));
+    if (!ep || !ep->hasChildNodes()) {
+        log.error("Path element missing inside Certificate element");
+        throw IOException("FileResolver can't access certificate file, missing Path element.");
+    }
+    
+    auto_ptr_char certpath(ep->getFirstChild()->getNodeValue());
+    const XMLCh* format_xml=e->getAttributeNS(NULL,SHIB_L(format));
+    if (format_xml && *format_xml) {
+        format = xmlFormatToFormat(format_xml);
+        if (format == UNKNOWN) {
+            auto_ptr_char unknown(format_xml);
+            log.error("Configuration specifies unknown certificate encoding format (%s)", unknown.get());
+            throw IOException("FileResolver configuration contains unknown certificate encoding format ($1)",params(1,unknown.get()));
+        }
+    }
+    
+    try {
+        X509* x=NULL;
+        PKCS12* p12=NULL;
+        in=BIO_new(BIO_s_file_internal());
+        if (in && BIO_read_filename(in,certpath.get())>0) {
+            if (!format_xml || !*format_xml) {
+                // Determine the cert encoding format dynamically, if not explicitly specified
+                format = getEncodingFormat(in);
+                log.debug("Cert encoding format for (%s) dynamically resolved as (%s)", certpath.get(), formatToString(format).c_str());
+            }
+
+            switch(format) {
+                case PEM:
                     while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get()))) {
-                        m_certs.push_back(x);
+                           m_certs.push_back(x);
                     }
-                }
-                else if (!XMLString::compareString(format,cDER)) {
+                    break;
+                                
+                case DER:
                     x=d2i_X509_bio(in,NULL);
                     if (x)
                         m_certs.push_back(x);
                     else {
                         log_openssl();
                         BIO_free(in);
-                        throw CredentialException("FileResolver() unable to load DER CA certificate from file");
+                        throw IOException("FileResolver unable to load DER certificate from file ($1)",params(1,certpath.get()));
                     }
-                }
-                else {
-                    PKCS12* p12 = d2i_PKCS12_bio(in, NULL);
+                    break;
+
+                case _PKCS12:
+                    p12=d2i_PKCS12_bio(in,NULL);
                     if (p12) {
                         PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
                         PKCS12_free(p12);
@@ -226,21 +226,91 @@ FileResolver::FileResolver(const DOMElement* e)
                     if (x) {
                         m_certs.push_back(x);
                         x=NULL;
-                    }
-                    else {
+                    } else {
                         log_openssl();
                         BIO_free(in);
-                        throw CredentialException("FileResolver() unable to load PKCS12 CA certificate from file");
+                        throw IOException("FileResolver unable to load PKCS12 certificate from file ($1)",params(1,certpath.get()));
                     }
-                }
+                    break;
+            } // end switch
+
+        } else {
+            log_openssl();
+            if (in) {
                 BIO_free(in);
+                in=NULL;
             }
-            else {
+            throw IOException("FileResolver unable to load certificate(s) from file ($1)",params(1,certpath.get()));
+        }
+        if (in) {
+            BIO_free(in);
+            in=NULL;
+        }
+
+        if (m_certs.empty()) {
+            throw IOException("FileResolver unable to load any certificate(s)");
+        }
+
+        // Load any extra CA files.
+        DOMNodeList* nlist=e->getElementsByTagNameNS(::XML::CREDS_NS,SHIB_L(CAPath));
+        for (unsigned int i=0; nlist && i<nlist->getLength(); i++) {
+            if (!nlist->item(i)->hasChildNodes())
+                continue;
+            auto_ptr_char capath(static_cast<DOMElement*>(nlist->item(i))->getFirstChild()->getNodeValue());
+            x=NULL;
+            p12=NULL;
+            in=BIO_new(BIO_s_file_internal());
+            if (in && BIO_read_filename(in,capath.get())>0) {
+                if (!format_xml || !*format_xml) {
+                    // Determine the cert encoding format dynamically, if not explicitly specified
+                    format = getEncodingFormat(in);
+                    log.debug("Cert encoding format for (%s) dynamically resolved as (%s)", certpath.get(), formatToString(format).c_str());
+                }
+
+                switch (format)
+                {
+                    case PEM:
+                        while (x=PEM_read_bio_X509(in,NULL,passwd_callback,const_cast<char*>(certpass.get()))) {
+                            m_certs.push_back(x);
+                        }
+                        break;
+
+                    case DER:
+                        x=d2i_X509_bio(in,NULL);
+                        if (x)
+                            m_certs.push_back(x);
+                        else {
+                            log_openssl();
+                            BIO_free(in);
+                            throw IOException("FileResolver unable to load DER CA certificate from file ($1)",params(1,capath.get()));
+                        }
+                        break;
+
+                    case _PKCS12:
+                        p12 = d2i_PKCS12_bio(in, NULL);
+                        if (p12) {
+                            PKCS12_parse(p12, certpass.get(), NULL, &x, NULL);
+                            PKCS12_free(p12);
+                        }
+                        if (x) {
+                            m_certs.push_back(x);
+                            x=NULL;
+                        } else {
+                            log_openssl();
+                            BIO_free(in);
+                            throw IOException("FileResolver unable to load PKCS12 CA certificate from file ($1)",params(1,capath.get()));
+                        }
+                        break;
+                } //end switch
+
+                BIO_free(in);
+
+            } else {
                 if (in)
                     BIO_free(in);
                 log_openssl();
-                Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("CA file '%s' can't be opened", capath.get());
-                throw CredentialException("FileResolver() can't open CA file");
+                log.error("CA file (%s) can't be opened", capath.get());
+                throw IOException("FileResolver can't open CA file ($1)",params(1,capath.get()));
             }
         }
     }
@@ -257,15 +327,15 @@ FileResolver::FileResolver(const DOMElement* e)
 
 FileResolver::~FileResolver()
 {
-    for (vector<X509*>::iterator i=m_certs.begin(); i!=m_certs.end(); i++)
-        X509_free(*i);
-    for (vector<XSECCryptoX509*>::iterator j=m_xseccerts.begin(); j!=m_xseccerts.end(); j++)
-        delete (*j);
+    for_each(m_certs.begin(),m_certs.end(),X509_free);
+    for_each(m_xseccerts.begin(),m_xseccerts.end(),xmltooling::cleanup<XSECCryptoX509>());
 }
 
 void FileResolver::attach(void* ctx) const
 {
-    saml::NDC ndc("FileResolver");
+#ifdef _DEBUG
+    saml::NDC ndc("attach");
+#endif
     
     SSL_CTX* ssl_ctx=reinterpret_cast<SSL_CTX*>(ctx);
 
@@ -274,8 +344,7 @@ void FileResolver::attach(void* ctx) const
     SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, const_cast<char*>(m_keypass.c_str()));
 
     int ret=0;
-    switch (m_keyformat)
-    {
+    switch (m_keyformat) {
         case PEM:
             ret=SSL_CTX_use_PrivateKey_file(ssl_ctx, m_keypath.c_str(), m_keyformat);
             break;
@@ -305,24 +374,24 @@ void FileResolver::attach(void* ctx) const
     
     if (ret!=1) {
         log_openssl();
-        throw CredentialException("FileResolver::attach() unable to set private key");
+        throw IOException("Unable to attach private key to SSL context");
     }
 
     // Attach certs.
     for (vector<X509*>::const_iterator i=m_certs.begin(); i!=m_certs.end(); i++) {
         if (i==m_certs.begin()) {
-            if (SSL_CTX_use_certificate(ssl_ctx, *i)!=1) {
+            if (SSL_CTX_use_certificate(ssl_ctx, *i) != 1) {
                 log_openssl();
-                throw CredentialException("FileResolver::attach() unable to set EE certificate in context");
+                throw IOException("Unable to attach SP client certificate to SSL context");
             }
         }
         else {
             // When we add certs, they don't get ref counted, so we need to duplicate them.
             X509* dup = X509_dup(*i);
-            if (SSL_CTX_add_extra_chain_cert(ssl_ctx, dup) != 0) {
+            if (SSL_CTX_add_extra_chain_cert(ssl_ctx, dup) != 1) {
                 X509_free(dup);
                 log_openssl();
-                throw CredentialException("FileResolver::attach() unable to add CA certificate to context");
+                throw IOException("Unable to attach CA certificate to SSL context");
             }
         }
     }
@@ -330,12 +399,15 @@ void FileResolver::attach(void* ctx) const
 
 XSECCryptoKey* FileResolver::getKey() const
 {
+#ifdef _DEBUG
+    saml::NDC ndc("getKey");
+#endif
+
     // Get a EVP_PKEY.
     EVP_PKEY* pkey=NULL;
     BIO* in=BIO_new(BIO_s_file_internal());
     if (in && BIO_read_filename(in,m_keypath.c_str())>0) {
-        switch (m_keyformat)
-        {
+        switch (m_keyformat) {
             case PEM:
                 pkey=PEM_read_bio_PrivateKey(in, NULL, passwd_callback, const_cast<char*>(m_keypass.c_str()));
                 break;
@@ -359,8 +431,7 @@ XSECCryptoKey* FileResolver::getKey() const
     // Now map it to an XSEC wrapper.
     if (pkey) {
         XSECCryptoKey* ret=NULL;
-        switch (pkey->type)
-        {
+        switch (pkey->type) {
             case EVP_PKEY_RSA:
                 ret=new OpenSSLCryptoKeyRSA(pkey);
                 break;
@@ -370,7 +441,6 @@ XSECCryptoKey* FileResolver::getKey() const
                 break;
             
             default:
-                saml::NDC ndc("FileResolver");
                 Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("unsupported private key type");
         }
         EVP_PKEY_free(pkey);
@@ -378,9 +448,8 @@ XSECCryptoKey* FileResolver::getKey() const
             return ret;
     }
 
-    saml::NDC ndc("FileResolver");
     log_openssl();
-    Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("FileResolver::getKey() unable to load private key from file");
+    Category::getInstance(XMLPROVIDERS_LOGCAT".CredResolvers").error("FileResolver unable to load private key from file");
     return NULL;
 }
 
@@ -431,3 +500,92 @@ void FileResolver::dump(FILE* f) const
 #endif
     }
 }
+
+// Used to determine the encoding format of credentials files
+// dynamically. Supports: PEM, DER, PKCS12.
+FileResolver::format_t FileResolver::getEncodingFormat(BIO* in) const
+{
+    PKCS12* p12 = NULL;
+    format_t format;
+
+    const int READSIZE = 1;
+    char buf[READSIZE];
+    char b1;
+    int mark;
+
+    try {
+        if ( (mark = BIO_tell(in)) < 0 ) 
+            throw IOException("getEncodingFormat: BIO_tell() can't get the file position");
+        if ( BIO_read(in, buf, READSIZE) <= 0 ) 
+            throw IOException("getEncodingFormat: BIO_read() can't read from the stream");
+        if ( BIO_seek(in, mark) < 0 ) 
+            throw IOException("getEncodingFormat: BIO_seek() can't reset the file position");
+    }
+    catch (...) {
+        log_openssl();
+        throw;
+    }
+
+    b1 = buf[0];
+
+    // This is a slight variation of the Java code by Chad La Joie.
+    //
+    // Check the first byte of the file.  If it's some kind of
+    // DER-encoded structure (including PKCS12), it will begin with ASCII 048.
+    // Otherwise, assume it's PEM.
+    if (b1 !=  48) {
+        format = PEM;
+    } else {
+        // Here we know it's DER-encoded, now try to parse it as a PKCS12
+        // ASN.1 structure.  If it fails, must be another kind of DER-encoded
+        // key/cert structure.  A little inefficient...but it works.
+        if ( (p12=d2i_PKCS12_bio(in,NULL)) == NULL ) {
+            format = DER;
+        } else {
+            format = _PKCS12;
+        }
+        if (p12)
+            PKCS12_free(p12);    
+        if ( BIO_seek(in, mark) < 0 ) {
+            log_openssl();
+            throw IOException("getEncodingFormat: BIO_seek() can't reset the file position");
+        }
+    }
+
+    return format;
+}
+
+// Convert key/cert format_t types to a human-meaningful string for debug output
+string FileResolver::formatToString(format_t format) const
+{
+    switch(format) {
+        case PEM:
+            return "PEM";
+        case DER:
+            return "DER";
+        case _PKCS12:
+            return "PKCS12";
+        default:
+            return "UNKNOWN";
+    }
+}
+
+// Convert key/cert raw XML format attribute (XMLCh[]) to format_t type
+FileResolver::format_t FileResolver::xmlFormatToFormat(const XMLCh* format_xml) const
+{
+    static const XMLCh cPEM[] = { chLatin_P, chLatin_E, chLatin_M, chNull };
+    static const XMLCh cDER[] = { chLatin_D, chLatin_E, chLatin_R, chNull };
+    static const XMLCh cPKCS12[] = { chLatin_P, chLatin_K, chLatin_C, chLatin_S, chDigit_1, chDigit_2, chNull };
+    format_t format;
+
+    if (!XMLString::compareString(format_xml,cPEM))
+        format=PEM;
+    else if (!XMLString::compareString(format_xml,cDER))
+        format=DER;
+    else if (!XMLString::compareString(format_xml,cPKCS12))
+        format=_PKCS12;
+    else
+        format=UNKNOWN;
+
+    return format;
+}