Factor out XML signing code, add SAML 2 Artifact/POST
authorScott Cantor <cantor.2@osu.edu>
Wed, 27 Sep 2006 03:22:43 +0000 (03:22 +0000)
committerScott Cantor <cantor.2@osu.edu>
Wed, 27 Sep 2006 03:22:43 +0000 (03:22 +0000)
saml/Makefile.am
saml/binding/MessageEncoder.h
saml/binding/impl/MessageEncoder.cpp
saml/saml.vcproj
saml/saml1/binding/impl/SAML1POSTEncoder.cpp
saml/saml2/binding/SAML2ArtifactEncoder.h [new file with mode: 0644]
saml/saml2/binding/SAML2POSTEncoder.h [new file with mode: 0644]
saml/saml2/binding/impl/SAML2ArtifactEncoder.cpp [new file with mode: 0644]
saml/saml2/binding/impl/SAML2POSTEncoder.cpp [new file with mode: 0644]

index 40d2744..1aa5ff4 100644 (file)
@@ -58,10 +58,10 @@ saml1coreinclude_HEADERS = \
        saml1/core/Protocols.h
 
 saml1bindinclude_HEADERS = \
-       saml1/binding/SAML1ArtifactEncoder.h \
-       saml1/binding/SAML1POSTEncoder.h \
        saml1/binding/SAMLArtifactType0001.h \
-       saml1/binding/SAMLArtifactType0002.h
+       saml1/binding/SAMLArtifactType0002.h \
+       saml1/binding/SAML1ArtifactEncoder.h \
+       saml1/binding/SAML1POSTEncoder.h
 
 saml2coreinclude_HEADERS = \
        saml2/core/Assertions.h \
@@ -69,7 +69,9 @@ saml2coreinclude_HEADERS = \
 
 saml2bindinclude_HEADERS = \
        saml2/binding/SAML2Artifact.h \
-       saml2/binding/SAML2ArtifactType0004.h
+       saml2/binding/SAML2ArtifactType0004.h \
+       saml1/binding/SAML2ArtifactEncoder.h \
+       saml1/binding/SAML2POSTEncoder.h
 
 saml2mdinclude_HEADERS = \
        saml2/metadata/AbstractMetadataProvider.h \
@@ -90,10 +92,10 @@ libsaml_la_SOURCES = \
        saml1/core/impl/AssertionsSchemaValidators.cpp \
        saml1/core/impl/ProtocolsImpl.cpp \
        saml1/core/impl/ProtocolsSchemaValidators.cpp \
-       saml1/binding/impl/SAML1ArtifactEncoder.cpp \
-       saml1/binding/impl/SAML1POSTEncoder.cpp \
        saml1/binding/impl/SAMLArtifactType0001.cpp \
        saml1/binding/impl/SAMLArtifactType0002.cpp \
+       saml1/binding/impl/SAML1ArtifactEncoder.cpp \
+       saml1/binding/impl/SAML1POSTEncoder.cpp \
        saml2/core/impl/Assertions20Impl.cpp \
        saml2/core/impl/Assertions20SchemaValidators.cpp \
        saml2/core/impl/Protocols20Impl.cpp \
@@ -110,6 +112,8 @@ libsaml_la_SOURCES = \
        saml2/metadata/impl/WhitelistMetadataFilter.cpp \
        saml2/binding/impl/SAML2Artifact.cpp \
        saml2/binding/impl/SAML2ArtifactType0004.cpp \
+       saml2/binding/impl/SAML2ArtifactEncoder.cpp \
+       saml2/binding/impl/SAML2POSTEncoder.cpp \
        encryption/EncryptedKeyResolver.cpp \
        security/impl/TrustEngine.cpp \
        security/impl/AbstractPKIXTrustEngine.cpp \
