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