From: cantor Date: Fri, 20 Apr 2007 03:21:34 +0000 (+0000) Subject: Added uni/multicast encrypting methods. X-Git-Tag: 2.4.1~387 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fopensaml2.git;a=commitdiff_plain;h=020d5c58b3f60dcd3ab006a80ed69ee8c1b8c427 Added uni/multicast encrypting methods. Moved decryption method to base class. git-svn-id: https://svn.middleware.georgetown.edu/cpp-opensaml2/trunk@227 fb386ef7-a10c-0410-8ebf-fd3f8e989ab0 --- diff --git a/saml/Makefile.am b/saml/Makefile.am index 3b25823..4453d9f 100644 --- a/saml/Makefile.am +++ b/saml/Makefile.am @@ -132,6 +132,7 @@ libsaml_la_SOURCES = \ saml1/binding/impl/SAML1MessageRule.cpp \ saml1/profile/AssertionValidator.cpp \ saml1/profile/BrowserSSOProfileValidator.cpp \ + saml2/core/impl/Assertions.cpp \ saml2/core/impl/Assertions20Impl.cpp \ saml2/core/impl/Assertions20SchemaValidators.cpp \ saml2/core/impl/Protocols20Impl.cpp \ diff --git a/saml/binding/MessageEncoder.h b/saml/binding/MessageEncoder.h index ad87a08..0bb70ee 100644 --- a/saml/binding/MessageEncoder.h +++ b/saml/binding/MessageEncoder.h @@ -46,6 +46,15 @@ namespace opensaml { virtual ~MessageEncoder() {} /** + * Indicates whether the encoding format requires that messages be as compact as possible. + * + * @return true iff the encoding has size constraints + */ + virtual bool isCompact() const { + return false; + } + + /** * Interface to caller-supplied artifact generation mechanism. * * Generating an artifact for storage and retrieval requires knowledge of diff --git a/saml/saml.vcproj b/saml/saml.vcproj index 17cfdeb..9c0f39e 100644 --- a/saml/saml.vcproj +++ b/saml/saml.vcproj @@ -311,6 +311,10 @@ Name="impl" > + + diff --git a/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp b/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp index c450177..6360fca 100644 --- a/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp +++ b/saml/saml2/binding/impl/SAML2RedirectEncoder.cpp @@ -48,6 +48,10 @@ namespace opensaml { public: SAML2RedirectEncoder(const DOMElement* e) {} virtual ~SAML2RedirectEncoder() {} + + bool isCompact() const { + return true; + } long encode( GenericResponse& genericResponse, diff --git a/saml/saml2/core/Assertions.h b/saml/saml2/core/Assertions.h index 0d6b59d..a5a7fb7 100644 --- a/saml/saml2/core/Assertions.h +++ b/saml/saml2/core/Assertions.h @@ -38,6 +38,11 @@ namespace opensaml { + namespace saml2md { + class SAML_API MetadataProvider; + class SAML_API MetadataCredentialCriteria; + }; + /** * @namespace opensaml::saml2 * SAML 2.0 assertion namespace @@ -47,6 +52,16 @@ namespace opensaml { // Forward references class SAML_API Assertion; class SAML_API EncryptedAssertion; + + /** + * Marker interface for SAML types that can be encrypted. + */ + class SAML_API EncryptableObject : public virtual xmltooling::XMLObject + { + protected: + EncryptableObject() {} + virtual ~EncryptableObject() {} + }; DECL_XMLOBJECT_SIMPLE(SAML_API,AssertionIDRef,AssertionID,SAML 2.0 AssertionIDRef element); DECL_XMLOBJECT_SIMPLE(SAML_API,AssertionURIRef,AssertionURI,SAML 2.0 AssertionURIRef element); @@ -62,6 +77,40 @@ namespace opensaml { static const XMLCh TYPE_NAME[]; /** + * Encrypts an object to a single recipient using this object as a container. + * + * @param xmlObject object to encrypt + * @param metadataProvider a locked MetadataProvider to supply encryption keys + * @param criteria metadata-based CredentialCriteria to use + * @param compact true iff compact KeyInfo should be used + * @param algorithm optionally specifies data encryption algorithm if none can be determined from metadata + * @return the encrypted object + */ + virtual void encrypt( + const EncryptableObject& xmlObject, + const saml2md::MetadataProvider& metadataProvider, + saml2md::MetadataCredentialCriteria& criteria, + bool compact=false, + const XMLCh* algorithm=NULL + ); + + /** + * Encrypts an object to multiple recipients using this object as a container. + * + * @param recipients pairs containing a locked MetadataProvider to supply encryption keys, + * and a metadata-based CredentialCriteria to use + * @param compact true iff compact KeyInfo should be used + * @param algorithm optionally specifies data encryption algorithm if none can be determined from metadata + * @return the encrypted object + */ + virtual void encrypt( + const EncryptableObject& xmlObject, + const std::vector< std::pair >& recipients, + bool compact=false, + const XMLCh* algorithm=NULL + ); + + /** * Decrypts the element using the supplied CredentialResolver. * *