index ed976ce..81e39d2 100644 (file)
@@ -164,6 +164,19 @@ namespace opensaml {
     protected:\r
         MessageEncoder() : m_urlEncoder(NULL), m_artifactGenerator(NULL) {}\r
         \r
+        /**\r
+         * Helper function to build a new XML Signature with KeyInfo, based\r
+         * on the supplied CredentialResolver.\r
+         * \r
+         * @param credResolver      CredentialResolver instance to supply signing material\r
+         * @param sigAlgorithm      optional signature algorithm identifier\r
+         * @return  a new Signature object\r
+         */\r
+        xmlsignature::Signature* buildSignature(\r
+            const xmlsignature::CredentialResolver* credResolver,\r
+            const XMLCh* sigAlgorithm=NULL\r
+            ) const;\r
+        \r
         /** Pointer to a URLEncoder implementation. */\r
         const URLEncoder* m_urlEncoder;\r
         \r
@@ -181,6 +194,15 @@ namespace opensaml {
 \r
     /** MessageEncoder for SAML 1.x Browser/POST "binding" (really part of profile) */\r
     #define SAML1_POST_ENCODER  "urn:oasis:names:tc:SAML:1.0:profiles:browser-post"\r
+    \r
+    /** MessageEncoder for SAML 2.0 HTTP-Artifact binding */\r
+    #define SAML2_ARTIFACT_ENCODER "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"\r
+\r
+    /** MessageEncoder for SAML 2.0 HTTP-POST binding */\r
+    #define SAML2_POST_ENCODER "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"\r
+\r
+    /** MessageEncoder for SAML 2.0 HTTP-Redirect binding */\r
+    #define SAML2_REDIRECT_ENCODER "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"\r
 };\r
 \r
 #endif /* __saml_encoder_h__ */\r
index 7510254..825f5f7 100644 (file)
 #include "internal.h"
 #include "binding/MessageEncoder.h"
 
+#include <xmltooling/signature/KeyInfo.h>
+#include <xmltooling/signature/Signature.h>
+
 using namespace opensaml;
+using namespace xmlsignature;
 using namespace xmltooling;
 using namespace std;
 
@@ -32,6 +36,11 @@ namespace opensaml {
         SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML1ArtifactEncoderFactory;
         SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML1POSTEncoderFactory;
     }; 
+
+    namespace saml2p {
+        SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML2ArtifactEncoderFactory;
+        SAML_DLLLOCAL PluginManager<MessageEncoder,const DOMElement*>::Factory SAML2POSTEncoderFactory;
+    };
 };
 
 void SAML_API opensaml::registerMessageEncoders()
@@ -39,4 +48,39 @@ void SAML_API opensaml::registerMessageEncoders()
     SAMLConfig& conf=SAMLConfig::getConfig();
     conf.MessageEncoderManager.registerFactory(SAML1_ARTIFACT_ENCODER, saml1p::SAML1ArtifactEncoderFactory);
     conf.MessageEncoderManager.registerFactory(SAML1_POST_ENCODER, saml1p::SAML1POSTEncoderFactory);
+    conf.MessageEncoderManager.registerFactory(SAML2_ARTIFACT_ENCODER, saml2p::SAML2ArtifactEncoderFactory);
+    conf.MessageEncoderManager.registerFactory(SAML2_POST_ENCODER, saml2p::SAML2POSTEncoderFactory);
+}
+
+namespace {
+    class SAML_DLLLOCAL _addcert : public binary_function<X509Data*,XSECCryptoX509*,void> {
+    public:
+        void operator()(X509Data* bag, XSECCryptoX509* cert) const {
+            safeBuffer& buf=cert->getDEREncodingSB();
+            X509Certificate* x=X509CertificateBuilder::buildX509Certificate();
+            x->setValue(buf.sbStrToXMLCh());
+            bag->getX509Certificates().push_back(x);
+        }
+    };
+};
+
+Signature* MessageEncoder::buildSignature(const CredentialResolver* credResolver, const XMLCh* sigAlgorithm) const
+{
+    // Build a Signature.
+    Signature* sig = SignatureBuilder::buildSignature();
+    if (sigAlgorithm)
+        sig->setSignatureAlgorithm(sigAlgorithm);
+    sig->setSigningKey(credResolver->getKey());
+
+    // Build KeyInfo.
+    const vector<XSECCryptoX509*>& certs = credResolver->getCertificates();
+    if (!certs.empty()) {
+        KeyInfo* keyInfo=KeyInfoBuilder::buildKeyInfo();
+        X509Data* x509Data=X509DataBuilder::buildX509Data();
+        keyInfo->getX509Datas().push_back(x509Data);
+        for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data));
+        sig->setKeyInfo(keyInfo);
+    }
+    
+    return sig;
 }
index 1c47f4e..625c424 100644 (file)
                                                        </FileConfiguration>\r
                                                </File>\r
                                                <File\r
+                                                       RelativePath=".\saml2\binding\impl\SAML2ArtifactEncoder.cpp"\r
+                                                       >\r
+                                               </File>\r
+                                               <File\r
                                                        RelativePath=".\saml2\binding\impl\SAML2ArtifactType0004.cpp"\r
                                                        >\r
                                                        <FileConfiguration\r
                                                                />\r
                                                        </FileConfiguration>\r
                                                </File>\r
