From e39828c168f8f0135373daf46989d6b28257b39f Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Sun, 4 Jun 2006 04:16:27 +0000 Subject: [PATCH] Semi-tested Encryption wrapper code. --- xmltooling/Makefile.am | 2 + xmltooling/encryption/Encrypter.h | 193 ++++++++++++++++++++++++++ xmltooling/encryption/Encryption.h | 6 +- xmltooling/encryption/impl/Encrypter.cpp | 224 +++++++++++++++++++++++++++++++ xmltooling/signature/Signature.h | 3 +- xmltooling/util/ParserPool.h | 17 ++- xmltooling/xmltooling.vcproj | 8 ++ 7 files changed, 444 insertions(+), 9 deletions(-) create mode 100644 xmltooling/encryption/Encrypter.h create mode 100644 xmltooling/encryption/impl/Encrypter.cpp diff --git a/xmltooling/Makefile.am b/xmltooling/Makefile.am index 9dcf6d8..35c6017 100644 --- a/xmltooling/Makefile.am +++ b/xmltooling/Makefile.am @@ -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 index 0000000..bb00268 --- /dev/null +++ b/xmltooling/encryption/Encrypter.h @@ -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 + +#include +#include + +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 MUST 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 MUST 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__ */ diff --git a/xmltooling/encryption/Encryption.h b/xmltooling/encryption/Encryption.h index 53a94d9..b6d5fc6 100644 --- a/xmltooling/encryption/Encryption.h +++ b/xmltooling/encryption/Encryption.h @@ -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 #include @@ -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 index 0000000..4f5b354 --- /dev/null +++ b/xmltooling/encryption/impl/Encrypter.cpp @@ -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 +#include +#include +#include +#include + +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(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(XMLObjectBuilder::buildOneFromElement(encData->getElement())); + if (!(xmlObject.get()) || !(xmlEncData=dynamic_cast(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 encKey( + m_cipher->encryptKey(encParams.m_keyBuffer, encParams.m_keyBufferSize, ENCRYPT_NONE, kencParams->m_algorithm) + ); + EncryptedKey* xmlEncKey=NULL; + auto_ptr xmlObjectKey(XMLObjectBuilder::buildOneFromElement(encKey->getElement())); + if (!(xmlObjectKey.get()) || !(xmlEncKey=dynamic_cast(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; +} diff --git a/xmltooling/signature/Signature.h b/xmltooling/signature/Signature.h index 14549ec..135dd98 100644 --- a/xmltooling/signature/Signature.h +++ b/xmltooling/signature/Signature.h @@ -165,8 +165,7 @@ namespace xmlsignature { } }; - DECL_XMLTOOLING_EXCEPTION(XMLSecurityException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmltooling::XMLToolingException,Exceptions in XML Security processing); - DECL_XMLTOOLING_EXCEPTION(SignatureException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmlsignature::XMLSecurityException,Exceptions in signature processing); + DECL_XMLTOOLING_EXCEPTION(SignatureException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmlsignature,xmltooling::XMLToolingException,Exceptions in signature processing); }; diff --git a/xmltooling/util/ParserPool.h b/xmltooling/util/ParserPool.h index 34fbdd9..1966656 100644 --- a/xmltooling/util/ParserPool.h +++ b/xmltooling/util/ParserPool.h @@ -151,20 +151,29 @@ namespace xmltooling { virtual BinInputStream* makeStream() const { return new StreamBinInputStream(m_is); } /// @endcond - - private: - std::istream& m_is; - + /** + * A Xerces input stream that wraps a C++ input stream + */ class XMLTOOL_API StreamBinInputStream : public BinInputStream { public: + /** + * Constructs a Xerces input stream around a C++ input stream reference. + * + * @param is reference to an input stream + */ StreamBinInputStream(std::istream& is) : m_is(is), m_pos(0) {} + /// @cond off virtual unsigned int curPos() const { return m_pos; } virtual unsigned int readBytes(XMLByte* const toFill, const unsigned int maxToRead); + /// @endcond private: std::istream& m_is; unsigned int m_pos; }; + + private: + std::istream& m_is; }; }; diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index bb36e0e..b6d71f5 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -316,6 +316,10 @@ Name="impl" > + + @@ -515,6 +519,10 @@ Name="encryption" > + + -- 2.1.4