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