The object returned will be unmarshalled around the decrypted DOM element in a @@ -74,13 +123,13 @@ namespace opensaml { */ virtual xmltooling::XMLObject* decrypt( const xmltooling::CredentialResolver& credResolver, const XMLCh* recipient, xmltooling::CredentialCriteria* criteria=NULL - ) const=0; + ) const; END_XMLOBJECT; BEGIN_XMLOBJECT(SAML_API,EncryptedID,EncryptedElementType,SAML 2.0 EncryptedID element); END_XMLOBJECT; - BEGIN_XMLOBJECT(SAML_API,BaseID,xmltooling::XMLObject,SAML 2.0 BaseID abstract element); + BEGIN_XMLOBJECT(SAML_API,BaseID,EncryptableObject,SAML 2.0 BaseID abstract element); DECL_STRING_ATTRIB(NameQualifier,NAMEQUALIFIER); DECL_STRING_ATTRIB(SPNameQualifier,SPNAMEQUALIFIER); END_XMLOBJECT; @@ -111,7 +160,7 @@ namespace opensaml { static const XMLCh TRANSIENT[]; END_XMLOBJECT; - BEGIN_XMLOBJECT(SAML_API,NameID,NameIDType,SAML 2.0 NameID element); + BEGIN_XMLOBJECT2(SAML_API,NameID,NameIDType,EncryptableObject,SAML 2.0 NameID element); END_XMLOBJECT; BEGIN_XMLOBJECT(SAML_API,Issuer,NameIDType,SAML 2.0 Issuer element); @@ -266,7 +315,7 @@ namespace opensaml { BEGIN_XMLOBJECT(SAML_API,AttributeValue,xmltooling::ElementProxy,SAML 2.0 AttributeValue element); END_XMLOBJECT; - BEGIN_XMLOBJECT(SAML_API,Attribute,xmltooling::AttributeExtensibleXMLObject,SAML 2.0 Attribute element); + BEGIN_XMLOBJECT2(SAML_API,Attribute,xmltooling::AttributeExtensibleXMLObject,EncryptableObject,SAML 2.0 Attribute element); DECL_STRING_ATTRIB(Name,NAME); DECL_STRING_ATTRIB(NameFormat,NAMEFORMAT); DECL_STRING_ATTRIB(FriendlyName,FRIENDLYNAME); @@ -320,7 +369,7 @@ namespace opensaml { virtual Issuer* getIssuer() const=0; }; - BEGIN_XMLOBJECT2(SAML_API,Assertion,saml2::RootObject,opensaml::Assertion,SAML 2.0 Assertion element); + BEGIN_XMLOBJECT3(SAML_API,Assertion,saml2::RootObject,opensaml::Assertion,EncryptableObject,SAML 2.0 Assertion element); DECL_INHERITED_STRING_ATTRIB(Version,VER); DECL_INHERITED_STRING_ATTRIB(ID,ID); DECL_INHERITED_DATETIME_ATTRIB(IssueInstant,ISSUEINSTANT); @@ -446,7 +495,7 @@ namespace opensaml { throw xmltooling::XMLObjectException("Unable to obtain typed builder for KeyInfoConfirmationDataType."); } }; - + /** * Registers builders and validators for SAML 2.0 Assertion classes into the runtime. */ diff --git a/saml/saml2/core/impl/Assertions.cpp b/saml/saml2/core/impl/Assertions.cpp new file mode 100644 index 0000000..585723a --- /dev/null +++ b/saml/saml2/core/impl/Assertions.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2001-2007 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Assertions.cpp + * + * Built-in behavior for SAML 2.0 Assertion interfaces. + */ + +#include "internal.h" +#include "exceptions.h" +#include "saml/encryption/EncryptedKeyResolver.h" +#include "saml2/core/Assertions.h" +#include "saml2/metadata/Metadata.h" +#include "saml2/metadata/MetadataProvider.h" +#include "saml2/metadata/MetadataCredentialContext.h" +#include "saml2/metadata/MetadataCredentialCriteria.h" + +#include +#include +#include + +using namespace opensaml::saml2md; +using namespace opensaml::saml2; +using namespace xmlencryption; +using namespace xmlsignature; +using namespace xmltooling; +using namespace std; + +void EncryptedElementType::encrypt( + const EncryptableObject& xmlObject, + const MetadataProvider& metadataProvider, + MetadataCredentialCriteria& criteria, + bool compact, + const XMLCh* algorithm + ) +{ + // With one recipient, we let the library generate the encryption key for us. + // Get the key encryption key to use. + criteria.setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL); + const Credential* KEK = metadataProvider.resolve(&criteria); + if (!KEK) + throw EncryptionException("No key encryption credential found."); + + // Try and find EncryptionMethod information surrounding the credential. + const MetadataCredentialContext* metaCtx = dynamic_cast(KEK->getCredentalContext()); + if (metaCtx) { + const vector encMethods = metaCtx->getKeyDescriptor().getEncryptionMethods(); + if (!encMethods.empty()) + algorithm = encMethods.front()->getAlgorithm(); + } + + if (!algorithm || !*algorithm) + algorithm = DSIGConstants::s_unicodeStrURIAES256_CBC; + + Encrypter encrypter; + Encrypter::EncryptionParams ep(algorithm, NULL, 0, NULL, compact); + Encrypter::KeyEncryptionParams kep(*KEK); + setEncryptedData(encrypter.encryptElement(xmlObject.getDOM(),ep,&kep)); +} + +void EncryptedElementType::encrypt( + const EncryptableObject& xmlObject, + const vector< pair >& recipients, + bool compact, + const XMLCh* algorithm + ) +{ + if (recipients.size()==1) + return encrypt(xmlObject, *recipients.front().first, *recipients.front().second, compact, algorithm); + + // With multiple recipients, we have to generate an encryption key and then multicast it, + // so we need to split the encryption and key wrapping steps. + if (!algorithm || !*algorithm) + algorithm = DSIGConstants::s_unicodeStrURIAES256_CBC; + + // Generate a random key. + unsigned char keyBuffer[32]; + if (XSECPlatformUtils::g_cryptoProvider->getRandom(keyBuffer,32)<32) + throw EncryptionException("Unable to generate encryption key; was PRNG seeded?"); + Encrypter encrypter; + Encrypter::EncryptionParams ep(algorithm, keyBuffer, 32, NULL, compact); + setEncryptedData(encrypter.encryptElement(xmlObject.getDOM(),ep)); + getEncryptedData()->setId(SAMLConfig::getConfig().generateIdentifier()); + + // Generate a uniquely named KeyInfo. + KeyInfo* keyInfo = KeyInfoBuilder::buildKeyInfo(); + getEncryptedData()->setKeyInfo(keyInfo); + KeyName* carriedName = KeyNameBuilder::buildKeyName(); + keyInfo->getKeyNames().push_back(carriedName); + carriedName->setName(SAMLConfig::getConfig().generateIdentifier()); + + VectorOf(EncryptedKey) keys = getEncryptedKeys(); + + // Now we encrypt the key for each recipient. + for (vector< pair >::const_iterator r = recipients.begin(); r!=recipients.end(); ++r) { + // Get key encryption key to use. + r->second->setUsage(CredentialCriteria::ENCRYPTION_CREDENTIAL); + const Credential* KEK = r->first->resolve(r->second); + if (!KEK) { + auto_ptr_char name(dynamic_cast(r->second->getRole().getParent())->getEntityID()); + log4cpp::Category::getInstance(SAML_LOGCAT".Encryption").warn("No key encryption credential found for (%s).", name.get()); + continue; + } + + // Encrypt the key and add it to the message. + Encrypter::KeyEncryptionParams kep( + *KEK, Encrypter::getKeyTransportAlgorithm(*KEK, algorithm), + dynamic_cast(r->second->getRole().getParent())->getEntityID() + ); + EncryptedKey* encryptedKey = encrypter.encryptKey(keyBuffer, ep.m_keyBufferSize, kep, compact); + keys.push_back(encryptedKey); + if (keys.size()>1) { + // Copy details from the other key. + encryptedKey->setCarriedKeyName(keys.front()->getCarriedKeyName()->cloneCarriedKeyName()); + encryptedKey->setReferenceList(keys.front()->getReferenceList()->cloneReferenceList()); + } + else { + // Attach the carried key name. + CarriedKeyName* carried = CarriedKeyNameBuilder::buildCarriedKeyName(); + carried->setName(carriedName->getName()); + encryptedKey->setCarriedKeyName(carried); + + // Attach a back-reference to the data. + ReferenceList* reflist = ReferenceListBuilder::buildReferenceList(); + encryptedKey->setReferenceList(reflist); + DataReference* dataref = DataReferenceBuilder::buildDataReference(); + reflist->getDataReferences().push_back(dataref); + XMLCh* uri = new XMLCh[XMLString::stringLen(getEncryptedData()->getId()) + 2]; + *uri = chPound; + *(uri+1) = chNull; + XMLString::catString(uri, getEncryptedData()->getId()); + dataref->setURI(uri); + delete[] uri; + } + } +} + +XMLObject* EncryptedElementType::decrypt(const CredentialResolver& credResolver, const XMLCh* recipient, CredentialCriteria* criteria) const +{ + if (!getEncryptedData()) + throw DecryptionException("No encrypted data present."); + EncryptedKeyResolver ekr(*this); + Decrypter decrypter(&credResolver, criteria, &ekr); + DOMDocumentFragment* frag = decrypter.decryptData(*getEncryptedData(), recipient); + if (frag->hasChildNodes() && frag->getFirstChild()==frag->getLastChild()) { + DOMNode* plaintext=frag->getFirstChild(); + if (plaintext->getNodeType()==DOMNode::ELEMENT_NODE) { + // Import the tree into a new Document that we can bind to the unmarshalled object. + XercesJanitor newdoc(XMLToolingConfig::getConfig().getParser().newDocument()); + DOMElement* treecopy = static_cast(newdoc->importNode(plaintext, true)); + newdoc->appendChild(treecopy); + auto_ptr ret(XMLObjectBuilder::buildOneFromElement(treecopy, true)); + newdoc.release(); + return ret.release(); + } + } + frag->release(); + throw DecryptionException("Decryption did not result in a single element."); +} diff --git a/saml/saml2/core/impl/Assertions20Impl.cpp b/saml/saml2/core/impl/Assertions20Impl.cpp index 3a714b7..4dd9a09 100644 --- a/saml/saml2/core/impl/Assertions20Impl.cpp +++ b/saml/saml2/core/impl/Assertions20Impl.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -52,7 +51,7 @@ using samlconstants::SAML20_NS; namespace opensaml { namespace saml2 { - + DECL_XMLOBJECTIMPL_SIMPLE(SAML_DLLLOCAL,AssertionIDRef); DECL_XMLOBJECTIMPL_SIMPLE(SAML_DLLLOCAL,AssertionURIRef); DECL_XMLOBJECTIMPL_SIMPLE(SAML_DLLLOCAL,Audience); @@ -191,29 +190,6 @@ namespace opensaml { } } } - - XMLObject* decrypt(const CredentialResolver& credResolver, const XMLCh* recipient, CredentialCriteria* criteria) const - { - if (!m_EncryptedData) - throw DecryptionException("No encrypted data present."); - EncryptedKeyResolver ekr(*this); - Decrypter decrypter(&credResolver, criteria, &ekr); - DOMDocumentFragment* frag = decrypter.decryptData(*m_EncryptedData, recipient); - if (frag->hasChildNodes() && frag->getFirstChild()==frag->getLastChild()) { - DOMNode* plaintext=frag->getFirstChild(); - if (plaintext->getNodeType()==DOMNode::ELEMENT_NODE) { - // Import the tree into a new Document that we can bind to the unmarshalled object. - XercesJanitor newdoc(XMLToolingConfig::getConfig().getParser().newDocument()); - DOMElement* treecopy = static_cast(newdoc->importNode(plaintext, true)); - newdoc->appendChild(treecopy); - auto_ptr ret(XMLObjectBuilder::buildOneFromElement(treecopy, true)); - newdoc.release(); - return ret.release(); - } - } - frag->release(); - throw DecryptionException("Decryption did not result in a single element."); - } IMPL_XMLOBJECT_CLONE(EncryptedElementType); IMPL_TYPED_FOREIGN_CHILD(EncryptedData,xmlencryption); @@ -227,7 +203,6 @@ namespace opensaml { } }; - //TODO unit test for this class SAML_DLLLOCAL EncryptedIDImpl : public virtual EncryptedID, public EncryptedElementTypeImpl { public: diff --git a/saml/saml2/core/impl/Protocols20Impl.cpp b/saml/saml2/core/impl/Protocols20Impl.cpp index e4b652e..75a7cdc 100644 --- a/saml/saml2/core/impl/Protocols20Impl.cpp +++ b/saml/saml2/core/impl/Protocols20Impl.cpp @@ -22,12 +22,10 @@ #include "internal.h" #include "exceptions.h" -#include "saml/encryption/EncryptedKeyResolver.h" #include "saml2/core/Protocols.h" #include #include -#include #include #include #include @@ -1288,29 +1286,6 @@ namespace opensaml { } } - XMLObject* decrypt(const CredentialResolver& credResolver, const XMLCh* recipient, CredentialCriteria* criteria) const - { - if (!m_EncryptedData) - throw DecryptionException("No encrypted data present."); - EncryptedKeyResolver ekr(*this); - Decrypter decrypter(&credResolver, criteria, &ekr); - DOMDocumentFragment* frag = decrypter.decryptData(*m_EncryptedData, recipient); - if (frag->hasChildNodes() && frag->getFirstChild()==frag->getLastChild()) { - DOMNode* plaintext=frag->getFirstChild(); - if (plaintext->getNodeType()==DOMNode::ELEMENT_NODE) { - // Import the tree into a new Document that we can bind to the unmarshalled object. - XercesJanitor newdoc(XMLToolingConfig::getConfig().getParser().newDocument()); - DOMElement* treecopy = static_cast(newdoc->importNode(plaintext, true)); - newdoc->appendChild(treecopy); - auto_ptr ret(XMLObjectBuilder::buildOneFromElement(treecopy, true)); - newdoc.release(); - return ret.release(); - } - } - frag->release(); - throw DecryptionException("Decryption did not result in a single element."); - } - IMPL_XMLOBJECT_CLONE(NewEncryptedID); EncryptedElementType* cloneEncryptedElementType() const { return new NewEncryptedIDImpl(*this);