Merge branch '1.x' of ssh://authdev.it.ohio-state.edu/~scantor/git/cpp-xmltooling...
[shibboleth/cpp-xmltooling.git] / xmltooling / encryption / impl / Decrypter.cpp
1 /**
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.
6  *
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
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  */
20
21 /**
22  * Decrypter.cpp
23  * 
24  * Methods for decrypting XMLObjects and other data.
25  */
26
27 #include "internal.h"
28 #include "logging.h"
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"
35
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>
44
45 using namespace xmlencryption;
46 using namespace xmlsignature;
47 using namespace xmltooling;
48 using namespace xercesc;
49 using namespace std;
50
51 Decrypter::Decrypter(const CredentialResolver* credResolver, CredentialCriteria* criteria, const EncryptedKeyResolver* EKResolver)
52     : m_cipher(nullptr), m_credResolver(credResolver), m_criteria(criteria), m_EKResolver(EKResolver)
53 {
54 }
55
56 Decrypter::~Decrypter()
57 {
58     if (m_cipher)
59         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
60 }
61
62 void Decrypter::setEncryptedKeyResolver(const EncryptedKeyResolver* EKResolver)
63 {
64     m_EKResolver=EKResolver;
65 }
66
67 void Decrypter::setKEKResolver(const CredentialResolver* resolver, CredentialCriteria* criteria)
68 {
69     m_credResolver=resolver;
70     m_criteria=criteria;
71 }
72
73 DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, XSECCryptoKey* key)
74 {
75     if (encryptedData.getDOM()==nullptr)
76         throw DecryptionException("The object must be marshalled before decryption.");
77
78     // We can reuse the cipher object if the document hasn't changed.
79
80     if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) {
81         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
82         m_cipher=nullptr;
83     }
84     
85     if (!m_cipher)
86         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument());
87
88     try {
89         m_cipher->setKey(key->clone());
90         DOMNode* ret=m_cipher->decryptElementDetached(encryptedData.getDOM());
91         if (ret->getNodeType()!=DOMNode::DOCUMENT_FRAGMENT_NODE) {
92             ret->release();
93             throw DecryptionException("Decryption operation did not result in DocumentFragment.");
94         }
95         return static_cast<DOMDocumentFragment*>(ret);
96     }
97     catch(XSECException& e) {
98         auto_ptr_char temp(e.getMsg());
99         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
100     }
101     catch(XSECCryptoException& e) {
102         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
103     }
104 }
105
106 DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, const XMLCh* recipient)
107 {
108     if (!m_credResolver)
109         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
110
111     // Resolve a decryption key directly.
112     vector<const Credential*> creds;
113     int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
114     if (m_criteria) {
115         m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
116         m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types);
117         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
118         if (meth)
119             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
120         m_credResolver->resolve(creds,m_criteria);
121     }
122     else {
123         CredentialCriteria criteria;
124         criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
125         criteria.setKeyInfo(encryptedData.getKeyInfo(), types);
126         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
127         if (meth)
128             criteria.setXMLAlgorithm(meth->getAlgorithm());
129         m_credResolver->resolve(creds,&criteria);
130     }
131
132     // Loop over them and try each one.
133     XSECCryptoKey* key;
134     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
135         try {
136             key = (*cred)->getPrivateKey();
137             if (!key)
138                 continue;
139             return decryptData(encryptedData, key);
140         }
141         catch(DecryptionException& ex) {
142             logging::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
143         }
144     }
145
146     // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm...
147     const XMLCh* algorithm=
148         encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
149     if (!algorithm)
150         throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
151     
152     // Check for external resolver.
153     const EncryptedKey* encKey=nullptr;
154     if (m_EKResolver)
155         encKey = m_EKResolver->resolveKey(encryptedData, recipient);
156     else {
157         EncryptedKeyResolver ekr;
158         encKey = ekr.resolveKey(encryptedData, recipient);
159     }
160
161     if (!encKey)
162         throw DecryptionException("Unable to locate an encrypted key.");
163
164     auto_ptr<XSECCryptoKey> keywrapper(decryptKey(*encKey, algorithm));
165     if (!keywrapper.get())
166         throw DecryptionException("Unable to decrypt the encrypted key.");
167     return decryptData(encryptedData, keywrapper.get());
168 }
169
170 void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, XSECCryptoKey* key)
171 {
172     if (encryptedData.getDOM()==nullptr)
173         throw DecryptionException("The object must be marshalled before decryption.");
174
175     // We can reuse the cipher object if the document hasn't changed.
176
177     if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) {
178         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
179         m_cipher=nullptr;
180     }
181     
182     if (!m_cipher)
183         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument());
184
185     try {
186         m_cipher->setKey(key->clone());
187         auto_ptr<XSECBinTXFMInputStream> in(m_cipher->decryptToBinInputStream(encryptedData.getDOM()));
188         
189         XMLByte buf[8192];
190         xsecsize_t count = in->readBytes(buf, sizeof(buf));
191         while (count > 0)
192             out.write(reinterpret_cast<char*>(buf),count);
193     }
194     catch(XSECException& e) {
195         auto_ptr_char temp(e.getMsg());
196         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
197     }
198     catch(XSECCryptoException& e) {
199         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
200     }
201 }
202
203 void Decrypter::decryptData(ostream& out, const EncryptedData& encryptedData, const XMLCh* recipient)
204 {
205     if (!m_credResolver)
206         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
207
208     // Resolve a decryption key directly.
209     vector<const Credential*> creds;
210     int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
211     if (m_criteria) {
212         m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
213         m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types);
214         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
215         if (meth)
216             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
217         m_credResolver->resolve(creds,m_criteria);
218     }
219     else {
220         CredentialCriteria criteria;
221         criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
222         criteria.setKeyInfo(encryptedData.getKeyInfo(), types);
223         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
224         if (meth)
225             criteria.setXMLAlgorithm(meth->getAlgorithm());
226         m_credResolver->resolve(creds,&criteria);
227     }
228
229     // Loop over them and try each one.
230     XSECCryptoKey* key;
231     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
232         try {
233             key = (*cred)->getPrivateKey();
234             if (!key)
235                 continue;
236             return decryptData(out, encryptedData, key);
237         }
238         catch(DecryptionException& ex) {
239             logging::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
240         }
241     }
242
243     // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm...
244     const XMLCh* algorithm=
245         encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : nullptr;
246     if (!algorithm)
247         throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
248     
249     // Check for external resolver.
250     const EncryptedKey* encKey=nullptr;
251     if (m_EKResolver)
252         encKey = m_EKResolver->resolveKey(encryptedData, recipient);
253     else {
254         EncryptedKeyResolver ekr;
255         encKey = ekr.resolveKey(encryptedData, recipient);
256     }
257
258     if (!encKey)
259         throw DecryptionException("Unable to locate an encrypted key.");
260
261     auto_ptr<XSECCryptoKey> keywrapper(decryptKey(*encKey, algorithm));
262     if (!keywrapper.get())
263         throw DecryptionException("Unable to decrypt the encrypted key.");
264     decryptData(out, encryptedData, keywrapper.get());
265 }
266
267 XSECCryptoKey* Decrypter::decryptKey(const EncryptedKey& encryptedKey, const XMLCh* algorithm)
268 {
269     if (!m_credResolver)
270         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
271
272     if (encryptedKey.getDOM()==nullptr)
273         throw DecryptionException("The object must be marshalled before decryption.");
274
275     XSECAlgorithmHandler* handler;
276     try {
277         handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
278         if (!handler)
279             throw DecryptionException("Unrecognized algorithm, no way to build object around decrypted key.");
280     }
281     catch(XSECException& e) {
282         auto_ptr_char temp(e.getMsg());
283         throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
284     }
285     catch(XSECCryptoException& e) {
286         throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
287     }
288     
289     // We can reuse the cipher object if the document hasn't changed.
290
291     if (m_cipher && m_cipher->getDocument()!=encryptedKey.getDOM()->getOwnerDocument()) {
292         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
293         m_cipher=nullptr;
294     }
295     
296     if (!m_cipher)
297         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument());
298     
299     // Resolve key decryption keys.
300     int types = CredentialCriteria::KEYINFO_EXTRACTION_KEY | CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES;
301     vector<const Credential*> creds;
302     if (m_criteria) {
303         m_criteria->setUsage(Credential::ENCRYPTION_CREDENTIAL);
304         m_criteria->setKeyInfo(encryptedKey.getKeyInfo(), types);
305         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
306         if (meth)
307             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
308         m_credResolver->resolve(creds, m_criteria);
309     }
310     else {
311         CredentialCriteria criteria;
312         criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
313         criteria.setKeyInfo(encryptedKey.getKeyInfo(), types);
314         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
315         if (meth)
316             criteria.setXMLAlgorithm(meth->getAlgorithm());
317         m_credResolver->resolve(creds, &criteria);
318     }
319     if (creds.empty())
320         throw DecryptionException("Unable to resolve any key decryption keys.");
321
322     XMLByte buffer[1024];
323     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
324         try {
325             if (!(*cred)->getPrivateKey())
326                 throw DecryptionException("Credential did not contain a private key.");
327             memset(buffer,0,sizeof(buffer));
328             m_cipher->setKEK((*cred)->getPrivateKey()->clone());
329
330             try {
331                 int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024);
332                 if (keySize<=0)
333                     throw DecryptionException("Unable to decrypt key.");
334         
335                 // Try to wrap the key.
336                 return handler->createKeyForURI(algorithm, buffer, keySize);
337             }
338             catch(XSECException& e) {
339                 auto_ptr_char temp(e.getMsg());
340                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
341             }
342             catch(XSECCryptoException& e) {
343                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
344             }
345         }
346         catch(DecryptionException& ex) {
347             logging::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
348         }
349     }
350     
351     throw DecryptionException("Unable to decrypt key.");
352 }