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