+                                               <File\r
+                                                       RelativePath=".\saml2\binding\impl\SAML2POSTEncoder.cpp"\r
+                                                       >\r
+                                               </File>\r
                                        </Filter>\r
                                </Filter>\r
                        </Filter>\r
                                                >\r
                                        </File>\r
                                        <File\r
+                                               RelativePath=".\saml2\binding\SAML2ArtifactEncoder.h"\r
+                                               >\r
+                                       </File>\r
+                                       <File\r
                                                RelativePath=".\saml2\binding\SAML2ArtifactType0004.h"\r
                                                >\r
                                        </File>\r
+                                       <File\r
+                                               RelativePath=".\saml2\binding\SAML2POSTEncoder.h"\r
+                                               >\r
+                                       </File>\r
                                </Filter>\r
                        </Filter>\r
                        <Filter\r
index f62bd62..5b0704f 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * SAML1POSTEncoder.cpp
  * 
- * SAML 1.x Artifact binding/profile message encoder
+ * SAML 1.x POST binding/profile message encoder
  */
 
 #include "internal.h"
@@ -42,16 +42,6 @@ namespace opensaml {
         {
             return new SAML1POSTEncoder(e);
         }
-
-        class SAML_DLLLOCAL _addcert : public binary_function<X509Data*,XSECCryptoX509*,void> {
-        public:
-            void operator()(X509Data* bag, XSECCryptoX509* cert) const {
-                safeBuffer& buf=cert->getDEREncodingSB();
-                X509Certificate* x=X509CertificateBuilder::buildX509Certificate();
-                x->setValue(buf.sbStrToXMLCh());
-                bag->getX509Certificates().push_back(x);
-            }
-        };
     };
 };
 
@@ -92,22 +82,12 @@ void SAML1POSTEncoder::encode(
         else {
             log.debug("signing and marshalling the response");
 
-            // Append a Signature.
-            response->setSignature(SignatureBuilder::buildSignature());
-            response->getSignature()->setSigningKey(credResolver->getKey());
-    
-            // Build KeyInfo.
-            const vector<XSECCryptoX509*>& certs = credResolver->getCertificates();
-            if (!certs.empty()) {
-                KeyInfo* keyInfo=KeyInfoBuilder::buildKeyInfo();
-                X509Data* x509Data=X509DataBuilder::buildX509Data();
-                keyInfo->getX509Datas().push_back(x509Data);
-                for_each(certs.begin(),certs.end(),bind1st(_addcert(),x509Data));
-                response->getSignature()->setKeyInfo(keyInfo);
-            }
+            // Build a Signature.
+            Signature* sig = buildSignature(credResolver, sigAlgorithm);
+            response->setSignature(sig);
     
             // Sign response while marshalling.
-            vector<Signature*> sigs(1,response->getSignature());
+            vector<Signature*> sigs(1,sig);
             rootElement = response->marshall((DOMDocument*)NULL,&sigs);
         }
     }
