Giter Site home page Giter Site logo

Comments (30)

derwydd avatar derwydd commented on August 22, 2024

This is the required output:

<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema" Destination="https://url" ID="" IssueInstant="2016-01-30T19:40:30.335Z" Version="2.0">
  <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/444e</saml2:Issuer>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
      <ds:Reference URI="#">
        <ds:Transforms>
          <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
          <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
            <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/>
          </ds:Transform>
        </ds:Transforms>
        <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
        <ds:DigestValue>digest needs to be inserted here=</ds:DigestValue>
      </ds:Reference>
    </ds:SignedInfo>
    <ds:SignatureValue>signature needs to be inserted here</ds:SignatureValue>
    <ds:KeyInfo>
      <ds:X509Data>
        <ds:X509Certificate>cert needs to be inserted here</ds:X509Certificate>
      </ds:X509Data>
    </ds:KeyInfo>
  </ds:Signature>
  <saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
  </saml2p:Status>
  <saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xs="http://www.w3.org/2001/XMLSchema" ID="" IssueInstant="2016-01-30T19:40:30.335Z" Version="2.0">
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://www.okta.com/44e</saml2:Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
      <ds:SignedInfo>
        <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
        <ds:Reference URI="#">
          <ds:Transforms>
            <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
            <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
              <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs"/>
            </ds:Transform>
          </ds:Transforms>
          <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
          <ds:DigestValue>digest needs to be inserted here</ds:DigestValue>
        </ds:Reference>
      </ds:SignedInfo>
      <ds:SignatureValue>signature needs to be inserted here</ds:SignatureValue>
      <ds:KeyInfo>
        <ds:X509Data>
          <ds:X509Certificate>cert needs to be inserted here</ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </ds:Signature>
    <saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
      <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">[email protected]</saml2:NameID>
      <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
        <saml2:SubjectConfirmationData NotOnOrAfter="2016-01-30T19:45:30.335Z" Recipient="https://url"/>
      </saml2:SubjectConfirmation>
    </saml2:Subject>
    <saml2:Conditions xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" NotBefore="2016-01-30T19:35:30.335Z" NotOnOrAfter="2016-01-30T19:45:30.335Z">
      <saml2:AudienceRestriction>
        <saml2:Audience>COMPANY</saml2:Audience>
      </saml2:AudienceRestriction>
    </saml2:Conditions>
    <saml2:AuthnStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" AuthnInstant="2016-01-30T19:40:30.335Z" SessionIndex="34567456789">
      <saml2:AuthnContext>
        <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
      </saml2:AuthnContext>
    </saml2:AuthnStatement>
    <saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
      <saml2:Attribute Name="login" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
        <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">[email protected]</saml2:AttributeValue>
      </saml2:Attribute>
    </saml2:AttributeStatement>
  </saml2:Assertion>
</saml2p:Response>

from libsaml.

benoist avatar benoist commented on August 22, 2024

If you use the protocol binding classes adding signatures is done for you. If you don't use those, then you need to use the Saml::Util.sign_xml function. The add_signature function only adds the signature element, it does not sign it yet.

from libsaml.

derwydd avatar derwydd commented on August 22, 2024

Is there a tutorial or example on the binding classes? Are you referring to the httpost in the bindings class?

from libsaml.

benoist avatar benoist commented on August 22, 2024

Yes depending on the requirements on the service provider side you can choose between HTTPPost, HTTPArtifact and the HTTPRedirect binding. They are explained here:
https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf

Unfortunately the only thing we have for now is the documentation in the readme.

from libsaml.

derwydd avatar derwydd commented on August 22, 2024

the sign_xml function worked. much appreciated. However, now I get Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd. Basically my code and out are identical to the first output however I have the needed sections signed.

from libsaml.

derwydd avatar derwydd commented on August 22, 2024

Also, the Issuer does not include the Format.

from libsaml.

derwydd avatar derwydd commented on August 22, 2024

Ok, it looks like I solved some issues. We now are having a "digest mismatch" error. Not sure if this is due to the generation of the keys?

from libsaml.

benoist avatar benoist commented on August 22, 2024

Digest mismatches only occur when the XML has been altered after signing. The sign_xml function returns a signed version of the XML in string format. If you want to verify this signature, you have to use the raw version, so you cannot parse the response with the saml objects and then verify the signature. You can use the verify_xml util function to verify raw signed xml versions.

from libsaml.

derwydd avatar derwydd commented on August 22, 2024

Ok so I'm passing this to another server and they are reporting this. So yes I was doing two things after the sign_ xml. First converting this to a string but based out of habit, then removing all \n and \r. Then I was base64 encoding it with the rails base64 lib, after which removing \n and \r in the base64 since for some odd reason it was showing up there as well. So your saying just send the xml doc. They want it base64 encoded. How do I handle that. Thanks for your help.

