Semi-tested Encryption wrapper code.
authorScott Cantor <cantor.2@osu.edu>
Sun, 4 Jun 2006 04:16:27 +0000 (04:16 +0000)
committerScott Cantor <cantor.2@osu.edu>
Sun, 4 Jun 2006 04:16:27 +0000 (04:16 +0000)
xmltooling/Makefile.am
xmltooling/encryption/Encrypter.h [new file with mode: 0644]
xmltooling/encryption/Encryption.h
xmltooling/encryption/impl/Encrypter.cpp [new file with mode: 0644]
xmltooling/signature/Signature.h
xmltooling/util/ParserPool.h
xmltooling/xmltooling.vcproj

index 9dcf6d8..35c6017 100644 (file)
@@ -41,6 +41,7 @@ libxmltoolinginclude_HEADERS = \
     XMLToolingConfig.h
 
 encinclude_HEADERS = \
+       encryption/Encrypter.h \
     encryption/Encryption.h
 
 implinclude_HEADERS = \
@@ -75,6 +76,7 @@ noinst_HEADERS = \
 
 if BUILD_XMLSEC
 xmlsec_sources = \
+    encryption/impl/Encrypter.cpp \
        signature/impl/SignatureValidator.cpp \
     signature/impl/XMLSecSignatureImpl.cpp
 else
