97583a7c316818ee94f1a61b9e6a4219ca7e240a
[shibboleth/cpp-xmltooling.git] / xmltooling / encryption / impl / Encrypter.cpp
1 /*
2  *  Copyright 2001-2010 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 "encryption/Encryption.h"
26 #include "security/Credential.h"
27 #include "signature/KeyInfo.h"
28
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/XENCCipher.hpp>
34 #include <xsec/xenc/XENCEncryptedData.hpp>
35 #include <xsec/xenc/XENCEncryptedKey.hpp>
36
37 using namespace xmlencryption;
38 using namespace xmlsignature;
39 using namespace xmltooling;
40 using namespace xercesc;
41 using namespace std;
42
43 Encrypter::EncryptionParams::EncryptionParams(
44     const XMLCh* algorithm, const unsigned char* keyBuffer, unsigned int keyBufferSize, const Credential* credential, bool compact
45     ) : m_algorithm(algorithm), m_keyBuffer(keyBuffer), m_keyBufferSize(keyBufferSize), m_credential(credential), m_compact(compact)
46 {
47 }
48
49 Encrypter::EncryptionParams::~EncryptionParams()
50 {
51 }
52
53 Encrypter::KeyEncryptionParams::KeyEncryptionParams(const Credential& credential, const XMLCh* algorithm, const XMLCh* recipient)
54     : m_credential(credential), m_algorithm(algorithm), m_recipient(recipient)
55 {
56 }
57
58 Encrypter::KeyEncryptionParams::~KeyEncryptionParams()
59 {
60 }
61
62 Encrypter::Encrypter() : m_cipher(nullptr)
63 {
64 }
65
66 Encrypter::~Encrypter()
67 {
68     XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
69     memset(m_keyBuffer,0,32);
70 }
71
72 void Encrypter::checkParams(EncryptionParams& encParams, KeyEncryptionParams* kencParams)
73 {
74     if (encParams.m_keyBufferSize==0) {
75         if (encParams.m_credential) {
76             if (kencParams)
77                 throw EncryptionException("Generating EncryptedKey inline requires the encryption key in raw form.");
78         }
79         else {
80             if (!kencParams)
81                 throw EncryptionException("Using a generated encryption key requires a KeyEncryptionParams object.");
82
83             // We're generating a random key. The maximum supported length is AES-256, so we need 32 bytes.
84             if (XSECPlatformUtils::g_cryptoProvider->getRandom(m_keyBuffer,32)<32)
85                 throw EncryptionException("Unable to generate random data; was PRNG seeded?");
86             encParams.m_keyBuffer=m_keyBuffer;
87             encParams.m_keyBufferSize=32;
88         }
89     }
90     
91     XSECCryptoKey* key=nullptr;
92     if (encParams.m_credential) {
93         key = encParams.m_credential->getPrivateKey();
94         if (!key)
95             throw EncryptionException("Credential in EncryptionParams structure did not supply a private/secret key.");
96         // Set the encryption key.
97         m_cipher->setKey(key->clone());
98     }
99     else {
100         // We have to have a raw key now, so we need to build a wrapper around it.
101         XSECAlgorithmHandler* handler =XSECPlatformUtils::g_algorithmMapper->mapURIToHandler(encParams.m_algorithm);
102         if (handler != nullptr)
103             key = handler->createKeyForURI(
104                 encParams.m_algorithm,const_cast<unsigned char*>(encParams.m_keyBuffer),encParams.m_keyBufferSize
105                 );
106
107         if (!key)
108             throw EncryptionException("Unable to build wrapper for key, unknown algorithm?");
109         // Overwrite the length if known.
110         switch (static_cast<XSECCryptoSymmetricKey*>(key)->getSymmetricKeyType()) {
111             case XSECCryptoSymmetricKey::KEY_3DES_192:
112                 encParams.m_keyBufferSize = 192/8;
113                 break;
114             case XSECCryptoSymmetricKey::KEY_AES_128:
115                 encParams.m_keyBufferSize = 128/8;
116                 break;
117             case XSECCryptoSymmetricKey::KEY_AES_192:
118                 encParams.m_keyBufferSize = 192/8;
119                 break;
120             case XSECCryptoSymmetricKey::KEY_AES_256:
121                 encParams.m_keyBufferSize = 256/8;
122                 break;
123         }
124         // Set the encryption key.
125         m_cipher->setKey(key);
126     }
127 }
128
129 EncryptedData* Encrypter::encryptElement(DOMElement* element, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
130 {
131     // We can reuse the cipher object if the document hasn't changed.
132     
133     if (m_cipher && m_cipher->getDocument()!=element->getOwnerDocument()) {
134         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
135         m_cipher=nullptr;
136     }
137     
138     if (!m_cipher) {
139         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(element->getOwnerDocument());
140         m_cipher->setExclusiveC14nSerialisation(false);
141     }
142     
143     try {
144         checkParams(encParams,kencParams);
145         m_cipher->encryptElementDetached(element, ENCRYPT_NONE, encParams.m_algorithm);
146         return decorateAndUnmarshall(encParams, kencParams);
147     }
148     catch(XSECException& e) {
149         auto_ptr_char temp(e.getMsg());
150         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
151     }
152     catch(XSECCryptoException& e) {
153         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
154     }
155 }
156
157 EncryptedData* Encrypter::encryptElementContent(DOMElement* element, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
158 {
159     // We can reuse the cipher object if the document hasn't changed.
160
161     if (m_cipher && m_cipher->getDocument()!=element->getOwnerDocument()) {
162         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
163         m_cipher=nullptr;
164     }
165     
166     if (!m_cipher) {
167         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(element->getOwnerDocument());
168         m_cipher->setExclusiveC14nSerialisation(false);
169     }
170     
171     try {
172         checkParams(encParams,kencParams);
173         m_cipher->encryptElementContentDetached(element, ENCRYPT_NONE, encParams.m_algorithm);
174         return decorateAndUnmarshall(encParams, kencParams);
175     }
176     catch(XSECException& e) {
177         auto_ptr_char temp(e.getMsg());
178         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
179     }
180     catch(XSECCryptoException& e) {
181         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
182     }
183 }
184
185 EncryptedData* Encrypter::encryptStream(istream& input, EncryptionParams& encParams, KeyEncryptionParams* kencParams)
186 {
187     // Get a fresh cipher object and document.
188
189     if (m_cipher) {
190         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
191         m_cipher=nullptr;
192     }
193     
194     DOMDocument* doc=nullptr;
195     try {
196         doc=XMLToolingConfig::getConfig().getParser().newDocument();
197         XercesJanitor<DOMDocument> janitor(doc);
198         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(doc);
199         m_cipher->setExclusiveC14nSerialisation(false);
200         
201         checkParams(encParams,kencParams);
202         StreamInputSource::StreamBinInputStream xstream(input);
203         m_cipher->encryptBinInputStream(&xstream, ENCRYPT_NONE, encParams.m_algorithm);
204         return decorateAndUnmarshall(encParams, kencParams);
205     }
206     catch(XSECException& e) {
207         auto_ptr_char temp(e.getMsg());
208         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
209     }
210     catch(XSECCryptoException& e) {
211         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
212     }
213 }
214
215 EncryptedData* Encrypter::decorateAndUnmarshall(EncryptionParams& encParams, KeyEncryptionParams* kencParams)
216 {
217     XENCEncryptedData* encData=m_cipher->getEncryptedData();
218     if (!encData)
219         throw EncryptionException("No EncryptedData element found?");
220
221     // Unmarshall a tooling version of EncryptedData around the DOM.
222     EncryptedData* xmlEncData=nullptr;
223     auto_ptr<XMLObject> xmlObject(XMLObjectBuilder::buildOneFromElement(encData->getElement()));
224     if (!(xmlObject.get()) || !(xmlEncData=dynamic_cast<EncryptedData*>(xmlObject.get())))
225         throw EncryptionException("Unable to unmarshall into EncryptedData object.");
226     
227     // Unbind from DOM so we can divorce this from the original document.
228     xmlEncData->releaseThisAndChildrenDOM();
229     
230     // KeyInfo?
231     KeyInfo* kinfo = encParams.m_credential ? encParams.m_credential->getKeyInfo(encParams.m_compact) : nullptr;
232     if (kinfo)
233         xmlEncData->setKeyInfo(kinfo);
234     
235     // Are we doing a key encryption?
236     if (kencParams) {
237         XSECCryptoKey* kek = kencParams->m_credential.getPublicKey();
238         if (!kek)
239             throw EncryptionException("Credential in KeyEncryptionParams structure did not supply a public key.");
240         if (!kencParams->m_algorithm)
241             kencParams->m_algorithm = getKeyTransportAlgorithm(kencParams->m_credential, encParams.m_algorithm);
242         if (!kencParams->m_algorithm)
243             throw EncryptionException("Unable to derive a supported key encryption algorithm.");
244
245         m_cipher->setKEK(kek->clone());
246         // ownership of this belongs to us, for some reason...
247         auto_ptr<XENCEncryptedKey> encKey(
248             m_cipher->encryptKey(encParams.m_keyBuffer, encParams.m_keyBufferSize, ENCRYPT_NONE, kencParams->m_algorithm)
249             );
250         EncryptedKey* xmlEncKey=nullptr;
251         auto_ptr<XMLObject> xmlObjectKey(XMLObjectBuilder::buildOneFromElement(encKey->getElement()));
252         if (!(xmlObjectKey.get()) || !(xmlEncKey=dynamic_cast<EncryptedKey*>(xmlObjectKey.get())))
253             throw EncryptionException("Unable to unmarshall into EncryptedKey object.");
254         
255         xmlEncKey->releaseThisAndChildrenDOM();
256         
257         // Recipient?
258         if (kencParams->m_recipient)
259             xmlEncKey->setRecipient(kencParams->m_recipient);
260         
261         // KeyInfo?
262         kinfo = kencParams->m_credential.getKeyInfo(encParams.m_compact);
263         if (kinfo)
264             xmlEncKey->setKeyInfo(kinfo);
265         
266         // Add the EncryptedKey inline.
267         if (!xmlEncData->getKeyInfo())
268             xmlEncData->setKeyInfo(KeyInfoBuilder::buildKeyInfo());
269         xmlEncData->getKeyInfo()->getUnknownXMLObjects().push_back(xmlEncKey);
270         xmlObjectKey.release();
271     }
272     
273     xmlObject.release();
274     return xmlEncData;
275 }
276
277 EncryptedKey* Encrypter::encryptKey(
278     const unsigned char* keyBuffer, unsigned int keyBufferSize, KeyEncryptionParams& kencParams, bool compact
279     )
280 {
281     if (!kencParams.m_algorithm)
282         throw EncryptionException("KeyEncryptionParams structure did not include a key encryption algorithm.");
283
284     // Get a fresh cipher object and document.
285
286     if (m_cipher) {
287         XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->releaseCipher(m_cipher);
288         m_cipher=nullptr;
289     }
290
291     XSECCryptoKey* kek = kencParams.m_credential.getPublicKey();
292     if (!kek)
293         throw EncryptionException("Credential in KeyEncryptionParams structure did not supply a public key.");
294
295     DOMDocument* doc=nullptr;
296     try {
297         doc=XMLToolingConfig::getConfig().getParser().newDocument();
298         XercesJanitor<DOMDocument> janitor(doc);
299         m_cipher=XMLToolingInternalConfig::getInternalConfig().m_xsecProvider->newCipher(doc);
300         m_cipher->setExclusiveC14nSerialisation(false);
301         m_cipher->setKEK(kek->clone());
302         auto_ptr<XENCEncryptedKey> encKey(m_cipher->encryptKey(keyBuffer, keyBufferSize, ENCRYPT_NONE, kencParams.m_algorithm));
303         
304         EncryptedKey* xmlEncKey=nullptr;
305         auto_ptr<XMLObject> xmlObjectKey(XMLObjectBuilder::buildOneFromElement(encKey->getElement()));
306         if (!(xmlObjectKey.get()) || !(xmlEncKey=dynamic_cast<EncryptedKey*>(xmlObjectKey.get())))
307             throw EncryptionException("Unable to unmarshall into EncryptedKey object.");
308         
309         xmlEncKey->releaseThisAndChildrenDOM();
310         
311         // Recipient?
312         if (kencParams.m_recipient)
313             xmlEncKey->setRecipient(kencParams.m_recipient);
314
315         // KeyInfo?
316         KeyInfo* kinfo = kencParams.m_credential.getKeyInfo(compact);
317         if (kinfo)
318             xmlEncKey->setKeyInfo(kinfo);
319
320         xmlObjectKey.release();
321         return xmlEncKey;
322     }
323     catch(XSECException& e) {
324         auto_ptr_char temp(e.getMsg());
325         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + temp.get());
326     }
327     catch(XSECCryptoException& e) {
328         throw EncryptionException(string("XMLSecurity exception while encrypting: ") + e.getMsg());
329     }
330 }
331
332 const XMLCh* Encrypter::getKeyTransportAlgorithm(const Credential& credential, const XMLCh* encryptionAlg)
333 {
334     XMLToolingConfig& conf = XMLToolingConfig::getConfig();
335     const char* alg = credential.getAlgorithm();
336     if (!alg || !strcmp(alg, "RSA")) {
337         if (XMLString::equals(encryptionAlg,DSIGConstants::s_unicodeStrURI3DES_CBC)) {
338             if (conf.isXMLAlgorithmSupported(DSIGConstants::s_unicodeStrURIRSA_1_5, XMLToolingConfig::ALGTYPE_KEYENCRYPT))
339                 return DSIGConstants::s_unicodeStrURIRSA_1_5;
340             else if (conf.isXMLAlgorithmSupported(DSIGConstants::s_unicodeStrURIRSA_OAEP_MGFP1, XMLToolingConfig::ALGTYPE_KEYENCRYPT))
341                 return DSIGConstants::s_unicodeStrURIRSA_OAEP_MGFP1;
342         }
343         else {
344             if (conf.isXMLAlgorithmSupported(DSIGConstants::s_unicodeStrURIRSA_OAEP_MGFP1, XMLToolingConfig::ALGTYPE_KEYENCRYPT))
345                 return DSIGConstants::s_unicodeStrURIRSA_OAEP_MGFP1;
346             else if (conf.isXMLAlgorithmSupported(DSIGConstants::s_unicodeStrURIRSA_1_5, XMLToolingConfig::ALGTYPE_KEYENCRYPT))
347                 return DSIGConstants::s_unicodeStrURIRSA_1_5;
348         }
349     }
350     else if (!strcmp(alg, "AES")) {
351         const XMLCh* ret = nullptr;
352         switch (credential.getKeySize()) {
353             case 128:
354                 ret = DSIGConstants::s_unicodeStrURIKW_AES128;
355             case 192:
356                 ret = DSIGConstants::s_unicodeStrURIKW_AES192;
357             case 256:
358                 ret = DSIGConstants::s_unicodeStrURIKW_AES256;
359             default:
360                 return nullptr;
361         }
362         if (conf.isXMLAlgorithmSupported(ret, XMLToolingConfig::ALGTYPE_KEYENCRYPT))
363             return ret;
364     }
365     else if (!strcmp(alg, "DESede")) {
366         if (conf.isXMLAlgorithmSupported(DSIGConstants::s_unicodeStrURIKW_3DES, XMLToolingConfig::ALGTYPE_KEYENCRYPT))
367             return DSIGConstants::s_unicodeStrURIKW_3DES;
368     }
369
370     return nullptr;
371 }