Comments (30)
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.
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.
Is there a tutorial or example on the binding classes? Are you referring to the httpost in the bindings class?
from libsaml.
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.
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.
Also, the Issuer does not include the Format.
from libsaml.
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.
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.
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.
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.
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.
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.
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.
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.
Thank you for all your help, everything is working now...
from libsaml.
You're welcome. Happy to hear that it all works now.
from libsaml.
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.
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.
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.
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.
@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.
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.
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.
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.
Are you using a rails controller?
from libsaml.
No, in a class. But included ::AbstractController::Callbacks to get the before_filter helper
from libsaml.
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.
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.
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.
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)
- what should be the identity id with uri in idp and sp HOT 4
- Provide access to ResponseLocation property HOT 2
- Sign using SHA1 HOT 6
- xmldsig dependency issue HOT 6
- SOAP binding not checking for WantAuthnRequestsSigned HOT 1
- Support multiple entity descriptors in metadata when using Url ProviderStore HOT 1
- KeyInfo node in EncryptedData node HOT 5
- chomp vs gsub("\n", "") on Base64 encoding HOT 2
- Help sought on “Digest value” & “Signature value” HOT 2
- ECP profile support? HOT 7
- Support for SAML responses where only the assertion is signed HOT 2
- Support version of rails <= 4.2 HOT 1
- Onelogin: undefined method `canonicalize' for nil:NilClass HOT 18
- Questions about documentation HOT 20
- Azure AADSTS700016: Application with identifier 'https://sts.windows.net/xxxx-xxxx-xxxx/' was not found in the directory 'xxxx-xxxx-xxxx'. HOT 6
- Question about SAML Response validation HOT 1
- Redundant use of XmlMini_REXML HOT 2
- Add element SessionNotOnOrAfter HOT 1
- RSpec fails after upgrading(`~> 1.8`) gem `nokogiri`.
- Signature verification with multiple certificates when validating SAML responses HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from libsaml.