Revised decryption APIs to clarify CredentialResolver/KeyResolver difference.
[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/CredentialResolver.h"
27
28 #include <log4cpp/Category.hh>
29 #include <xsec/enc/XSECCryptoException.hpp>
30 #include <xsec/framework/XSECException.hpp>
31 #include <xsec/framework/XSECAlgorithmMapper.hpp>
32 #include <xsec/framework/XSECAlgorithmHandler.hpp>
33 #include <xsec/xenc/XENCEncryptedData.hpp>
34 #include <xsec/xenc/XENCEncryptedKey.hpp>
35
36 using namespace xmlencryption;
37 using namespace xmlsignature;
38 using namespace xmltooling;
39 using namespace std;
40
41 Decrypter::~Decrypter()
42 {
43     if (m_cipher)
44         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
45 }
46
47 DOMDocumentFragment* Decrypter::decryptData(EncryptedData& encryptedData)
48 {
49     if (encryptedData.getDOM()==NULL)
50         throw DecryptionException("The object must be marshalled before decryption.");
51     
52     // We can reuse the cipher object if the document hasn't changed.
53
54     if (m_cipher && m_cipher->getDocument()!=encryptedData.getDOM()->getOwnerDocument()) {
55         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
56         m_cipher=NULL;
57     }
58     
59     if (!m_cipher)
60         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedData.getDOM()->getOwnerDocument());
61     
62     try {
63         // Resolve decryption key.
64         XSECCryptoKey* key=NULL;
65         if (m_resolver)
66             key=m_resolver->resolveKey(encryptedData.getKeyInfo());
67
68         if (!key && m_KEKresolver) {
69             // See if there's an encrypted key available. We'll need the algorithm...
70             const XMLCh* algorithm=
71                 encryptedData.getEncryptionMethod() ? encryptedData.getEncryptionMethod()->getAlgorithm() : NULL;
72             if (!algorithm)
73                 throw DecryptionException("No EncryptionMethod/@Algorithm set, key decryption cannot proceed.");
74             
75             if (encryptedData.getKeyInfo()) {
76                 const vector<XMLObject*>& others=const_cast<const KeyInfo*>(encryptedData.getKeyInfo())->getUnknownXMLObjects();
77                 for (vector<XMLObject*>::const_iterator i=others.begin(); i!=others.end(); i++) {
78                     EncryptedKey* encKey=dynamic_cast<EncryptedKey*>(*i);
79                     if (encKey) {
80                         try {
81                             key=decryptKey(*encKey, algorithm);
82                         }
83                         catch (DecryptionException& e) {
84                             log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(e.what());
85                         }
86                     }
87                 }
88             }
89             
90             if (!key) {
91                 // Check for a non-trivial resolver.
92                 const EncryptedKeyResolver* ekr=dynamic_cast<const EncryptedKeyResolver*>(m_resolver);
93                 if (ekr) {
94                     EncryptedKey* encKey=ekr->resolveKey(encryptedData);
95                     if (encKey) {
96                         try {
97                             key=decryptKey(*encKey, algorithm);
98                         }
99                         catch (DecryptionException& e) {
100                             log4cpp::Category::getInstance(XMLTOOLING_LOGCAT".Decrypter").warn(e.what());
101                         }
102                     }
103                 }
104             }
105         }
106
107         if (!key)
108             throw DecryptionException("Unable to resolve a decryption key.");
109         
110         m_cipher->setKey(key);
111         DOMNode* ret=m_cipher->decryptElementDetached(encryptedData.getDOM());
112         if (ret->getNodeType()!=DOMNode::DOCUMENT_FRAGMENT_NODE) {
113             ret->release();
114             throw DecryptionException("Decryption operation did not result in DocumentFragment.");
115         }
116         return static_cast<DOMDocumentFragment*>(ret);
117     }
118     catch(XSECException& e) {
119         auto_ptr_char temp(e.getMsg());
120         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
121     }
122     catch(XSECCryptoException& e) {
123         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
124     }
125 }
126
127 XSECCryptoKey* Decrypter::decryptKey(EncryptedKey& encryptedKey, const XMLCh* algorithm)
128 {
129     if (encryptedKey.getDOM()==NULL)
130         throw DecryptionException("The object must be marshalled before decryption.");
131     
132     // We can reuse the cipher object if the document hasn't changed.
133
134     if (m_cipher && m_cipher->getDocument()!=encryptedKey.getDOM()->getOwnerDocument()) {
135         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
136         m_cipher=NULL;
137     }
138     
139     if (!m_cipher)
140         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(encryptedKey.getDOM()->getOwnerDocument());
141     
142     try {
143         // Resolve key decryption key.
144         XSECCryptoKey* key=NULL;
145         if (m_KEKresolver)
146             key=m_KEKresolver->getKey(encryptedKey.getKeyInfo());
147         if (!key)
148             throw DecryptionException("Unable to resolve a key decryption key.");
149         m_cipher->setKEK(key);
150         
151         XMLByte buffer[1024];
152         int keySize = m_cipher->decryptKey(encryptedKey.getDOM(), buffer, 1024);
153         if (keySize > 0) {
154             // Try to map the key.
155             XSECAlgorithmHandler* handler = XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(algorithm);
156             if (handler != NULL)
157                 return handler->createKeyForURI(algorithm, buffer, keySize);
158             throw DecryptionException("Unrecognized algorithm, could not build object around decrypted key.");
159         }
160         throw DecryptionException("Unable to decrypt key.");
161     }
162     catch(XSECException& e) {
163         auto_ptr_char temp(e.getMsg());
164         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + temp.get());
165     }
166     catch(XSECCryptoException& e) {
167         throw DecryptionException(string("XMLSecurity exception while decrypting: ") + e.getMsg());
168     }
169 }