2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
24 * Methods for decrypting XMLObjects and other data.
29 #include "encryption/Decrypter.h"
30 #include "encryption/EncryptedKeyResolver.h"
31 #include "encryption/Encryption.h"
32 #include "security/Credential.h"
33 #include "security/CredentialCriteria.h"
34 #include "security/CredentialResolver.h"
36 #include <xsec/enc/XSECCryptoException.hpp>
37 #include <xsec/framework/XSECException.hpp>
38 #include <xsec/framework/XSECAlgorithmMapper.hpp>
39 #include <xsec/framework/XSECAlgorithmHandler.hpp>
40 #include <xsec/utils/XSECBinTXFMInputStream.hpp>
41 #include <xsec/xenc/XENCCipher.hpp>
42 #include <xsec/xenc/XENCEncryptedData.hpp>
43 #include <xsec/xenc/XENCEncryptedKey.hpp>
45 using namespace xmlencryption;
46 using namespace xmlsignature;
47 using namespace xmltooling;
48 using namespace xercesc;
53 const CredentialResolver* credResolver,
54 CredentialCriteria* criteria,
55 const EncryptedKeyResolver* EKResolver,
56 bool requireAuthenticatedCipher
57 ) : m_cipher(nullptr), m_credResolver(credResolver), m_criteria(criteria), m_EKResolver(EKResolver),
58 m_requireAuthenticatedCipher(requireAuthenticatedCipher)
62 Decrypter::~Decrypter()
65 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
68 void Decrypter::setEncryptedKeyResolver(const EncryptedKeyResolver* EKResolver)
70 m_EKResolver=EKResolver;
73 void Decrypter::setKEKResolver(const CredentialResolver* resolver, CredentialCriteria* criteria)
75 m_credResolver=resolver;
79 DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, XSECCryptoKey* key)
81 if (encryptedData.getDOM() == nullptr)
82 throw DecryptionException("The object must be marshalled before decryption.");
84 XMLToolingInternalConfig& xmlconf = XMLToolingInternalConfig::getInternalConfig();
85 if (m_requireAuthenticatedCipher) {
86 const XMLCh* alg = encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
87 if (!alg || !xmlconf.isXMLAlgorithmSupported(alg, XMLToolingConfig::ALGTYPE_AUTHNENCRYPT)) {
88 throw DecryptionException("Unauthenticated data encryption algorithm unsupported.");
92 // We can reuse the cipher object if the document hasn't changed.
94 if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) {
95 xmlconf.m_xsecProvider->releaseCipher(m_cipher);
100 m_cipher = xmlconf.m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument());
103 m_cipher->setKey(key->clone());
104 DOMNode* ret = m_cipher->decryptElementDetached(encryptedData.getDOM());
105 if (ret->getNodeType()!=DOMNode::DOCUMENT_FRAGMENT_NODE) {
107 throw DecryptionException("Decryption operation did not result in DocumentFragment.");
109 return static_cast<DOMDocumentFragment*>(ret);
111 catch(XSECException& e) {
112 auto_ptr_char temp(e.getMsg());
113 throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
115 catch(XSECCryptoException& e) {
116 throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
120 DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, const XMLCh* recipient)
123 throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
125 // Resolve a decryption key directly.
126 vector<const Credential*> creds;
127 int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
129 m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
130 m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types);
131 const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
133 m_criteria->setXMLAlgorithm(meth->getAlgorithm());
134 m_credResolver->resolve(creds, m_criteria);
137 CredentialCriteria criteria;
138 criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
139 criteria.setKeyInfo(encryptedData.getKeyInfo(), types);
140 const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
142 criteria.setXMLAlgorithm(meth->getAlgorithm());
143 m_credResolver->resolve(creds, &criteria);
146 // Loop over them and try each one.
148 for (vector<const Credential*>::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) {
150 key = (*cred)->getPrivateKey();
153 return decryptData(encryptedData, key);
155 catch(DecryptionException& ex) {
156 logging::Category::getInstance(XMLTOOLING_LOGCAT ".Decrypter").warn(ex.what());
160 // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm...
161 const XMLCh* algorithm=
162 encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
164 throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
166 // Check for external resolver.
167 const EncryptedKey* encKey=nullptr;
169 encKey = m_EKResolver->resolveKey(encryptedData, recipient);
171 EncryptedKeyResolver ekr;
172 encKey = ekr.resolveKey(encryptedData, recipient);
176 throw DecryptionException("Unable to locate an encrypted key.");
178 auto_ptr<XSECCryptoKey> keywrapper(decryptKey(*encKey, algorithm));
179 if (!keywrapper.get())
180 throw DecryptionException("Unable to decrypt the encrypted key.");
181 return decryptData(encryptedData, keywrapper.get());
184 void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, XSECCryptoKey* key)
186 if (encryptedData.getDOM() == nullptr)
187 throw DecryptionException("The object must be marshalled before decryption.");
189 XMLToolingInternalConfig& xmlconf = XMLToolingInternalConfig::getInternalConfig();
190 if (m_requireAuthenticatedCipher) {
191 const XMLCh* alg = encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
192 if (!alg || !xmlconf.isXMLAlgorithmSupported(alg, XMLToolingConfig::ALGTYPE_AUTHNENCRYPT)) {
193 throw DecryptionException("Unauthenticated data encryption algorithm unsupported.");
197 // We can reuse the cipher object if the document hasn't changed.
199 if (m_cipher && m_cipher->getDocument() != encryptedData.getDOM()->getOwnerDocument()) {
200 xmlconf.m_xsecProvider->releaseCipher(m_cipher);
205 m_cipher = xmlconf.m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument());
208 m_cipher->setKey(key->clone());
209 auto_ptr<XSECBinTXFMInputStream> in(m_cipher->decryptToBinInputStream(encryptedData.getDOM()));
212 xsecsize_t count = in->readBytes(buf, sizeof(buf));
214 out.write(reinterpret_cast<char*>(buf),count);
216 catch(XSECException& e) {
217 auto_ptr_char temp(e.getMsg());
218 throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
220 catch(XSECCryptoException& e) {
221 throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
225 void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, const XMLCh* recipient)
228 throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
230 // Resolve a decryption key directly.
231 vector<const Credential*> creds;
232 int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
234 m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
235 m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types);
236 const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
238 m_criteria->setXMLAlgorithm(meth->getAlgorithm());
239 m_credResolver->resolve(creds,m_criteria);
242 CredentialCriteria criteria;
243 criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
244 criteria.setKeyInfo(encryptedData.getKeyInfo(), types);
245 const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
247 criteria.setXMLAlgorithm(meth->getAlgorithm());
248 m_credResolver->resolve(creds,&criteria);
251 // Loop over them and try each one.
253 for (vector<const Credential*>::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) {
255 key = (*cred)->getPrivateKey();
258 return decryptData(out, encryptedData, key);
260 catch(DecryptionException& ex) {
261 logging::Category::getInstance(XMLTOOLING_LOGCAT ".Decrypter").warn(ex.what());
265 // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm...
266 const XMLCh* algorithm=
267 encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
269 throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
271 // Check for external resolver.
272 const EncryptedKey* encKey=nullptr;
274 encKey = m_EKResolver->resolveKey(encryptedData, recipient);
276 EncryptedKeyResolver ekr;
277 encKey = ekr.resolveKey(encryptedData, recipient);
281 throw DecryptionException("Unable to locate an encrypted key.");
283 auto_ptr<XSECCryptoKey> keywrapper(decryptKey(*encKey, algorithm));
284 if (!keywrapper.get())
285 throw DecryptionException("Unable to decrypt the encrypted key.");
286 decryptData(out, encryptedData, keywrapper.get());
289 XSECCryptoKey* Decrypter::decryptKey(const EncryptedKey& encryptedKey, const XMLCh* algorithm)
292 throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
294 if (encryptedKey.getDOM()==nullptr)
295 throw DecryptionException("The object must be marshalled before decryption.");
297 XSECAlgorithmHandler* handler;
299 handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
301 throw DecryptionException("Unrecognized algorithm, no way to build object around decrypted key.");
303 catch(XSECException& e) {
304 auto_ptr_char temp(e.getMsg());
305 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
307 catch(XSECCryptoException& e) {
308 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
311 // We can reuse the cipher object if the document hasn't changed.
313 if (m_cipher && m_cipher->getDocument()!=encryptedKey.getDOM()->getOwnerDocument()) {
314 XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
319 m_cipher = XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument());
321 // Resolve key decryption keys.
322 int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
323 vector<const Credential*> creds;
325 m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
326 m_criteria->setKeyInfo(encryptedKey.getKeyInfo(), types);
327 const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
329 m_criteria->setXMLAlgorithm(meth->getAlgorithm());
330 m_credResolver->resolve(creds, m_criteria);
333 CredentialCriteria criteria;
334 criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
335 criteria.setKeyInfo(encryptedKey.getKeyInfo(), types);
336 const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
338 criteria.setXMLAlgorithm(meth->getAlgorithm());
339 m_credResolver->resolve(creds, &criteria);
342 throw DecryptionException("Unable to resolve any key decryption keys.");
344 XMLByte buffer[1024];
345 for (vector<const Credential*>::const_iterator cred = creds.begin(); cred != creds.end(); ++cred) {
347 if (!(*cred)->getPrivateKey())
348 throw DecryptionException("Credential did not contain a private key.");
349 memset(buffer,0,sizeof(buffer));
350 m_cipher->setKEK((*cred)->getPrivateKey()->clone());
353 int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024);
355 throw DecryptionException("Unable to decrypt key.");
357 // Try to wrap the key.
358 return handler->createKeyForURI(algorithm, buffer, keySize);
360 catch(XSECException& e) {
361 auto_ptr_char temp(e.getMsg());
362 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
364 catch(XSECCryptoException& e) {
365 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
368 catch(DecryptionException& ex) {
369 logging::Category::getInstance(XMLTOOLING_LOGCAT ".Decrypter").warn(ex.what());
373 // Some algorithms are vulnerable to chosen ciphertext attacks, so we generate a random key
374 // to prevent discovery of the validity of the original candidate.
375 logging::Category::getInstance(XMLTOOLING_LOGCAT ".Decrypter").warn(
376 "unable to decrypt key, generating random key for defensive purposes"
378 pair<const char*,unsigned int> mapped = XMLToolingConfig::getConfig().mapXMLAlgorithmToKeyAlgorithm(algorithm);
382 if (XSECPlatformUtils::g_cryptoProvider->getRandom(reinterpret_cast<unsigned char*>(buffer),mapped.second) < mapped.second)
383 throw DecryptionException("Unable to generate random data; was PRNG seeded?");
384 return handler->createKeyForURI(algorithm, buffer, mapped.second);
386 catch(XSECException& e) {
387 auto_ptr_char temp(e.getMsg());
388 throw DecryptionException(string("XMLSecurity exception while generating key: ") + temp.get());
390 catch (XSECCryptoException& e) {
391 throw DecryptionException(string("XMLSecurity exception while generating key: ") + e.getMsg());