2 * Copyright 2001-2010 Internet2
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * Built-in behavior for SAML 2.0 Assertion interfaces.
24 #include "exceptions.h"
25 #include "saml/encryption/EncryptedKeyResolver.h"
26 #include "saml2/core/Assertions.h"
27 #include "saml2/metadata/Metadata.h"
28 #include "saml2/metadata/MetadataProvider.h"
29 #include "saml2/metadata/MetadataCredentialContext.h"
30 #include "saml2/metadata/MetadataCredentialCriteria.h"
32 #include <xmltooling/logging.h>
33 #include <xmltooling/XMLToolingConfig.h>
34 #include <xmltooling/encryption/Encrypter.h>
35 #include <xmltooling/encryption/Decrypter.h>
36 #include <xmltooling/security/Credential.h>
37 #include <xmltooling/signature/KeyInfo.h>
38 #include <xmltooling/util/ParserPool.h>
40 #include <xsec/utils/XSECPlatformUtils.hpp>
42 using namespace opensaml::saml2md;
43 using namespace opensaml::saml2;
44 using namespace xmlencryption;
45 using namespace xmlsignature;
46 using namespace xmltooling;
49 void EncryptedElementType::encrypt(
50 const EncryptableObject& xmlObject,
51 const MetadataProvider& metadataProvider,
52 MetadataCredentialCriteria& criteria,
54 const XMLCh* algorithm
57 XMLToolingConfig& conf = XMLToolingConfig::getConfig();
59 // With one recipient, we let the library generate the encryption key for us.
60 // Get the key encryption key to use. To make use of EncryptionMethod, we have
61 // to examine each possible credential in conjunction with the algorithms we
63 criteria.setUsage(Credential::ENCRYPTION_CREDENTIAL);
64 vector<const Credential*> creds;
65 if (metadataProvider.resolve(creds, &criteria) == 0)
66 throw EncryptionException("No peer encryption credential found.");
70 const Credential* KEK = nullptr;
72 for (vector<const Credential*>::const_iterator c = creds.begin(); !KEK && c != creds.end(); ++c) {
73 // Try and find EncryptionMethod information surrounding the credential.
74 // All we're doing if they're present is setting algorithms where possible to
75 // the algorithms preferred by the credential, if we support them.
76 // The problem is that if we don't support them, the only case we can detect
77 // is if neither algorithm type is set *and* there's an EncryptionMethod present.
78 dataalg = keyalg = nullptr;
79 const MetadataCredentialContext* metaCtx = dynamic_cast<const MetadataCredentialContext*>((*c)->getCredentalContext());
81 const vector<EncryptionMethod*>& encMethods = metaCtx->getKeyDescriptor().getEncryptionMethods();
82 for (vector<EncryptionMethod*>::const_iterator meth = encMethods.begin(); meth != encMethods.end(); ++meth) {
83 if ((*meth)->getAlgorithm()) {
84 if (!dataalg && conf.isXMLAlgorithmSupported((*meth)->getAlgorithm(), XMLToolingConfig::ALGTYPE_ENCRYPT))
85 dataalg = (*meth)->getAlgorithm();
86 else if (!keyalg && conf.isXMLAlgorithmSupported((*meth)->getAlgorithm(), XMLToolingConfig::ALGTYPE_KEYENCRYPT))
87 keyalg = (*meth)->getAlgorithm();
91 if (!dataalg && !keyalg && !encMethods.empty()) {
92 // We know nothing, and something was specified that we don't support, so keep looking.
97 if (!keyalg && !(keyalg = Encrypter::getKeyTransportAlgorithm(*(*c), algorithm ? algorithm : dataalg))) {
98 // We can't derive a supported algorithm from the credential, so it will fail later anyway.
107 throw EncryptionException("No supported peer encryption credential found.");
109 // Passed in algorithm takes precedence.
110 if (algorithm && *algorithm)
113 #ifdef XSEC_OPENSSL_HAVE_AES
114 dataalg = DSIGConstants::s_unicodeStrURIAES256_CBC;
116 dataalg = DSIGConstants::s_unicodeStrURI3DES_CBC;
121 Encrypter::EncryptionParams ep(dataalg, nullptr, 0, nullptr, compact);
122 Encrypter::KeyEncryptionParams kep(*KEK, keyalg);
123 setEncryptedData(encrypter.encryptElement(xmlObject.marshall(), ep, &kep));
126 void EncryptedElementType::encrypt(
127 const EncryptableObject& xmlObject,
128 const vector< pair<const MetadataProvider*, MetadataCredentialCriteria*> >& recipients,
130 const XMLCh* algorithm
133 // With multiple recipients, we have to generate an encryption key and then multicast it,
134 // so we need to split the encryption and key wrapping steps.
135 if (!algorithm || !*algorithm) {
136 #ifdef XSEC_OPENSSL_HAVE_AES
137 algorithm = DSIGConstants::s_unicodeStrURIAES256_CBC;
139 algorithm = DSIGConstants::s_unicodeStrURI3DES_CBC;
143 // Generate a random key.
144 unsigned char keyBuffer[32];
145 if (XSECPlatformUtils::g_cryptoProvider->getRandom(keyBuffer,32)<32)
146 throw EncryptionException("Unable to generate encryption key; was PRNG seeded?");
148 Encrypter::EncryptionParams ep(algorithm, keyBuffer, 32, nullptr, compact);
149 setEncryptedData(encrypter.encryptElement(xmlObject.marshall(), ep));
150 getEncryptedData()->setId(SAMLConfig::getConfig().generateIdentifier());
152 // Generate a uniquely named KeyInfo.
153 KeyInfo* keyInfo = KeyInfoBuilder::buildKeyInfo();
154 getEncryptedData()->setKeyInfo(keyInfo);
155 KeyName* carriedName = KeyNameBuilder::buildKeyName();
156 keyInfo->getKeyNames().push_back(carriedName);
157 carriedName->setName(SAMLConfig::getConfig().generateIdentifier());
159 VectorOf(EncryptedKey) keys = getEncryptedKeys();
161 // Now we encrypt the key for each recipient.
162 for (vector< pair<const MetadataProvider*, MetadataCredentialCriteria*> >::const_iterator r = recipients.begin(); r!=recipients.end(); ++r) {
163 // Get key encryption keys to use.
164 r->second->setUsage(Credential::ENCRYPTION_CREDENTIAL);
165 vector<const Credential*> creds;
166 if (r->first->resolve(creds, r->second) == 0) {
167 auto_ptr_char name(dynamic_cast<const EntityDescriptor*>(r->second->getRole().getParent())->getEntityID());
168 logging::Category::getInstance(SAML_LOGCAT".Encryption").warn("No key encryption credentials found for (%s).", name.get());
173 const Credential* KEK = nullptr;
175 for (vector<const Credential*>::const_iterator c = creds.begin(); !KEK && c != creds.end(); ++c) {
176 // Try and find EncryptionMethod information surrounding the credential.
177 // All we're doing if they're present is setting algorithms where possible to
178 // the algorithms preferred by the credential, if we support them.
179 // The problem is that if we don't support them, the only case we can detect
180 // is if neither algorithm type is set *and* there's an EncryptionMethod present.
182 const MetadataCredentialContext* metaCtx = dynamic_cast<const MetadataCredentialContext*>((*c)->getCredentalContext());
184 const vector<EncryptionMethod*>& encMethods = metaCtx->getKeyDescriptor().getEncryptionMethods();
185 for (vector<EncryptionMethod*>::const_iterator meth = encMethods.begin(); meth != encMethods.end(); ++meth) {
186 if ((*meth)->getAlgorithm()) {
187 if (!keyalg && XMLToolingConfig::getConfig().isXMLAlgorithmSupported((*meth)->getAlgorithm(), XMLToolingConfig::ALGTYPE_KEYENCRYPT))
188 keyalg = (*meth)->getAlgorithm();
193 if (!keyalg && !(keyalg = Encrypter::getKeyTransportAlgorithm(*(*c), algorithm))) {
194 // We can't derive a supported algorithm from the credential, so it will fail later anyway.
203 auto_ptr_char name(dynamic_cast<const EntityDescriptor*>(r->second->getRole().getParent())->getEntityID());
204 logging::Category::getInstance(SAML_LOGCAT".Encryption").warn("no supported key encryption credential found for (%s).", name.get());
208 // Encrypt the key and add it to the message.
209 Encrypter::KeyEncryptionParams kep(
210 *KEK, keyalg, dynamic_cast<const EntityDescriptor*>(r->second->getRole().getParent())->getEntityID()
212 EncryptedKey* encryptedKey = encrypter.encryptKey(keyBuffer, ep.m_keyBufferSize, kep, compact);
213 keys.push_back(encryptedKey);
214 if (keys.size() > 1) {
215 // Copy details from the other key.
216 encryptedKey->setCarriedKeyName(keys.front()->getCarriedKeyName()->cloneCarriedKeyName());
217 encryptedKey->setReferenceList(keys.front()->getReferenceList()->cloneReferenceList());
220 // Attach the carried key name.
221 CarriedKeyName* carried = CarriedKeyNameBuilder::buildCarriedKeyName();
222 carried->setName(carriedName->getName());
223 encryptedKey->setCarriedKeyName(carried);
225 // Attach a back-reference to the data.
226 ReferenceList* reflist = ReferenceListBuilder::buildReferenceList();
227 encryptedKey->setReferenceList(reflist);
228 DataReference* dataref = DataReferenceBuilder::buildDataReference();
229 reflist->getDataReferences().push_back(dataref);
230 XMLCh* uri = new XMLCh[XMLString::stringLen(getEncryptedData()->getId()) + 2];
233 XMLString::catString(uri, getEncryptedData()->getId());
234 dataref->setURI(uri);
240 XMLObject* EncryptedElementType::decrypt(const CredentialResolver& credResolver, const XMLCh* recipient, CredentialCriteria* criteria) const
242 if (!getEncryptedData())
243 throw DecryptionException("No encrypted data present.");
244 EncryptedKeyResolver ekr(*this);
245 Decrypter decrypter(&credResolver, criteria, &ekr);
246 DOMDocumentFragment* frag = decrypter.decryptData(*getEncryptedData(), recipient);
247 if (frag->hasChildNodes() && frag->getFirstChild()==frag->getLastChild()) {
248 DOMNode* plaintext=frag->getFirstChild();
249 if (plaintext->getNodeType()==DOMNode::ELEMENT_NODE) {
250 // Import the tree into a new Document that we can bind to the unmarshalled object.
251 XercesJanitor<DOMDocument> newdoc(XMLToolingConfig::getConfig().getParser().newDocument());
252 DOMElement* treecopy;
254 treecopy = static_cast<DOMElement*>(newdoc->importNode(plaintext, true));
256 catch (XMLException& ex) {
258 auto_ptr_char temp(ex.getMessage());
259 throw DecryptionException(
260 string("Error importing decypted DOM into new document: ") + (temp.get() ? temp.get() : "no message")
264 newdoc->appendChild(treecopy);
265 auto_ptr<XMLObject> ret(XMLObjectBuilder::buildOneFromElement(treecopy, true));
267 return ret.release();
271 throw DecryptionException("Decryption did not result in a single element.");