diff --git a/xmltooling/encryption/Encrypter.h b/xmltooling/encryption/Encrypter.h
new file mode 100644 (file)
index 0000000..bb00268
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ *  Copyright 2001-2006 Internet2
+ * 
+ * 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.
+ */
+
+/**
+ * @file Encrypter.h
+ * 
+ * Methods for encrypting XMLObjects and other data.
+ */
+
+#if !defined(__xmltooling_encrypter_h__) && !defined(XMLTOOLING_NO_XMLSEC)
+#define __xmltooling_encrypter_h__
+
+#include <xmltooling/encryption/Encryption.h>
+
+#include <xsec/enc/XSECCryptoKey.hpp>
+#include <xsec/xenc/XENCCipher.hpp>
+
+namespace xmlencryption {
+
+    /**
+     * Wrapper API for XML Encryption functionality.
+     * Designed to allow both external and internal key generation as follows:
+     * 
+     * If no keying material is supplied, then the algorithm MAY be recognized
+     * and a key can be generated internally. This is only done if a KeyEncryptionParams
+     * structure is also supplied to the operation (otherwise the key would be lost).
+     * 
+     * If an XSECCryptoKey is supplied, then it is used directly, but if KeyEncryptionParams
+     * are supplied, an exception will result unless the raw key buffer is also supplied.
+     * 
+     * If a raw key is provided, then a key object can also be created internally if the
+     * algorithm is recognized.
+     * 
+     * Summing up, if KeyEncryptionParams are used, a raw key must be available or the
+     * key can be generated when the encryption algorithm itself is a standard one. If
+     * no KeyEncryptionParams are supplied, then the key must be supplied either in raw
+     * or object form. 
+     */
+    class XMLTOOL_API Encrypter
+    {
+    public:
+
+        /**
+         * Structure to collect encryption requirements.
+         */
+        struct XMLTOOL_API EncryptionParams {
+            
+            /**
+             * Constructor.
+             * The algorithm constant and key buffer <strong>MUST</strong> be accessible for the life of
+             * the structure. The other objects will be destroyed if need be when the structure is destroyed. 
+             * 
+             * @param algorithm     the XML Encryption key wrapping or transport algorithm constant
+             * @param keyBuffer     buffer containing the raw key information
+             * @param keyBufferSize the size of the raw key buffer in bytes  
+             * @param key           the key encryption key to use, or NULL
+             * @param keyInfo       a KeyInfo object to place within the EncryptedData structure
+             */
+            EncryptionParams(
+                const XMLCh* algorithm=DSIGConstants::s_unicodeStrURIAES256_CBC,
+                const unsigned char* keyBuffer=NULL,
+                unsigned int keyBufferSize=0,
+                XSECCryptoKey* key=NULL,
+                xmlsignature::KeyInfo* keyInfo=NULL
+                ) : m_keyBuffer(keyBuffer), m_keyBufferSize(keyBufferSize), m_key(key), m_keyInfo(keyInfo), m_algorithm(algorithm) {
+            }
+
+            ~EncryptionParams() {
+                delete m_key;
+                delete m_keyInfo;
+            }
+        private:
+            const unsigned char* m_keyBuffer;
+            unsigned int m_keyBufferSize;
+            XSECCryptoKey* m_key;
+            xmlsignature::KeyInfo* m_keyInfo;
+            const XMLCh* m_algorithm;
+            
+            friend class Encrypter;
+        };
+        
+        /**
+         * Structure to collect key wrapping/transport requirements.
+         */
+        struct XMLTOOL_API KeyEncryptionParams {
+            
+            /**
+             * Constructor.
+             * The algorithm constant <strong>MUST</strong> be accessible for the life of the structure.
+             * Using a static constant suffices for this. The other objects will be destroyed if need be
+             * when the structure is destroyed. 
+             * 
+             * @param algorithm     the XML Encryption key wrapping or transport algorithm constant
+             * @param key           the key encryption key to use
+             * @param keyInfo       a KeyInfo object to place within the EncryptedKey structure that describes the KEK
+             */
+            KeyEncryptionParams(const XMLCh* algorithm, XSECCryptoKey* key, xmlsignature::KeyInfo* keyInfo=NULL)
+                : m_key(key), m_keyInfo(keyInfo), m_algorithm(algorithm) {
+            }
+        
+            ~KeyEncryptionParams() {
+                delete m_key;
+                delete m_keyInfo;
+            }
+        private:
+            XSECCryptoKey* m_key;
+            xmlsignature::KeyInfo* m_keyInfo;
+            const XMLCh* m_algorithm;
+            
+            friend class Encrypter;
+        };
+    
+        Encrypter() : m_cipher(NULL) {}
+
+        ~Encrypter();
+        
+        /**
+         * Encrypts the supplied element and returns the resulting object.
+         * The returned object will be unmarshalled around a DOM tree created
+         * using the encrypted element's owning document.
+         * 
+         * If an encryption algorithm is set, but no key, a random key will be
+         * generated iff keParams is non-NULL and the algorithm is known.
+         * 
+         * If key encryption parameters are supplied, then the encryption key
+         * is wrapped and the result placed into an EncryptedKey object in the
+         * KeyInfo of the returned EncryptedData.
+         * 
+         * @param element   the DOM element to encrypt
+         * @param keParams  key encryption settings, or NULL
+         */
+        EncryptedData* encryptElement(DOMElement* element, EncryptionParams& encParams, KeyEncryptionParams* kencParams=NULL);
+
+        /**
+         * Encrypts the supplied element's children and returns the resulting object.
+         * The returned object will be unmarshalled around a DOM tree created
+         * using the encrypted content's owning document.
+         * 
+         * If an encryption algorithm is set, but no key, a random key will be
+         * generated iff keParams is non-NULL and the algorithm is known.
+
+         * If key encryption parameters are supplied, then the encryption key
+         * is wrapped and the result placed into an EncryptedKey object in the
+         * KeyInfo of the returned EncryptedData.
+         * 
+         * @param element   parent element of children to encrypt
+         * @param keParams  key encryption settings, or NULL
+         */
+        EncryptedData* encryptElementContent(DOMElement* element, EncryptionParams& encParams, KeyEncryptionParams* kencParams=NULL);
+
+        /**
+         * Encrypts the supplied input stream and returns the resulting object.
+         * The returned object will be unmarshalled around a DOM tree created
+         * using the encrypted element's owning document.
+         * 
+         * If an encryption algorithm is set, but no key, a random key will be
+         * generated iff keParams is non-NULL and the algorithm is known.
+
+         * If key encryption parameters are supplied, then the encryption key
+         * is wrapped and the result placed into an EncryptedKey object in the
+         * KeyInfo of the returned EncryptedData.
+         * 
+         * @param input   the stream to encrypt
+         * @param keParams  key encryption settings, or NULL
+         */
+        EncryptedData* encryptStream(std::istream& input, EncryptionParams& encParams, KeyEncryptionParams* kencParams=NULL);
+        
+    private:
+        void checkParams(EncryptionParams& encParams, KeyEncryptionParams* kencParams);
+        EncryptedData* decorateAndUnmarshall(EncryptionParams& encParams, KeyEncryptionParams* kencParams);
+    
+        XENCCipher* m_cipher;
+        unsigned char m_keyBuffer[32];
+    };
+
+    DECL_XMLTOOLING_EXCEPTION(EncryptionException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlencryption,xmltooling::XMLToolingException,Exceptions in encryption processing);
+
+};
+
+#endif /* __xmltooling_encrypter_h__ */
index 53a94d9..b6d5fc6 100644 (file)
@@ -20,8 +20,8 @@
  * XMLObjects representing XML Encryption content
  */
 