from libsaml.

benoist avatar benoist commented on August 22, 2024

It looks like you are trying to support the HTTP Post binding. Any reason why you don't/can't use the Binding function to do the signing and conversion to Base64 as thats all included in the binding?

https://github.com/digidentity/libsaml/blob/master/lib/saml/bindings/http_post.rb

from libsaml.

derwydd avatar derwydd commented on August 22, 2024

I tried .create_form_attributes from http post before and the format requested was off. However, there has been changes since then, and I will try this method again. So correct me if I'm wrong, just pass the response as an argument to .create_form_attributes. Thanks

from libsaml.

benoist avatar benoist commented on August 22, 2024

Correct, if you need the assertion to be signed as well, you have to call add_signature on the assertion before sending it to the binding function.

from libsaml.

derwydd avatar derwydd commented on August 22, 2024

Perfect, that's what I'm doing now. I call that after all the assertions are set up. So, I will attempt again and let you know . Much appreciated

from libsaml.

benoist avatar benoist commented on August 22, 2024

If you don't need signed assertions and signed responses, you don't need to call add_signature, the binding function will also do that for you (well actually the util function will). Only when you need 2 signatures one for the assertion and one for the response, you need to call add_signature on the assertion object. Calling add_signature multiple times has no side effect though :)

from libsaml.

derwydd avatar derwydd commented on August 22, 2024

Thank you for all your help, everything is working now...

from libsaml.

benoist avatar benoist commented on August 22, 2024

You're welcome. Happy to hear that it all works now.

from libsaml.

sumanranjanpanda avatar sumanranjanpanda commented on August 22, 2024

Hi Benoist,

I am new to SAML and trying your gem to build SAML response as an IdP & send it to SP from my rails application.
Here are my application detail

ruby-2.2.1
rails-3.2.19
libsaml-2.20.4

Could you give an example to add signature to the SAML response from certificate with pfx extenstion?
How to use this file to add the signature to SAML response?
In a .net code I found that it is using the following code.
X509Certificate2(fileName, password, X509KeyStorageFlags.MachineKeySet);
Here fileName is the pfx certificate file and having a password.

Thanks,
Suman

from libsaml.

benoist avatar benoist commented on August 22, 2024

Hi,

The signing api expects RSA private keys and X509 certificates to create and verify signatures.
So you would have to convert your pfx file to a that first. I haven't tried it but I think this should work

p12 = OpenSSL::PKCS12.new(File.read('somefile.pfx'), 'yourpass')
cert = OpenSSL::X509::Certificate.new(p12.certificate)
key = OpenSSL::PKey::RSA.new(p12.key)

Now you have the correct objects to sign and verify and you can use libsaml as described in the examples.

from libsaml.

sumanranjanpanda avatar sumanranjanpanda commented on August 22, 2024

Thank you for your quick response. I have used the Saml::Util.encrypt_assertion to sign using the certificate.

But when I am calling Saml::Bindings::HTTPPost.create_form_attributes, it put me in infinite loop with the below output on console

[DEPRECATED] `private_key` please use signing_key or encryption_key
[DEPRECATED] `private_key` please use signing_key or encryption_key
stack level too deep
/home/sumanp/.rvm/gems/ruby-2.2.1/gems/libsaml-2.20.4/lib/saml/provider.rb:50:in `private_key'
/home/sumanp/.rvm/gems/ruby-2.2.1/gems/libsaml-2.20.4/lib/saml/provider.rb:59:in `encryption_key'

Here is MyIdPClass

class MyIdPClass
  extend Saml::Rails::ControllerHelper

  attr_reader :idp_url, :assertion_consumer_url, :login_url,
              :logout_url, :settings, :auth_request_id, :certificate

  def initialize(service_settings)
    @settings = service_settings
    @idp_url = settings[:idp_url]
    @assertion_consumer_url = settings[:assertion_consumer_url]
    @login_url = settings[:login_url]
    @logout_url = settings[:logout_url]
    @auth_request_id =  "_#{Time.now.to_i}"
    p12 = OpenSSL::PKCS12.new(File.read(settings[:certificate_path]), settings[:certificate_password])
    @certificate = OpenSSL::X509::Certificate.new(p12.certificate)
    @signing_key = OpenSSL::PKey::RSA.new(p12.key)
  end

  def send_SAMLResponse_as_Idp_for_SSO(user, options={})
    response = build_SAMLResponse(user)
    Saml::Bindings::HTTPPost.create_form_attributes(response, login_url)
  end

  private

  def build_SAMLResponse(user, options={})
    Saml::Response.new(in_response_to: "_#{Time.now.to_i}",
                       assertion:      build_assertion(user),
                       status_value:   Saml::TopLevelCodes::SUCCESS)
  end

  def build_assertion(user)
    account = user.accounts.first
    assertion = Saml::Assertion.new(
      name_id:                 user.email,
      name_id_format:          'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
      authn_context_class_ref: Saml::ClassRefs::PASSWORD_PROTECTED,
      in_response_to:          "_#{Time.now.to_i}",
      recipient:               assertion_consumer_url,
      audience:                idp_url)
    assertion.add_attribute('userEmailID', user.email)
    assertion.add_attribute('companyName', user.forename)
    assertion.add_attribute('forename', user.surname)
    # assertion.add_signature
    Saml::Util.encrypt_assertion(assertion, certificate)
    return assertion
  end
