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