-#ifndef __xmltooling_encrypt_h__
-#define __xmltooling_encrypt_h__
+#ifndef __xmltooling_encryption_h__
+#define __xmltooling_encryption_h__
 
 #include <xmltooling/AttributeExtensibleXMLObject.h>
 #include <xmltooling/signature/KeyInfo.h>
@@ -151,4 +151,4 @@ namespace xmlencryption {
     void XMLTOOL_API registerEncryptionClasses();
 };
 
-#endif /* __xmltooling_encrypt_h__ */
+#endif /* __xmltooling_encryption_h__ */
diff --git a/xmltooling/encryption/impl/Encrypter.cpp b/xmltooling/encryption/impl/Encrypter.cpp
new file mode 100644 (file)
index 0000000..4f5b354
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ *  Copyright 2001-2006 Internet2
+ * 
+ * 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.
+ */
+
+/**
+ * Encrypter.cpp
+ * 
+ * Methods for encrypting XMLObjects and other data.
+ */
+
+#include "internal.h"
+#include "encryption/Encrypter.h"
+
+#include <xsec/enc/openssl/OpenSSLCryptoSymmetricKey.hpp>
+#include <xsec/enc/XSECCryptoException.hpp>
+#include <xsec/framework/XSECException.hpp>
+#include <xsec/xenc/XENCEncryptedData.hpp>
+#include <xsec/xenc/XENCEncryptedKey.hpp>
+
+using namespace xmlencryption;
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace std;
+
+Encrypter::~Encrypter()
+{
+    XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
+    memset(m_keyBuffer,0,32);
+}
+
+void Encrypter::checkParams(EncryptionParams& encParams, KeyEncryptionParams* kencParams)
+{
+    if (encParams.m_keyBufferSize==0) {
+        if (encParams.m_key) {
+            if (kencParams)
+                throw EncryptionException("Generating EncryptedKey inline requires the encryption key in raw form.");
+        }
+        else if (!encParams.m_key) {
+            if (!kencParams)
+                throw EncryptionException("Using a generated encryption key requires a KeyEncryptionParams object.");
+
+            // We're generating a random key. The maximum supported length is AES-256, so we need 32 bytes.
+            if (XSECPlatformUtils::g_cryptoProvider->getRandom(m_keyBuffer,32)<32)
+                throw EncryptionException("Unable to generate random data; was PRNG seeded?");
+            encParams.m_keyBuffer=m_keyBuffer;
+            encParams.m_keyBufferSize=32;
+        }
+    }
+    
+    if (!encParams.m_key) {
+        // We have to have a raw key now, so we need to build a wrapper around it.
+        if (XMLString::equals(encParams.m_algorithm,DSIGConstants::s_unicodeStrURI3DES_CBC)) {
+            encParams.m_key=new OpenSSLCryptoSymmetricKey(XSECCryptoSymmetricKey::KEY_3DES_192);
+        }
+        else if (XMLString::equals(encParams.m_algorithm,DSIGConstants::s_unicodeStrURIAES128_CBC)) {
+            encParams.m_key=new OpenSSLCryptoSymmetricKey(XSECCryptoSymmetricKey::KEY_AES_128);
+        }
+        else if (XMLString::equals(encParams.m_algorithm,DSIGConstants::s_unicodeStrURIAES192_CBC)) {
+            encParams.m_key=new OpenSSLCryptoSymmetricKey(XSECCryptoSymmetricKey::KEY_AES_192);
+        }
+        else if (XMLString::equals(encParams.m_algorithm,DSIGConstants::s_unicodeStrURIAES256_CBC)) {
+            encParams.m_key=new OpenSSLCryptoSymmetricKey(XSECCryptoSymmetricKey::KEY_AES_256);
+        }
+        else {
+            throw EncryptionException("Unrecognized encryption algorithm, unable to build key wrapper.");
+        }
+        static_cast<OpenSSLCryptoSymmetricKey*>(encParams.m_key)->setKey(encParams.m_keyBuffer, encParams.m_keyBufferSize);
+    }
+    
+    // Set the encryption key.
+    m_cipher->setKey(encParams.m_key->clone());
+}
+
+EncryptedData* Encrypter::encryptElement(DOMElement* element, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
+{
+    // We can reuse the cipher object if the document hasn't changed.
+    
+    if (m_cipher && m_cipher->getDocument()!=element->getOwnerDocument()) {
+        XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
+        m_cipher=NULL;
+    }
+    
+    if (!m_cipher)
+        m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(element->getOwnerDocument());
+    
+    try {
+        checkParams(encParams,kencParams);
+        m_cipher->encryptElementDetached(element, ENCRYPT_NONE, encParams.m_algorithm);
+        return decorateAndUnmarshall(encParams, kencParams);
+    }
+    catch(XSECException& e) {
+        auto_ptr_char temp(e.getMsg());
+        throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
+    }
+    catch(XSECCryptoException& e) {
+        throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
+    }
+}
+
+EncryptedData* Encrypter::encryptElementContent(DOMElement* element, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
+{
+    // We can reuse the cipher object if the document hasn't changed.
+
+    if (m_cipher && m_cipher->getDocument()!=element->getOwnerDocument()) {
+        XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
+        m_cipher=NULL;
+    }
+    
+    if (!m_cipher)
+        m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(element->getOwnerDocument());
+    
+    try {
+        checkParams(encParams,kencParams);
+        m_cipher->encryptElementContentDetached(element, ENCRYPT_NONE, encParams.m_algorithm);
+        return decorateAndUnmarshall(encParams, kencParams);
+    }
+    catch(XSECException& e) {
+        auto_ptr_char temp(e.getMsg());
+        throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
+    }
+    catch(XSECCryptoException& e) {
+        throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
+    }
+}
+
+EncryptedData* Encrypter::encryptStream(istream& input, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
+{
+    // Get a fresh cipher object and document.
+
+    if (m_cipher) {
+        XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
+        m_cipher=NULL;
+    }
+    
+    DOMDocument* doc=NULL;
+    try {
+        doc=XMLToolingConfig::getConfig().getParser().newDocument();
+        m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(doc);
+        
+        checkParams(encParams,kencParams);
+        StreamInputSource::StreamBinInputStream xstream(input);
+        m_cipher->encryptBinInputStream(&xstream, ENCRYPT_NONE, encParams.m_algorithm);
+        EncryptedData* xmlEncData = decorateAndUnmarshall(encParams, kencParams);
+        doc->release();
+        return xmlEncData;
+    }
+    catch(XSECException& e) {
+        doc->release();
+        auto_ptr_char temp(e.getMsg());
+        throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
+    }
+    catch(XSECCryptoException& e) {
+        doc->release();
+        throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
+    }
+    catch (...) {
+        doc->release();
+        throw;
+    }
+}
+
+EncryptedData* Encrypter::decorateAndUnmarshall(EncryptionParams& encParams, KeyEncryptionParams* kencParams)
+{
+    XENCEncryptedData* encData=m_cipher->getEncryptedData();
+    if (!encData)
+        throw EncryptionException("No EncryptedData element found?");
+
+    // Unmarshall a tooling version of EncryptedData around the DOM.
+    EncryptedData* xmlEncData=NULL;
+    auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(encData->getElement()));
+    if (!(xmlObject.get()) || !(xmlEncData=dynamic_cast<EncryptedData*>(xmlObject.get())))
+        throw EncryptionException("Unable to unmarshall into EncryptedData object.");
+    
+    // Unbind from DOM so we can divorce this from the original document.
+    xmlEncData->releaseThisAndChildrenDOM();
+    
+    // KeyInfo?
+    if (encParams.m_keyInfo) {
+        xmlEncData->setKeyInfo(encParams.m_keyInfo);
+        encParams.m_keyInfo=NULL;   // transfer ownership
+    }
+    
+    // Are we doing a key encryption?
+    if (kencParams) {
+        m_cipher->setKEK(kencParams->m_key->clone());
+        // ownership of this belongs to us, for some reason...
+        auto_ptr<XENCEncryptedKey> encKey(
+            m_cipher->encryptKey(encParams.m_keyBuffer, encParams.m_keyBufferSize, ENCRYPT_NONE, kencParams->m_algorithm)
+            );
+        EncryptedKey* xmlEncKey=NULL;
+        auto_ptr<XMLObject> xmlObjectKey(XMLObjectBuilder::buildOneFromElement(encKey->getElement()));
+        if (!(xmlObjectKey.get()) || !(xmlEncKey=dynamic_cast<EncryptedKey*>(xmlObjectKey.get())))
+            throw EncryptionException("Unable to unmarshall into EncryptedKey object.");
+        
+        xmlEncKey->releaseThisAndChildrenDOM();
+        
+        // KeyInfo?
+        if (kencParams->m_keyInfo) {
+            xmlEncKey->setKeyInfo(kencParams->m_keyInfo);
+            kencParams->m_keyInfo=NULL;   // transfer ownership
+        }
+        
+        // Add the EncryptedKey.
+        if (!xmlEncData->getKeyInfo())
+            xmlEncData->setKeyInfo(KeyInfoBuilder::buildKeyInfo());
+        xmlEncData->getKeyInfo()->getOthers().push_back(xmlEncKey);
+        xmlObjectKey.release();
+    }
+    
+    xmlObject.release();
+    return xmlEncData;
+}
index 14549ec..135dd98 100644 (file)
@@ -165,8 +165,7 @@ namespace xmlsignature {
         }\r
     };\r
 \r
-    DECL_XMLTOOLING_EXCEPTION(XMLSecurityException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmltooling::XMLToolingException,Exceptions in XML Security processing);\r
-    DECL_XMLTOOLING_EXCEPTION(SignatureException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmlsignature::XMLSecurityException,Exceptions in signature processing);\r
+    DECL_XMLTOOLING_EXCEPTION(SignatureException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmltooling::XMLToolingException,Exceptions in signature processing);\r
 \r
 };\r
 \r