end

MyIdPClass.new.send_SAMLResponse_as_Idp_for_SSO(User.last)

Could you please help me on this?

Thanks,
Suman

from libsaml.

benoist avatar benoist commented on August 22, 2024

I see you're missing the current_provider setting

You'd have to set it so that the binding functions know which provider to use.

One way to do that would be:

current_provider :current_identity_provider

private 

def current_identity_provider
  Saml::BasicProvider.new(entity_descriptor, private_key, 'identity_provider')
end

The entity descriptor expects an entity descriptor object.
The easiest way to create that is to add another function in the controller that returns it.
This also allows you to publish your metadata on an endpoint that service providers can use to configure their service provider settings.

def entity_descriptor
  signing_key_descriptor    = Saml::Elements::KeyDescriptor.new(certificate: certificate.to_pem, use: 'signing')
  encryption_key_descriptor = Saml::Elements::KeyDescriptor.new(certificate: certificate.to_pem, use: 'encryption')

  single_sign_on_service = Saml::Elements::IDPSSODescriptor::SingleSignOnService.new(
      binding:  Saml::ProtocolBinding::HTTP_POST,
      location: receive_authn_request_url
  )

  idp_sso_descriptor = Saml::Elements::IDPSSODescriptor.new(
      single_sign_on_services: [single_sign_on_service],
      key_descriptors:         [signing_key_descriptor, encryption_key_descriptor]
  )

  entity_descriptor                    = Saml::Elements::EntityDescriptor.new(entity_id: 'yourentityid')
  entity_descriptor.idp_sso_descriptor = idp_sso_descriptor

  entity_descriptor
end

from libsaml.

sumanranjanpanda avatar sumanranjanpanda commented on August 22, 2024

@benoist Thank you for your help. I found that Saml::BasicProvider initialize method requires 4 parameters. So, what will be the last parameter.

And could you please let me know where to get the values for the 'identity_provider' & 'yourentityid'?

Only I have the following things


1. idp_url (that is my application URL)
2. service provider assertion_consumer_url
3. service provider login_url
4. service provider logout_url
5. Certificate with pfx extension

from libsaml.

sumanranjanpanda avatar sumanranjanpanda commented on August 22, 2024

Hi Benoist, I am new to SAML and sorry for asking a silly question to you. but still I am getting the same error

when I am calling Saml::Bindings::HTTPPost.create_form_attributes, it put me in infinite loop with the below output on console


[DEPRECATED] `private_key` please use signing_key or encryption_key
[DEPRECATED] `private_key` please use signing_key or encryption_key
stack level too deep
/home/sumanp/.rvm/gems/ruby-2.2.1/gems/libsaml-2.20.4/lib/saml/provider.rb:50:in `private_key'
/home/sumanp/.rvm/gems/ruby-2.2.1/gems/libsaml-2.20.4/lib/saml/provider.rb:59:in `encryption_key'

Added the current_provider :current_identity_provider and replaced the 'identity_provider' & 'yourentityid' with idp_url (my application URL).

  def current_identity_provider
    Saml::BasicProvider.new(entity_descriptor, private_key, idp_url, private_key)
  end

and
entity_descriptor = Saml::Elements::EntityDescriptor.new(entity_id: idp_url)

Could you please help me on this?

Thanks,
Suman

from libsaml.

benoist avatar benoist commented on August 22, 2024

There was a bug in version < 2.20.4 that goes in to an endless loop if the private key is not set.
Version 2.20.5 is now pushed and addresses that issue.

So what this stack trace that you receive tells me, is that your private key is nil. Please make sure you correctly extract a private key from your pfx file.

from libsaml.

sumanranjanpanda avatar sumanranjanpanda commented on August 22, 2024

Yes that endless loop issue is fixed now but getting the below issue

undefined method `sign' for nil:NilClass
libsaml-master/lib/saml/provider.rb:63:in `sign'

