45e9e073351981af31348a2f9580bdbdd7445985
[shibboleth/cpp-xmltooling.git] / xmltooling / encryption / impl / Encrypter.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  * Encrypter.cpp
19  * 
20  * Methods for encrypting XMLObjects and other data.
21  */
22
23 #include "internal.h"
24 #include "encryption/Encrypter.h"
25 #include "security/Credential.h"
26
27 #include <xsec/enc/XSECCryptoException.hpp>
28 #include <xsec/framework/XSECException.hpp>
29 #include <xsec/framework/XSECAlgorithmMapper.hpp>
30 #include <xsec/framework/XSECAlgorithmHandler.hpp>
31 #include <xsec/xenc/XENCEncryptedData.hpp>
32 #include <xsec/xenc/XENCEncryptedKey.hpp>
33
34 using namespace xmlencryption;
35 using namespace xmlsignature;
36 using namespace xmltooling;
37 using namespace std;
38
39 Encrypter::~Encrypter()
40 {
41     XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
42     memset(m_keyBuffer,0,32);
43 }
44
45 void Encrypter::checkParams(EncryptionParams& encParams, KeyEncryptionParams* kencParams)
46 {
47     if (encParams.m_keyBufferSize==0) {
48         if (encParams.m_credential) {
49             if (kencParams)
50                 throw EncryptionException("Generating EncryptedKey inline requires the encryption key in raw form.");
51         }
52         else if (!encParams.m_credential) {
53             if (!kencParams)
54                 throw EncryptionException("Using a generated encryption key requires a KeyEncryptionParams object.");
55
56             // We're generating a random key. The maximum supported length is AES-256, so we need 32 bytes.
57             if (XSECPlatformUtils::g_cryptoProvider->getRandom(m_keyBuffer,32)<32)
58                 throw EncryptionException("Unable to generate random data; was PRNG seeded?");
59             encParams.m_keyBuffer=m_keyBuffer;
60             encParams.m_keyBufferSize=32;
61         }
62     }
63     
64     XSECCryptoKey* key=NULL;
65     if (encParams.m_credential) {
66         key = encParams.m_credential->getPrivateKey();
67         if (!key)
68             throw EncryptionException("Credential in EncryptionParams structure did not supply a private/secret key.");
69         // Set the encryption key.
70         m_cipher->setKey(key->clone());
71     }
72     else {
73         // We have to have a raw key now, so we need to build a wrapper around it.
74         XSECAlgorithmHandler* handler =XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(encParams.m_algorithm);
75         if (handler != NULL)
76             key = handler->createKeyForURI(
77                 encParams.m_algorithm,const_cast<unsigned char*>(encParams.m_keyBuffer),encParams.m_keyBufferSize
78                 );
79
80         if (!key)
81             throw EncryptionException("Unable to build wrapper for key, unknown algorithm?");
82         // Set the encryption key.
83         m_cipher->setKey(key);
84     }
85 }
86
87 EncryptedData* Encrypter::encryptElement(DOMElement* element, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
88 {
89     // We can reuse the cipher object if the document hasn't changed.
90     
91     if (m_cipher && m_cipher->getDocument()!=element->getOwnerDocument()) {
92         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
93         m_cipher=NULL;
94     }
95     
96     if (!m_cipher) {
97         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(element->getOwnerDocument());
98         m_cipher->setExclusiveC14nSerialisation(false);
99     }
100     
101     try {
102         checkParams(encParams,kencParams);
103         m_cipher->encryptElementDetached(element, ENCRYPT_NONE, encParams.m_algorithm);
104         return decorateAndUnmarshall(encParams, kencParams);
105     }
106     catch(XSECException& e) {
107         auto_ptr_char temp(e.getMsg());
108         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
109     }
110     catch(XSECCryptoException& e) {
111         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
112     }
113 }
114
115 EncryptedData* Encrypter::encryptElementContent(DOMElement* element, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
116 {
117     // We can reuse the cipher object if the document hasn't changed.
118
119     if (m_cipher && m_cipher->getDocument()!=element->getOwnerDocument()) {
120         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
121         m_cipher=NULL;
122     }
123     
124     if (!m_cipher) {
125         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(element->getOwnerDocument());
126         m_cipher->setExclusiveC14nSerialisation(false);
127     }
128     
129     try {
130         checkParams(encParams,kencParams);
131         m_cipher->encryptElementContentDetached(element, ENCRYPT_NONE, encParams.m_algorithm);
132         return decorateAndUnmarshall(encParams, kencParams);
133     }
134     catch(XSECException& e) {
135         auto_ptr_char temp(e.getMsg());
136         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
137     }
138     catch(XSECCryptoException& e) {
139         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
140     }
141 }
142
143 EncryptedData* Encrypter::encryptStream(istream& input, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
144 {
145     // Get a fresh cipher object and document.
146
147     if (m_cipher) {
148         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
149         m_cipher=NULL;
150     }
151     
152     DOMDocument* doc=NULL;
153     try {
154         doc=XMLToolingConfig::getConfig().getParser().newDocument();
155         XercesJanitor<DOMDocument> janitor(doc);
156         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(doc);
157         m_cipher->setExclusiveC14nSerialisation(false);
158         
159         checkParams(encParams,kencParams);
160         StreamInputSource::StreamBinInputStream xstream(input);
161         m_cipher->encryptBinInputStream(&xstream, ENCRYPT_NONE, encParams.m_algorithm);
162         return decorateAndUnmarshall(encParams, kencParams);
163     }
164     catch(XSECException& e) {
165         auto_ptr_char temp(e.getMsg());
166         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
167     }
168     catch(XSECCryptoException& e) {
169         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
170     }
171 }
172
173 EncryptedData* Encrypter::decorateAndUnmarshall(EncryptionParams& encParams, KeyEncryptionParams* kencParams)
174 {
175     XENCEncryptedData* encData=m_cipher->getEncryptedData();
176     if (!encData)
177         throw EncryptionException("No EncryptedData element found?");
178
179     // Unmarshall a tooling version of EncryptedData around the DOM.
180     EncryptedData* xmlEncData=NULL;
181     auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(encData->getElement()));
182     if (!(xmlObject.get()) || !(xmlEncData=dynamic_cast<EncryptedData*>(xmlObject.get())))
183         throw EncryptionException("Unable to unmarshall into EncryptedData object.");
184     
185     // Unbind from DOM so we can divorce this from the original document.
186     xmlEncData->releaseThisAndChildrenDOM();
187     
188     // KeyInfo?
189     const KeyInfo* kinfo = encParams.m_credential ? encParams.m_credential->getKeyInfo(encParams.m_compact) : NULL;
190     if (kinfo)
191         xmlEncData->setKeyInfo(kinfo->cloneKeyInfo());
192     
193     // Are we doing a key encryption?
194     if (kencParams) {
195         XSECCryptoKey* kek = kencParams->m_credential.getPublicKey();
196         if (!kek)
197             throw EncryptionException("Credential in KeyEncryptionParams structure did not supply a public key.");
198
199         m_cipher->setKEK(kek->clone());
200         // ownership of this belongs to us, for some reason...
201         auto_ptr<XENCEncryptedKey> encKey(
202             m_cipher->encryptKey(encParams.m_keyBuffer, encParams.m_keyBufferSize, ENCRYPT_NONE, kencParams->m_algorithm)
203             );
204         EncryptedKey* xmlEncKey=NULL;
205         auto_ptr<XMLObject> xmlObjectKey(XMLObjectBuilder::buildOneFromElement(encKey->getElement()));
206         if (!(xmlObjectKey.get()) || !(xmlEncKey=dynamic_cast<EncryptedKey*>(xmlObjectKey.get())))
207             throw EncryptionException("Unable to unmarshall into EncryptedKey object.");
208         
209         xmlEncKey->releaseThisAndChildrenDOM();
210         
211         // Recipient?
212         if (kencParams->m_recipient)
213             xmlEncKey->setRecipient(kencParams->m_recipient);
214         
215         // KeyInfo?
216         kinfo = kencParams->m_credential.getKeyInfo(encParams.m_compact);
217         if (kinfo)
218             xmlEncKey->setKeyInfo(kinfo->cloneKeyInfo());
219         
220         // Add the EncryptedKey inline.
221         if (!xmlEncData->getKeyInfo())
222             xmlEncData->setKeyInfo(KeyInfoBuilder::buildKeyInfo());
223         xmlEncData->getKeyInfo()->getUnknownXMLObjects().push_back(xmlEncKey);
224         xmlObjectKey.release();
225     }
226     
227     xmlObject.release();
228     return xmlEncData;
229 }
230
231 EncryptedKey* Encrypter::encryptKey(const unsigned char* keyBuffer, unsigned int keyBufferSize, KeyEncryptionParams& kencParams, bool compact)
232 {
233     // Get a fresh cipher object and document.
234
235     if (m_cipher) {
236         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
237         m_cipher=NULL;
238     }
239
240     XSECCryptoKey* kek = kencParams.m_credential.getPublicKey();
241     if (!kek)
242         throw EncryptionException("Credential in KeyEncryptionParams structure did not supply a public key.");
243
244     DOMDocument* doc=NULL;
245     try {
246         doc=XMLToolingConfig::getConfig().getParser().newDocument();
247         XercesJanitor<DOMDocument> janitor(doc);
248         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(doc);
249         m_cipher->setExclusiveC14nSerialisation(false);
250         m_cipher->setKEK(kek->clone());
251         auto_ptr<XENCEncryptedKey> encKey(m_cipher->encryptKey(keyBuffer, keyBufferSize, ENCRYPT_NONE, kencParams.m_algorithm));
252         
253         EncryptedKey* xmlEncKey=NULL;
254         auto_ptr<XMLObject> xmlObjectKey(XMLObjectBuilder::buildOneFromElement(encKey->getElement()));
255         if (!(xmlObjectKey.get()) || !(xmlEncKey=dynamic_cast<EncryptedKey*>(xmlObjectKey.get())))
256             throw EncryptionException("Unable to unmarshall into EncryptedKey object.");
257         
258         xmlEncKey->releaseThisAndChildrenDOM();
259         
260         // Recipient?
261         if (kencParams.m_recipient)
262             xmlEncKey->setRecipient(kencParams.m_recipient);
263
264         // KeyInfo?
265         const KeyInfo* kinfo = kencParams.m_credential.getKeyInfo(compact);
266         if (kinfo)
267             xmlEncKey->setKeyInfo(kinfo->cloneKeyInfo());
268
269         xmlObjectKey.release();
270         return xmlEncKey;
271     }
272     catch(XSECException& e) {
273         auto_ptr_char temp(e.getMsg());
274         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
275     }
276     catch(XSECCryptoException& e) {
277         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
278     }
279 }