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