index 34fbdd9..1966656 100644 (file)
@@ -151,20 +151,29 @@ namespace xmltooling {
         virtual BinInputStream* makeStream() const { return new StreamBinInputStream(m_is); }\r
         /// @endcond\r
 \r
-\r
-    private:\r
-        std::istream& m_is;\r
-\r
+        /**\r
+         * A Xerces input stream that wraps a C++ input stream\r
+         */\r
         class XMLTOOL_API StreamBinInputStream : public BinInputStream\r
         {\r
         public:\r
+            /**\r
+             * Constructs a Xerces input stream around a C++ input stream reference.\r
+             * \r
+             * @param is        reference to an input stream\r
+             */\r
             StreamBinInputStream(std::istream& is) : m_is(is), m_pos(0) {}\r
+            /// @cond off\r
             virtual unsigned int curPos() const { return m_pos; }\r
             virtual unsigned int readBytes(XMLByte* const toFill, const unsigned int maxToRead);\r
+            /// @endcond\r
         private:\r
             std::istream& m_is;\r
             unsigned int m_pos;\r
         };\r
+\r
+    private:\r
+        std::istream& m_is;\r
     };\r
 };\r
 \r
index bb36e0e..b6d71f5 100644 (file)
                                        Name="impl"\r
                                        >\r
                                        <File\r
+                                               RelativePath=".\encryption\impl\Encrypter.cpp"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\encryption\impl\EncryptionImpl.cpp"\r
                                                >\r
                                        </File>\r
                                Name="encryption"\r
                                >\r
                                <File\r
+                                       RelativePath=".\encryption\Encrypter.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\encryption\Encryption.h"\r
                                        >\r
                                </File>\r