diff --git a/saml/saml2/binding/SAML2ArtifactEncoder.h b/saml/saml2/binding/SAML2ArtifactEncoder.h
new file mode 100644 (file)
index 0000000..ecbdae2
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file saml/saml2/binding/SAML2ArtifactEncoder.h
+ * 
+ * SAML 2.0 HTTP-Artifact binding message encoder
+ */
+
+#include <saml/binding/MessageEncoder.h>
+
+
+namespace opensaml {
+    namespace saml2p {
+
+        /**
+         * SAML 2.0 HTTP-Artifact binding message encoder
+         */
+        class SAML_API SAML2ArtifactEncoder : public MessageEncoder
+        {
+        public:
+            SAML2ArtifactEncoder(const DOMElement* e);
+            virtual ~SAML2ArtifactEncoder();
+            
+            void encode(
+                std::map<std::string,std::string>& outputFields,
+                xmltooling::XMLObject* xmlObject,
+                const char* recipientID=NULL,
+                const char* relayState=NULL,
+                const xmlsignature::CredentialResolver* credResolver=NULL,
+                const XMLCh* sigAlgorithm=NULL
+                ) const;
+        };                
+
+    };
+};
diff --git a/saml/saml2/binding/SAML2POSTEncoder.h b/saml/saml2/binding/SAML2POSTEncoder.h
new file mode 100644 (file)
index 0000000..6074890
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * @file saml/saml2/binding/SAML2POSTEncoder.h
+ * 
+ * SAML 2.0 HTTP-POST binding message encoder
+ */
+
+#include <saml/binding/MessageEncoder.h>
+
+
+namespace opensaml {
+    namespace saml2p {
+
+        /**
+         * SAML 2.0 HTTP-POST binding message encoder
+         */
+        class SAML_API SAML2POSTEncoder : public MessageEncoder
+        {
+        public:
+            SAML2POSTEncoder(const DOMElement* e);
+            virtual ~SAML2POSTEncoder();
+            
+            void encode(
+                std::map<std::string,std::string>& outputFields,
+                xmltooling::XMLObject* xmlObject,
+                const char* recipientID=NULL,
+                const char* relayState=NULL,
+                const xmlsignature::CredentialResolver* credResolver=NULL,
+                const XMLCh* sigAlgorithm=NULL
+                ) const;
+        };                
+
+    };
+};
diff --git a/saml/saml2/binding/impl/SAML2ArtifactEncoder.cpp b/saml/saml2/binding/impl/SAML2ArtifactEncoder.cpp
new file mode 100644 (file)
index 0000000..386b07c
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * SAML2ArtifactEncoder.cpp
+ * 
+ * SAML 2.0 HTTP-Artifact binding message encoder
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "saml2/binding/SAML2Artifact.h"
+#include "saml2/binding/SAML2ArtifactEncoder.h"
+#include "saml2/core/Protocols.h"
+
+#include <log4cpp/Category.hh>
+#include <xmltooling/util/NDC.h>
+
+using namespace opensaml::saml2p;
+using namespace opensaml;
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace opensaml {
+    namespace saml2p {              
+        MessageEncoder* SAML_DLLLOCAL SAML2ArtifactEncoderFactory(const DOMElement* const & e)
+        {
+            return new SAML2ArtifactEncoder(e);
+        }
+    };
+};
+
+SAML2ArtifactEncoder::SAML2ArtifactEncoder(const DOMElement* e) {}
+
+SAML2ArtifactEncoder::~SAML2ArtifactEncoder() {}
+
+void SAML2ArtifactEncoder::encode(
+    map<string,string>& outputFields,
+    XMLObject* xmlObject,
+    const char* recipientID,
+    const char* relayState,
+    const CredentialResolver* credResolver,
+    const XMLCh* sigAlgorithm
+    ) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("encode");
+#endif
+    Category& log = Category::getInstance(SAML_LOGCAT".MessageEncoder.SAML2Artifact");
+    log.debug("validating input");
+    
+    outputFields.clear();
+    if (xmlObject->getParent())
+        throw BindingException("Cannot encode XML content with parent.");
+
+    StatusResponseType* response = NULL;
+    RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(xmlObject);
+    if (!request)
+        response = dynamic_cast<StatusResponseType*>(xmlObject);
+    if (!response)
+        throw BindingException("XML content for SAML 2.0 HTTP-Artifact Encoder must be a SAML 2.0 protocol message.");
+    
+    ArtifactMap* mapper = SAMLConfig::getConfig().getArtifactMap();
+    if (!mapper)
+        throw BindingException("SAML 2.0 HTTP-Artifact Encoder requires ArtifactMap be set in configuration.");
+
+    // Obtain a fresh artifact.
+    if (!m_artifactGenerator)
+        throw BindingException("SAML 2.0 HTTP-Artifact Encoder requires an ArtifactGenerator instance.");
+    log.debug("obtaining new artifact for relying party (%s)", recipientID ? recipientID : "unknown");
+    auto_ptr<SAMLArtifact> artifact(m_artifactGenerator->generateSAML2Artifact(recipientID));
+
+    if (credResolver) {
+        // Signature based on native XML signing.
+        if (request ? request->getSignature() : response->getSignature()) {
+            log.debug("message already signed, skipping signature operation");
+        }
+        else {
+            log.debug("signing the message");
+
+            // Build a Signature.
+            Signature* sig = buildSignature(credResolver, sigAlgorithm);
+            
+            // Append Signature.
+            request ? request->setSignature(sig) : response->setSignature(sig);    
+        
+            // Sign response while marshalling.
+            vector<Signature*> sigs(1,sig);
+            xmlObject->marshall((DOMDocument*)NULL,&sigs);
+        }
+    }
+    
+    // Pass back output fields.
+    outputFields["SAMLart"] = artifact->encode();
+    if (relayState)
+        outputFields["RelayState"] = relayState;
+
+    // Store the message. Last step in storage will be to delete the XML.
+    log.debug("storing artifact and content in map");
+    mapper->storeContent(xmlObject, artifact.get(), recipientID);
+
+    log.debug("message encoded");
+}
diff --git a/saml/saml2/binding/impl/SAML2POSTEncoder.cpp b/saml/saml2/binding/impl/SAML2POSTEncoder.cpp
new file mode 100644 (file)
index 0000000..9dace84
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *  Copyright 2001-2006 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.
+ */
+
+/**
+ * SAML2POSTEncoder.cpp
+ * 
+ * SAML 2.0 HTTP-POST binding message encoder
+ */
+
+#include "internal.h"
+#include "exceptions.h"
+#include "saml2/binding/SAML2POSTEncoder.h"
+#include "saml2/core/Protocols.h"
+
+#include <log4cpp/Category.hh>
+#include <xercesc/util/Base64.hpp>
+#include <xmltooling/util/NDC.h>
+
+using namespace opensaml::saml2p;
+using namespace opensaml;
+using namespace xmlsignature;
+using namespace xmltooling;
+using namespace log4cpp;
+using namespace std;
+
+namespace opensaml {
+    namespace saml2p {              
+        MessageEncoder* SAML_DLLLOCAL SAML2POSTEncoderFactory(const DOMElement* const & e)
+        {
+            return new SAML2POSTEncoder(e);
+        }
+    };
+};
+
+SAML2POSTEncoder::SAML2POSTEncoder(const DOMElement* e) {}
+
+SAML2POSTEncoder::~SAML2POSTEncoder() {}
+
+void SAML2POSTEncoder::encode(
+    map<string,string>& outputFields,
+    XMLObject* xmlObject,
+    const char* recipientID,
+    const char* relayState,
+    const CredentialResolver* credResolver,
+    const XMLCh* sigAlgorithm
+    ) const
+{
+#ifdef _DEBUG
+    xmltooling::NDC ndc("encode");
+#endif
+    Category& log = Category::getInstance(SAML_LOGCAT".MessageEncoder.SAML2POST");
+    log.debug("validating input");
+    
+    outputFields.clear();
+    if (xmlObject->getParent())
+        throw BindingException("Cannot encode XML content with parent.");
+    
+    StatusResponseType* response = NULL;
+    RequestAbstractType* request = dynamic_cast<RequestAbstractType*>(xmlObject);
+    if (!request)
+        response = dynamic_cast<StatusResponseType*>(xmlObject);
+    if (!response)
+        throw BindingException("XML content for SAML 2.0 HTTP-POST Encoder must be a SAML 2.0 protocol message.");
+    
+    DOMElement* rootElement = NULL;
+    if (credResolver) {
+        // Signature based on native XML signing.
+        if (request ? request->getSignature() : response->getSignature()) {
+            log.debug("message already signed, skipping signature operation");
+        }
+        else {
+            log.debug("signing and marshalling the message");
+
+            // Build a Signature.
+            Signature* sig = buildSignature(credResolver, sigAlgorithm);
+            
+            // Append Signature.
+            request ? request->setSignature(sig) : response->setSignature(sig);    
+        
+            // Sign response while marshalling.
+            vector<Signature*> sigs(1,sig);
+            rootElement = xmlObject->marshall((DOMDocument*)NULL,&sigs);
+        }
+    }
+    else {
+        log.debug("marshalling the message");
+        rootElement = xmlObject->marshall();
+    }
+    
+    string xmlbuf;
+    XMLHelper::serialize(rootElement, xmlbuf);
+    unsigned int len=0;
+    XMLByte* out=Base64::encode(reinterpret_cast<const XMLByte*>(xmlbuf.data()),xmlbuf.size(),&len);
+    if (out) {
+        xmlbuf.erase();
+        xmlbuf.append(reinterpret_cast<char*>(out),len);
+        XMLString::release(&out);
+    }
+    else {
+        throw BindingException("Base64 encoding of XML failed.");
+    }
+    
+    // Pass back output fields.
+    outputFields[request ? "SAMLRequest" : "SAMLResponse"] = xmlbuf;
+    if (relayState)
+        outputFields["RelayState"] = relayState;
+
+    // Cleanup by destroying XML.
+    delete xmlObject;
+
+    log.debug("message encoded");
+}