Revert to loop for decrypting keys for now, will revisit later.
[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 "encryption/Decrypter.h"
25 #include "encryption/EncryptedKeyResolver.h"
26 #include "security/Credential.h"
27 #include "security/CredentialCriteria.h"
28 #include "security/CredentialResolver.h"
29
30 #include <log4cpp/Category.hh>
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/xenc/XENCEncryptedData.hpp>
36 #include <xsec/xenc/XENCEncryptedKey.hpp>
37
38 using namespace xmlencryption;
39 using namespace xmlsignature;
40 using namespace xmltooling;
41 using namespace std;
42
43 Decrypter::~Decrypter()
44 {
45     if (m_cipher)
46         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
47 }
48
49 DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, XSECCryptoKey* key)
50 {
51     if (encryptedData.getDOM()==NULL)
52         throw DecryptionException("The object must be marshalled before decryption.");
53
54     // We can reuse the cipher object if the document hasn't changed.
55
56     if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) {
57         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
58         m_cipher=NULL;
59     }
60     
61     if (!m_cipher)
62         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument());
63
64     try {
65         m_cipher->setKey(key->clone());
66         DOMNode* ret=m_cipher->decryptElementDetached(encryptedData.getDOM());
67         if (ret->getNodeType()!=DOMNode::DOCUMENT_FRAGMENT_NODE) {
68             ret->release();
69             throw DecryptionException("Decryption operation did not result in DocumentFragment.");
70         }
71         return static_cast<DOMDocumentFragment*>(ret);
72     }
73     catch(XSECException& e) {
74         auto_ptr_char temp(e.getMsg());
75         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
76     }
77     catch(XSECCryptoException& e) {
78         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
79     }
80 }
81
82 DOMDocumentFragment* Decrypter::decryptData(const EncryptedData& encryptedData, const XMLCh* recipient)
83 {
84     if (!m_credResolver)
85         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
86
87     // Resolve a decryption key directly.
88     vector<const Credential*> creds;
89     if (m_criteria) {
90         m_criteria->setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
91         m_criteria->setKeyInfo(encryptedData.getKeyInfo());
92         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
93         if (meth) {
94             auto_ptr_char alg(meth->getAlgorithm());
95             m_criteria->setKeyAlgorithm(alg.get());
96         }
97         m_credResolver->resolve(creds,m_criteria);
98     }
99     else {
100         CredentialCriteria criteria;
101         criteria.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
102         criteria.setKeyInfo(encryptedData.getKeyInfo());
103         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
104         if (meth) {
105             auto_ptr_char alg(meth->getAlgorithm());
106             criteria.setKeyAlgorithm(alg.get());
107         }
108         m_credResolver->resolve(creds,&criteria);
109     }
110
111     // Loop over them and try each one.
112     XSECCryptoKey* key;
113     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
114         try {
115             key = (*cred)->getPrivateKey();
116             if (!key)
117                 continue;
118             return decryptData(encryptedData, key);
119         }
120         catch(DecryptionException& ex) {
121             log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
122         }
123     }
124
125     // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm...
126     const XMLCh* algorithm=
127         encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : NULL;
128     if (!algorithm)
129         throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
130     
131     // Check for external resolver.
132     const EncryptedKey* encKey=NULL;
133     if (m_EKResolver)
134         encKey = m_EKResolver->resolveKey(encryptedData, recipient);
135     else {
136         EncryptedKeyResolver ekr;
137         encKey = ekr.resolveKey(encryptedData, recipient);
138     }
139
140     if (!encKey)
141         throw DecryptionException("Unable to locate an encrypted key.");
142
143     auto_ptr<XSECCryptoKey> keywrapper(decryptKey(*encKey, algorithm));
144     if (!keywrapper.get())
145         throw DecryptionException("Unable to decrypt the encrypted key.");
146     return decryptData(encryptedData, keywrapper.get());
147 }
148
149 XSECCryptoKey* Decrypter::decryptKey(const EncryptedKey& encryptedKey, const XMLCh* algorithm)
150 {
151     if (!m_credResolver)
152         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
153
154     if (encryptedKey.getDOM()==NULL)
155         throw DecryptionException("The object must be marshalled before decryption.");
156
157     XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
158     if (!handler)
159         throw DecryptionException("Unrecognized algorithm, no way to build object around decrypted key.");
160     
161     // We can reuse the cipher object if the document hasn't changed.
162
163     if (m_cipher && m_cipher->getDocument()!=encryptedKey.getDOM()->getOwnerDocument()) {
164         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
165         m_cipher=NULL;
166     }
167     
168     if (!m_cipher)
169         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument());
170     
171     // Resolve key decryption key. We can't loop over possible credentials because
172     // we can't tell a valid decrypt from an invalid one.
173     vector<const Credential*> creds;
174     if (m_criteria) {
175         m_criteria->setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
176         m_criteria->setKeyInfo(encryptedKey.getKeyInfo());
177         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
178         if (meth) {
179             auto_ptr_char alg(meth->getAlgorithm());
180             m_criteria->setKeyAlgorithm(alg.get());
181         }
182         m_credResolver->resolve(creds, m_criteria);
183     }
184     else {
185         CredentialCriteria criteria;
186         criteria.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
187         criteria.setKeyInfo(encryptedKey.getKeyInfo());
188         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
189         if (meth) {
190             auto_ptr_char alg(meth->getAlgorithm());
191             criteria.setKeyAlgorithm(alg.get());
192         }
193         m_credResolver->resolve(creds, &criteria);
194     }
195     if (creds.empty())
196         throw DecryptionException("Unable to resolve any key decryption keys.");
197
198     XMLByte buffer[1024];
199     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
200         try {
201             if (!(*cred)->getPrivateKey())
202                 throw DecryptionException("Credential did not contain a private key.");
203             memset(buffer,0,sizeof(buffer));
204             m_cipher->setKEK((*cred)->getPrivateKey()->clone());
205
206             try {
207                 int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024);
208                 if (keySize<=0)
209                     throw DecryptionException("Unable to decrypt key.");
210         
211                 // Try to wrap the key.
212                 return handler->createKeyForURI(algorithm, buffer, keySize);
213             }
214             catch(XSECException& e) {
215                 auto_ptr_char temp(e.getMsg());
216                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
217             }
218             catch(XSECCryptoException& e) {
219                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
220             }
221         }
222         catch(DecryptionException& ex) {
223             log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
224         }
225     }
226     
227     throw DecryptionException("Unable to decrypt key.");
228 }