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