I have extracted the private key as per your suggestion and getting the value correctly #<OpenSSL::PKey::RSA:0x0000000c5cde40>

But I found that the value of message.provider is #<Saml::NullProvider:0x0000000a073b18>. And getting error because of this when it calls the sign method.

def sign_xml(message, format = :xml, &block)
     .........
     document.sign do |data, signature_algorithm|
         message.provider.sign(signature_algorithm, data)
     end
     .....
end

from libsaml.

benoist avatar benoist commented on August 22, 2024

Are you using a rails controller?

from libsaml.

sumanranjanpanda avatar sumanranjanpanda commented on August 22, 2024

No, in a class. But included ::AbstractController::Callbacks to get the before_filter helper

from libsaml.

benoist avatar benoist commented on August 22, 2024

Yeah thats not going to work. Including the callbacks module is not enough to get it work.
You don't really need the Rails helper, but what you do need to do is set the Saml.current_provider
This should be set to your basic provider that you created.

from libsaml.

sumanranjanpanda avatar sumanranjanpanda commented on August 22, 2024

Yes, I have tried that too. Here is my class.

class MyIdpClass
  extend Saml::Rails::ControllerHelper

  attr_reader :idp_url, :assertion_consumer_url, :login_url,
              :logout_url, :settings, :auth_request_id, :certificate, :private_key
  def initialize(service_settings = IDPSETTINGS || {})
    @settings = service_settings
    @idp_url = settings[:idp_url]
    @assertion_consumer_url = settings[:assertion_consumer_url]
    @login_url = settings[:login_url]
    @logout_url = settings[:logout_url]
    @auth_request_id =  "_#{Time.now.to_i}"
    p12 = OpenSSL::PKCS12.new(File.read(settings[:certificate_path]), settings[:certificate_password])
    @certificate = OpenSSL::X509::Certificate.new(p12.certificate)
    @private_key = OpenSSL::PKey::RSA.new(p12.key)
  end



  def send_SAMLResponse_as_Idp_for_SSO(user, options={})
    set_current_identity_provider ## saml current provider setup
    response = build_SAMLResponse(user)
    Saml::Bindings::HTTPPost.create_form_attributes(response, login_url)
  end

  private

  def set_current_identity_provider
    Saml::BasicProvider.new(entity_descriptor, private_key, idp_url, private_key)
  end

  def entity_descriptor
    signing_key_descriptor    = Saml::Elements::KeyDescriptor.new(certificate: certificate.to_pem, use: 'signing')
    encryption_key_descriptor = Saml::Elements::KeyDescriptor.new(certificate: certificate.to_pem, use: 'encryption')

    single_sign_on_service = Saml::Elements::IDPSSODescriptor::SingleSignOnService.new(
        binding:  Saml::ProtocolBinding::HTTP_POST,
        location: assertion_consumer_url
    )

    idp_sso_descriptor = Saml::Elements::IDPSSODescriptor.new(
        single_sign_on_services: [single_sign_on_service],
        key_descriptors:         [signing_key_descriptor, encryption_key_descriptor]
    )

    entity_descriptor                    = Saml::Elements::EntityDescriptor.new(entity_id: idp_url)
    entity_descriptor.idp_sso_descriptor = idp_sso_descriptor

    entity_descriptor
  end

  def build_SAMLResponse(user, options={})
    Saml::Response.new(in_response_to: auth_request_id,
                       assertion:      build_assertion(user),
                       status_value:   Saml::TopLevelCodes::SUCCESS)
  end

  def build_assertion(user)
    account = user.accounts.first
    assertion = Saml::Assertion.new(
      name_id:                 user.email, # Return anything that you can link to an account
      name_id_format:          'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
      authn_context_class_ref: Saml::ClassRefs::PASSWORD_PROTECTED,
      in_response_to:          auth_request_id,
      recipient:               assertion_consumer_url,
      audience:                idp_url)
    assertion.add_attribute('userEmailID', user.email)
    assertion.add_attribute('companyName', user.forename)
    # assertion.add_signature
    Saml::Util.encrypt_assertion(assertion, certificate)
    return assertion
  end

end

from libsaml.

benoist avatar benoist commented on August 22, 2024

You need to set the Saml.current_provider

def set_current_identity_provider
  Saml.current_provider = Saml::BasicProvider.new(entity_descriptor, private_key, idp_url, private_key)
end

from libsaml.

sumanranjanpanda avatar sumanranjanpanda commented on August 22, 2024

Able to generate the SAMLResponse.. Thank you so much for your valuable time. Got the response as follows. Wondering why the location is nil.

{:location=>nil, :variables=>{"SAMLResponse"=>"PD94bWwgdmVyc2lvbj0iMS4w..........."}}

from libsaml.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.