Add credential context APIs, refactor criteria matching, auto-extract criteria from...
[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     int types =
90         CredentialCriteria::KEYINFO_EXTRACTION_KEY |
91         CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES |
92         CredentialCriteria::KEYINFO_EXTRACTION_IMPLICIT_KEYNAMES;
93     if (m_criteria) {
94         m_criteria->setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
95         m_criteria->setKeyInfo(encryptedData.getKeyInfo(), types);
96         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
97         if (meth)
98             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
99         m_credResolver->resolve(creds,m_criteria);
100     }
101     else {
102         CredentialCriteria criteria;
103         criteria.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
104         criteria.setKeyInfo(encryptedData.getKeyInfo(), types);
105         const EncryptionMethod* meth = encryptedData.getEncryptionMethod();
106         if (meth)
107             criteria.setXMLAlgorithm(meth->getAlgorithm());
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 keys.
172     int types =
173         CredentialCriteria::KEYINFO_EXTRACTION_KEY |
174         CredentialCriteria::KEYINFO_EXTRACTION_KEYNAMES |
175         CredentialCriteria::KEYINFO_EXTRACTION_IMPLICIT_KEYNAMES;
176     vector<const Credential*> creds;
177     if (m_criteria) {
178         m_criteria->setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
179         m_criteria->setKeyInfo(encryptedKey.getKeyInfo(), types);
180         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
181         if (meth)
182             m_criteria->setXMLAlgorithm(meth->getAlgorithm());
183         m_credResolver->resolve(creds, m_criteria);
184     }
185     else {
186         CredentialCriteria criteria;
187         criteria.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL);
188         criteria.setKeyInfo(encryptedKey.getKeyInfo(), types);
189         const EncryptionMethod* meth = encryptedKey.getEncryptionMethod();
190         if (meth)
191             criteria.setXMLAlgorithm(meth->getAlgorithm());
192         m_credResolver->resolve(creds, &criteria);
193     }
194     if (creds.empty())
195         throw DecryptionException("Unable to resolve any key decryption keys.");
196
197     XMLByte buffer[1024];
198     for (vector<const Credential*>::const_iterator cred = creds.begin(); cred!=creds.end(); ++cred) {
199         try {
200             if (!(*cred)->getPrivateKey())
201                 throw DecryptionException("Credential did not contain a private key.");
202             memset(buffer,0,sizeof(buffer));
203             m_cipher->setKEK((*cred)->getPrivateKey()->clone());
204
205             try {
206                 int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024);
207                 if (keySize<=0)
208                     throw DecryptionException("Unable to decrypt key.");
209         
210                 // Try to wrap the key.
211                 return handler->createKeyForURI(algorithm, buffer, keySize);
212             }
213             catch(XSECException& e) {
214                 auto_ptr_char temp(e.getMsg());
215                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + temp.get());
216             }
217             catch(XSECCryptoException& e) {
218                 throw DecryptionException(string("XMLSecurity exception while decrypting key: ") + e.getMsg());
219             }
220         }
221         catch(DecryptionException& ex) {
222             log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(ex.what());
223         }
224     }
225     
226     throw DecryptionException("Unable to decrypt key.");
227 }