15 November 2018

SAML service provider metadata

by mo


saml-kit is a neat gem for generating SAML documents in ruby.

To generate metadata specifically for a service provider you can write:

metadata = Saml::Kit::Metadata.build do |x|
  x.entity_id = "https://www.example.org/metadata"
  x.organization_name = "Acme"
  x.contact_email = "acme@example.org"
  x.organization_url = "https://www.example.org"
  x.build_service_provider do |_|
    _.add_assertion_consumer_service('https://www.example.org/assertions', binding: :http_post)
  end
end
puts metadata.to_xml(pretty: true)

The above code will generate an xml document that conforms to the SAML 2.0 specification. In this example the metadata is not signed, and 0 signing and/or encryption certificates are configured.

<?xml version="1.0" encoding="UTF-8"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_0c83f3eb-6b21-47a6-9da0-cf6dc4ee941f" entityID="https://www.example.org/metadata">
  <SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
     <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
    <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.org/assertions" index="0" isDefault="true"/>
  </SPSSODescriptor>
  <Organization>
    <OrganizationName xml:lang="en">Acme</OrganizationName>
    <OrganizationDisplayName xml:lang="en">Acme</OrganizationDisplayName>
    <OrganizationURL xml:lang="en">https://www.example.org</OrganizationURL>
  </Organization>
  <ContactPerson contactType="technical">
    <Company>mailto:acme@example.org</Company>
  </ContactPerson>
</EntityDescriptor>

Internally, saml-kit has a global configuration that is used for generating messages. You can override the global configuration with most methods and classes in saml-kit but generally having a single global configuration is enough for most applications. To register an x509 certificate with a private key for generating signatures, you can configure saml-kit like this:

Saml::Kit.configure do |config|
  config.entity_id = "https://www.example.org/metadata"
  config.add_key_pair(
    ENV['X509'],
    ENV['PRIVATE_KEY'],
    use: :signing
  )
end
metadata = Saml::Kit::Metadata.build do |x|
  x.organization_name = "Acme"
  x.contact_email = "acme@example.org"
  x.organization_url = "https://www.example.org"
  x.build_service_provider do |_|
    _.add_assertion_consumer_service(
      'https://www.example.org/assertions',
      binding: :http_post
    )
  end
end
puts metadata.to_xml(pretty: true)

This will now generate a service provider metadata that is signed and specifies the x509 certificate used for generating signatures.

<?xml version="1.0"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_70a91ccf-1948-4df1-9f87-ebfd1be8020f" entityID="https://www.example.org/metadata">
 <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
   <SignedInfo>
     <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
     <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
     <Reference URI="#_70a91ccf-1948-4df1-9f87-ebfd1be8020f">
       <Transforms>
         <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
         <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
       </Transforms>
       <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
       <DigestValue>...</DigestValue>
     </Reference>
   </SignedInfo>
   <SignatureValue>...</SignatureValue>
   <KeyInfo>
     <X509Data>
       <X509Certificate>...</X509Certificate>
     </X509Data>
   </KeyInfo>
 </Signature>
 <SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
   <KeyDescriptor use="signing">
     <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
       <X509Data>
         <X509Certificate>...</X509Certificate>
       </X509Data>
     </KeyInfo>
   </KeyDescriptor>
   <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
   <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.org/assertions" index="0" isDefault="true"/>
 </SPSSODescriptor>
 <Organization>
   <OrganizationName xml:lang="en">Acme</OrganizationName>
   <OrganizationDisplayName xml:lang="en">Acme</OrganizationDisplayName>
   <OrganizationURL xml:lang="en">https://www.example.org</OrganizationURL>
 </Organization>
 <ContactPerson contactType="technical">
   <Company>mailto:acme@example.org</Company>
 </ContactPerson>
</EntityDescriptor>

saml-kit also supports generating and consuming encrypted messages. If your service provider would like to receive encrypted assertions you can include an encryption certificate. This example will also highlight a few different ways that configuration can be generated and provided.

configuration = Saml::Kit::Configuration.new do |config|
  config.entity_id = "https://www.example.org/metadata"
  config.generate_key_pair_for(use: :signing)
  config.generate_key_pair_for(use: :encryption)
end
metadata = Saml::Kit::Metadata.build(configuration: configuration) do |x|
  x.organization_name = "Acme"
  x.contact_email = "acme@example.org"
  x.organization_url = "https://www.example.org"
  x.build_service_provider do |_|
    _.add_assertion_consumer_service('https://www.example.org/assertions', binding: :http_post)
  end
end
puts metadata.to_xml(pretty: true)

This will produce:

<?xml version="1.0"?>
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_23bec3a7-17f3-4009-9b8b-d1927115ad28" entityID="https://www.example.org/metadata">
 <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
   <SignedInfo>
     <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
     <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
     <Reference URI="#_23bec3a7-17f3-4009-9b8b-d1927115ad28">
       <Transforms>
         <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
         <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
       </Transforms>
       <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
       <DigestValue>...</DigestValue>
     </Reference>
   </SignedInfo>
   <SignatureValue>...</SignatureValue>
   <KeyInfo>
     <X509Data>
       <X509Certificate>...</X509Certificate>
     </X509Data>
   </KeyInfo>
 </Signature>
 <SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
   <KeyDescriptor use="encryption">
     <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
       <X509Data>
         <X509Certificate>...</X509Certificate>
       </X509Data>
     </KeyInfo>
   </KeyDescriptor>
   <KeyDescriptor use="signing">
     <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
       <X509Data>
         <X509Certificate>...</X509Certificate>
       </X509Data>
     </KeyInfo>
   </KeyDescriptor>
   <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
   <AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://www.example.org/assertions" index="0" isDefault="true"/>
 </SPSSODescriptor>
 <Organization>
   <OrganizationName xml:lang="en">Acme</OrganizationName>
   <OrganizationDisplayName xml:lang="en">Acme</OrganizationDisplayName>
   <OrganizationURL xml:lang="en">https://www.example.org</OrganizationURL>
 </Organization>
 <ContactPerson contactType="technical">
   <Company>mailto:acme@example.org</Company>
 </ContactPerson>
</EntityDescriptor>

With this in place identity providers can now send you encrypted assertions using your encryption certificate. Identity providers will also be able to verify that messages originated from your service provider, by verifying any signatures embedded in messages sent from your service provider to the identity provider.

Give saml-kit a try and checkout the documentation for more examples.

security 💎