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