Algorithm and key size criteria, incoming signature algorithm extraction.
[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 "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             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
95         m_credResolver->resolve(creds,m_criteria);
96     }
97     else {
98         CredentialCriteria criteria;
99         criteria.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
100         criteria.setKeyInfo(encryptedData.getKeyInfo());
101         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
102         if (meth)
103             criteria.setXMLAlgorithm(meth->getAlgorithm());
104         m_credResolver->resolve(creds,&criteria);
105     }
106
107     // Loop over them and try each one.
108     XSECCryptoKey* key;
109     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
110         try {
111             key = (*cred)->getPrivateKey();
112             if (!key)
113                 continue;
114             return decryptData(encryptedData, key);
115         }
116         catch(DecryptionException& ex) {
117             log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
118         }
119     }
120
121     // We need to find an encrypted decryption key somewhere. We'll need the underlying algorithm...
122     const XMLCh* algorithm=
123         encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : NULL;
124     if (!algorithm)
125         throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
126     
127     // Check for external resolver.
128     const EncryptedKey* encKey=NULL;
129     if (m_EKResolver)
130         encKey = m_EKResolver->resolveKey(encryptedData, recipient);
131     else {
132         EncryptedKeyResolver ekr;
133         encKey = ekr.resolveKey(encryptedData, recipient);
134     }
135
136     if (!encKey)
137         throw DecryptionException("Unable to locate an encrypted key.");
138
139     auto_ptr<XSECCryptoKey> keywrapper(decryptKey(*encKey, algorithm));
140     if (!keywrapper.get())
141         throw DecryptionException("Unable to decrypt the encrypted key.");
142     return decryptData(encryptedData, keywrapper.get());
143 }
144
145 XSECCryptoKey* Decrypter::decryptKey(const EncryptedKey& encryptedKey, const XMLCh* algorithm)
146 {
147     if (!m_credResolver)
148         throw DecryptionException("No CredentialResolver supplied to provide decryption keys.");
149
150     if (encryptedKey.getDOM()==NULL)
151         throw DecryptionException("The object must be marshalled before decryption.");
152
153     XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
154     if (!handler)
155         throw DecryptionException("Unrecognized algorithm, no way to build object around decrypted key.");
156     
157     // We can reuse the cipher object if the document hasn't changed.
158
159     if (m_cipher && m_cipher->getDocument()!=encryptedKey.getDOM()->getOwnerDocument()) {
160         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
161         m_cipher=NULL;
162     }
163     
164     if (!m_cipher)
165         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument());
166     
167     // Resolve key decryption key. We can't loop over possible credentials because
168     // we can't tell a valid decrypt from an invalid one.
169     vector<const Credential*> creds;
170     if (m_criteria) {
171         m_criteria->setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
172         m_criteria->setKeyInfo(encryptedKey.getKeyInfo());
173         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
174         if (meth)
175             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
176         m_credResolver->resolve(creds, m_criteria);
177     }
178     else {
179         CredentialCriteria criteria;
180         criteria.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
181         criteria.setKeyInfo(encryptedKey.getKeyInfo());
182         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
183         if (meth)
184             criteria.setXMLAlgorithm(meth->getAlgorithm());
185         m_credResolver->resolve(creds, &criteria);
186     }
187     if (creds.empty())
188         throw DecryptionException("Unable to resolve any key decryption keys.");
189
190     XMLByte buffer[1024];
191     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
192         try {
193             if (!(*cred)->getPrivateKey())
194                 throw DecryptionException("Credential did not contain a private key.");
195             memset(buffer,0,sizeof(buffer));
196             m_cipher->setKEK((*cred)->getPrivateKey()->clone());
197
198             try {
199                 int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024);
200                 if (keySize<=0)
201                     throw DecryptionException("Unable to decrypt key.");
202         
203                 // Try to wrap the key.
204                 return handler->createKeyForURI(algorithm, buffer, keySize);
205             }
206             catch(XSECException& e) {
207                 auto_ptr_char temp(e.getMsg());
208                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
209             }
210             catch(XSECCryptoException& e) {
211                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
212             }
213         }
214         catch(DecryptionException& ex) {
215             log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
216         }
217     }
218     
219     throw DecryptionException("Unable to decrypt key.");
220 }