Major revamp of credential and trust handling code, PKIX engine still needs work.
[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             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     // 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     const Credential* cred=NULL;
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             auto_ptr_char alg(meth->getAlgorithm());
176             m_criteria->setKeyAlgorithm(alg.get());
177         }
178         cred = m_credResolver->resolve(m_criteria);
179     }
180     else {
181         CredentialCriteria criteria;
182         criteria.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
183         criteria.setKeyInfo(encryptedKey.getKeyInfo());
184         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
185         if (meth) {
186             auto_ptr_char alg(meth->getAlgorithm());
187             criteria.setKeyAlgorithm(alg.get());
188         }
189         cred = m_credResolver->resolve(&criteria);
190     }
191     if (!cred || !cred->getPrivateKey())
192         throw DecryptionException("Unable to resolve a key decryption key.");
193
194     try {
195         m_cipher->setKEK(cred->getPrivateKey()->clone());
196         XMLByte buffer[1024];
197         memset(buffer,0,sizeof(buffer));
198         int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024);
199         if (keySize<=0)
200             throw DecryptionException("Unable to decrypt key.");
201
202         // Try to map the key.
203         XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
204         if (handler != NULL)
205             return handler->createKeyForURI(algorithm, buffer, keySize);
206         throw DecryptionException("Unrecognized algorithm, could not build object around decrypted key.");
207     }
208     catch(XSECException& e) {
209         auto_ptr_char temp(e.getMsg());
210         throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
211     }
212     catch(XSECCryptoException& e) {
213         throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
214     }
215 }