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