X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=xmltooling%2Fencryption%2Fimpl%2FDecrypter.cpp;h=584aae0839c8e1d91cd020a7127ad6ff43857d92;hb=b23c5dab38620c14e0c3277c840994a11175d59b;hp=2f2afe2a16d6270dc80d7d0cd230e08e55265c41;hpb=ce4b4a22812fef63619154a8e9b12dd44a2cbb5e;p=shibboleth%2Fcpp-xmltooling.git diff --git a/xmltooling/encryption/impl/Decrypter.cpp b/xmltooling/encryption/impl/Decrypter.cpp index 2f2afe2..584aae0 100644 --- a/xmltooling/encryption/impl/Decrypter.cpp +++ b/xmltooling/encryption/impl/Decrypter.cpp @@ -1,17 +1,21 @@ -/* - * Copyright 2001-2007 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 +/** + * Licensed to the University Corporation for Advanced Internet + * Development, Inc. (UCAID) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. * - * http://www.apache.org/licenses/LICENSE-2.0 + * UCAID licenses this file to you 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 * - * 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. + * 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. */ /** @@ -21,94 +25,83 @@ */ #include "internal.h" +#include "logging.h" #include "encryption/Decrypter.h" #include "encryption/EncryptedKeyResolver.h" +#include "encryption/Encryption.h" +#include "security/Credential.h" +#include "security/CredentialCriteria.h" #include "security/CredentialResolver.h" -#include #include #include #include #include +#include +#include #include #include using namespace xmlencryption; using namespace xmlsignature; using namespace xmltooling; +using namespace xercesc; using namespace std; + +Decrypter::Decrypter( + const CredentialResolver* credResolver, + CredentialCriteria* criteria, + const EncryptedKeyResolver* EKResolver, + bool requireAuthenticatedCipher + ) : m_cipher(nullptr), m_credResolver(credResolver), m_criteria(criteria), m_EKResolver(EKResolver), + m_requireAuthenticatedCipher(requireAuthenticatedCipher) +{ +} + Decrypter::~Decrypter() { if (m_cipher) XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher); } -DOMDocumentFragment* Decrypter::decryptData(EncryptedData& encryptedData) +void Decrypter::setEncryptedKeyResolver(const EncryptedKeyResolver* EKResolver) +{ + m_EKResolver=EKResolver; +} + +void Decrypter::setKEKResolver(const CredentialResolver* resolver, CredentialCriteria* criteria) +{ + m_credResolver=resolver; + m_criteria=criteria; +} + +DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, XSECCryptoKey* key) { - if (encryptedData.getDOM()==NULL) + if (encryptedData.getDOM() == nullptr) throw DecryptionException("The object must be marshalled before decryption."); - + + XMLToolingInternalConfig& xmlconf = XMLToolingInternalConfig::getInternalConfig(); + if (m_requireAuthenticatedCipher) { + const XMLCh* alg = encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr; + if (!alg || !xmlconf.isXMLAlgorithmSupported(alg, XMLToolingConfig::ALGTYPE_AUTHNENCRYPT)) { + throw DecryptionException("Unauthenticated data encryption algorithm unsupported."); + } + } + // We can reuse the cipher object if the document hasn't changed. if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) { - XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher); - m_cipher=NULL; + xmlconf.m_xsecProvider->releaseCipher(m_cipher); + m_cipher=nullptr; } if (!m_cipher) - m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument()); - - try { - // Resolve decryption key. - XSECCryptoKey* key=NULL; - if (m_resolver) - key=m_resolver->resolveKey(encryptedData.getKeyInfo()); - - if (!key && m_KEKresolver) { - // See if there's an encrypted key available. We'll need the algorithm... - const XMLCh* algorithm= - encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : NULL; - if (!algorithm) - throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed."); - - if (encryptedData.getKeyInfo()) { - const vector& others=const_cast(encryptedData.getKeyInfo())->getUnknownXMLObjects(); - for (vector::const_iterator i=others.begin(); i!=others.end(); i++) { - EncryptedKey* encKey=dynamic_cast(*i); - if (encKey) { - try { - key=decryptKey(*encKey, algorithm); - } - catch (DecryptionException& e) { - log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(e.what()); - } - } - } - } - - if (!key) { - // Check for a non-trivial resolver. - const EncryptedKeyResolver* ekr=dynamic_cast(m_resolver); - if (ekr) { - EncryptedKey* encKey=ekr->resolveKey(encryptedData); - if (encKey) { - try { - key=decryptKey(*encKey, algorithm); - } - catch (DecryptionException& e) { - log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(e.what()); - } - } - } - } - } + m_cipher = xmlconf.m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument()); - if (!key) - throw DecryptionException("Unable to resolve a decryption key."); - - m_cipher->setKey(key); - DOMNode* ret=m_cipher->decryptElementDetached(encryptedData.getDOM()); + try { + m_cipher->setKey(key->clone()); + DOMNode* ret = m_cipher->decryptElementDetached(encryptedData.getDOM()); if (ret->getNodeType()!=DOMNode::DOCUMENT_FRAGMENT_NODE) { ret->release(); throw DecryptionException("Decryption operation did not result in DocumentFragment."); @@ -124,46 +117,277 @@ DOMDocumentFragment* Decrypter::decryptData(EncryptedData& encryptedData) } } -XSECCryptoKey* Decrypter::decryptKey(EncryptedKey& encryptedKey, const XMLCh* algorithm) +DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, const XMLCh* recipient) +{ + if (!m_credResolver) + throw DecryptionException("No CredentialResolver supplied to provide decryption keys."); + + // Resolve a decryption key directly. + vector creds; + int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES; + if (m_criteria) { + m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL); + m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types); + const EncryptionMethod* meth = encryptedData.getEncryptionMethod(); + if (meth) + m_criteria->setXMLAlgorithm(meth->getAlgorithm()); + m_credResolver->resolve(creds, m_criteria); + } + else { + CredentialCriteria criteria; + criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL); + criteria.setKeyInfo(encryptedData.getKeyInfo(), types); + const EncryptionMethod* meth = encryptedData.getEncryptionMethod(); + if (meth) + criteria.setXMLAlgorithm(meth->getAlgorithm()); + m_credResolver->resolve(creds, &criteria); + } + + // Loop over them and try each one. + XSECCryptoKey* key; + for (vector::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) { + try { + key = (*cred)->getPrivateKey(); + if (!key) + continue; + return decryptData(encryptedData, key); + } + catch(DecryptionException& ex) { + logging::Category::getInstance(XMLTOOLING_LOGCAT ".Decrypter").warn(ex.what()); + } + } + + // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm... + const XMLCh* algorithm= + encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr; + if (!algorithm) + throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed."); + + // Check for external resolver. + const EncryptedKey* encKey=nullptr; + if (m_EKResolver) + encKey = m_EKResolver->resolveKey(encryptedData, recipient); + else { + EncryptedKeyResolver ekr; + encKey = ekr.resolveKey(encryptedData, recipient); + } + + if (!encKey) + throw DecryptionException("Unable to locate an encrypted key."); + + auto_ptr keywrapper(decryptKey(*encKey, algorithm)); + if (!keywrapper.get()) + throw DecryptionException("Unable to decrypt the encrypted key."); + return decryptData(encryptedData, keywrapper.get()); +} + +void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, XSECCryptoKey* key) +{ + if (encryptedData.getDOM() == nullptr) + throw DecryptionException("The object must be marshalled before decryption."); + + XMLToolingInternalConfig& xmlconf = XMLToolingInternalConfig::getInternalConfig(); + if (m_requireAuthenticatedCipher) { + const XMLCh* alg = encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr; + if (!alg || !xmlconf.isXMLAlgorithmSupported(alg, XMLToolingConfig::ALGTYPE_AUTHNENCRYPT)) { + throw DecryptionException("Unauthenticated data encryption algorithm unsupported."); + } + } + + // We can reuse the cipher object if the document hasn't changed. + + if (m_cipher && m_cipher->getDocument() != encryptedData.getDOM()->getOwnerDocument()) { + xmlconf.m_xsecProvider->releaseCipher(m_cipher); + m_cipher = nullptr; + } + + if (!m_cipher) + m_cipher = xmlconf.m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument()); + + try { + m_cipher->setKey(key->clone()); + auto_ptr in(m_cipher->decryptToBinInputStream(encryptedData.getDOM())); + + XMLByte buf[8192]; + xsecsize_t count = in->readBytes(buf, sizeof(buf)); + while (count > 0) + out.write(reinterpret_cast(buf),count); + } + catch(XSECException& e) { + auto_ptr_char temp(e.getMsg()); + throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get()); + } + catch(XSECCryptoException& e) { + throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg()); + } +} + +void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, const XMLCh* recipient) +{ + if (!m_credResolver) + throw DecryptionException("No CredentialResolver supplied to provide decryption keys."); + + // Resolve a decryption key directly. + vector creds; + int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES; + if (m_criteria) { + m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL); + m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types); + const EncryptionMethod* meth = encryptedData.getEncryptionMethod(); + if (meth) + m_criteria->setXMLAlgorithm(meth->getAlgorithm()); + m_credResolver->resolve(creds,m_criteria); + } + else { + CredentialCriteria criteria; + criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL); + criteria.setKeyInfo(encryptedData.getKeyInfo(), types); + const EncryptionMethod* meth = encryptedData.getEncryptionMethod(); + if (meth) + criteria.setXMLAlgorithm(meth->getAlgorithm()); + m_credResolver->resolve(creds,&criteria); + } + + // Loop over them and try each one. + XSECCryptoKey* key; + for (vector::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) { + try { + key = (*cred)->getPrivateKey(); + if (!key) + continue; + return decryptData(out, encryptedData, key); + } + catch(DecryptionException& ex) { + logging::Category::getInstance(XMLTOOLING_LOGCAT ".Decrypter").warn(ex.what()); + } + } + + // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm... + const XMLCh* algorithm= + encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr; + if (!algorithm) + throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed."); + + // Check for external resolver. + const EncryptedKey* encKey=nullptr; + if (m_EKResolver) + encKey = m_EKResolver->resolveKey(encryptedData, recipient); + else { + EncryptedKeyResolver ekr; + encKey = ekr.resolveKey(encryptedData, recipient); + } + + if (!encKey) + throw DecryptionException("Unable to locate an encrypted key."); + + auto_ptr keywrapper(decryptKey(*encKey, algorithm)); + if (!keywrapper.get()) + throw DecryptionException("Unable to decrypt the encrypted key."); + decryptData(out, encryptedData, keywrapper.get()); +} + +XSECCryptoKey* Decrypter::decryptKey(const EncryptedKey& encryptedKey, const XMLCh* algorithm) { - if (encryptedKey.getDOM()==NULL) + if (!m_credResolver) + throw DecryptionException("No CredentialResolver supplied to provide decryption keys."); + + if (encryptedKey.getDOM()==nullptr) throw DecryptionException("The object must be marshalled before decryption."); + + XSECAlgorithmHandler* handler; + try { + handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm); + if (!handler) + throw DecryptionException("Unrecognized algorithm, no way to build object around decrypted key."); + } + catch(XSECException& e) { + auto_ptr_char temp(e.getMsg()); + throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get()); + } + catch(XSECCryptoException& e) { + throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg()); + } // We can reuse the cipher object if the document hasn't changed. if (m_cipher && m_cipher->getDocument()!=encryptedKey.getDOM()->getOwnerDocument()) { XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher); - m_cipher=NULL; + m_cipher = nullptr; } if (!m_cipher) - m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument()); + m_cipher = XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument()); - try { - // Resolve key decryption key. - XSECCryptoKey* key=NULL; - if (m_KEKresolver) - key=m_KEKresolver->getKey(encryptedKey.getKeyInfo()); - if (!key) - throw DecryptionException("Unable to resolve a key decryption key."); - m_cipher->setKEK(key); + // Resolve key decryption keys. + int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES; + vector creds; + if (m_criteria) { + m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL); + m_criteria->setKeyInfo(encryptedKey.getKeyInfo(), types); + const EncryptionMethod* meth = encryptedKey.getEncryptionMethod(); + if (meth) + m_criteria->setXMLAlgorithm(meth->getAlgorithm()); + m_credResolver->resolve(creds, m_criteria); + } + else { + CredentialCriteria criteria; + criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL); + criteria.setKeyInfo(encryptedKey.getKeyInfo(), types); + const EncryptionMethod* meth = encryptedKey.getEncryptionMethod(); + if (meth) + criteria.setXMLAlgorithm(meth->getAlgorithm()); + m_credResolver->resolve(creds, &criteria); + } + if (creds.empty()) + throw DecryptionException("Unable to resolve any key decryption keys."); + + XMLByte buffer[1024]; + for (vector::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) { + try { + if (!(*cred)->getPrivateKey()) + throw DecryptionException("Credential did not contain a private key."); + memset(buffer,0,sizeof(buffer)); + m_cipher->setKEK((*cred)->getPrivateKey()->clone()); + + try { + int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024); + if (keySize<=0) + throw DecryptionException("Unable to decrypt key."); - XMLByte buffer[1024]; - int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024); - if (keySize > 0) { - // Try to map the key. - XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm); - if (handler != NULL) + // Try to wrap the key. return handler->createKeyForURI(algorithm, buffer, keySize); - throw DecryptionException("Unrecognized algorithm, could not build object around decrypted key."); + } + catch(XSECException& e) { + auto_ptr_char temp(e.getMsg()); + throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get()); + } + catch(XSECCryptoException& e) { + throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg()); + } + } + catch(DecryptionException& ex) { + logging::Category::getInstance(XMLTOOLING_LOGCAT ".Decrypter").warn(ex.what()); } - throw DecryptionException("Unable to decrypt key."); + } + + // Some algorithms are vulnerable to chosen ciphertext attacks, so we generate a random key + // to prevent discovery of the validity of the original candidate. + logging::Category::getInstance(XMLTOOLING_LOGCAT ".Decrypter").warn( + "unable to decrypt key, generating random key for defensive purposes" + ); + pair mapped = XMLToolingConfig::getConfig().mapXMLAlgorithmToKeyAlgorithm(algorithm); + if (!mapped.second) + mapped.second = 256; + try { + if (XSECPlatformUtils::g_cryptoProvider->getRandom(reinterpret_cast(buffer),mapped.second) < mapped.second) + throw DecryptionException("Unable to generate random data; was PRNG seeded?"); + return handler->createKeyForURI(algorithm, buffer, mapped.second); } catch(XSECException& e) { auto_ptr_char temp(e.getMsg()); - throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get()); + throw DecryptionException(string("XMLSecurity exception while generating key: ") + temp.get()); } - catch(XSECCryptoException& e) { - throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg()); + catch (XSECCryptoException& e) { + throw DecryptionException(string("XMLSecurity exception while generating key: ") + e.getMsg()); } }