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