2
votes

I'm trying to create a SAML AuthnRequest for the redirect binding.

This is my (anonymized) AuthRequest:

<samlp:AuthnRequest AssertionConsumerServiceURL="https://tempuri.org/sp/AssertionConsumerService"
                    Destination="https://anothertempuri.org/broker/sso"
                    ID="a18471d18a93430a8b97b05989ae238f"
                    IssueInstant="2017-07-23T14:15:26Z"
                    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
                    Version="2.0"
                    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
  <saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">...</saml:Issuer>
</samlp:AuthnRequest>

I have a RelayState:

RelayState: 821eea8785134bbc8263be6838eda19d

And the SigAlg is http://www.w3.org/2001/04/xmldsig-more#rsa-sha256

I deflate, then base64-encode, then URL-encode the request. (The deflated, base64-encoded result checks out at the SAMLTool Decoder.)

The resulting string to be used for creating the signature then looks something like this:

SAMLRequest=fZL...5MP&RelayState=821eea8785134bbc8263be6838eda19d&SigAlg=http%3A%2F%2Fwww.w3.org%2F2001%2F04%2Fxmldsig-more%23rsa-sha256

I tried creating the signature with openssl:

openssl sha -sha256 -sign example.pem -out signed.sha256 theStringInAFile.txt
openssl base64 -in signed.sha256

and with C#

byte[] data = File.ReadAllBytes(FileToSign);
data = SignBytes(data);
X509Certificate2 certificate = new X509Certificate2(CertFile, Password);
using (RSA rsa = certificate.GetRSAPrivateKey())
{
    data = rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
signedData = Convert.ToBase64String(data, Base64FormattingOptions.None);

And (also with c#):

var signatureDescription = (SignatureDescription)CryptoConfig.CreateFromName("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
HashAlgorithm hashAlg = signatureDescription.CreateDigest();
hashAlg.ComputeHash(Encoding.UTF8.GetBytes(signString));

X509Certificate2 certificate = new X509Certificate2(CertFile, Password);

AsymmetricSignatureFormatter asymmetricSignatureFormatter = signatureDescription.CreateFormatter(certificate.GetRSAPrivateKey());

byte[] signatureValue = asymmetricSignatureFormatter.CreateSignature(hashAlg);
return Convert.ToBase64String(signatureValue);

All three methods result in the same base64-encoded string (except for the newlines in the openssl result).

Unfortunately, when I try to use this with the broker, they say something is wrong with the signature, and when I feed the same info to the SAMLTool AuthnRequest signing tool I get a different result.

What am I doing wrong?

1
This is a programming issue. I suggest you to post in StackOverflowpedrofb
it's unclear from your example if you're signing the base64 version or the original xml. Can you clarify? I think you want to be signing the xml, not the base64 of it.explunit
@explunit: I sign the complete string SAMLRequest=...&RelayState=...&SigAlg=... as per the spec section 3.4.4.1, second item 3.Miel
You're right, sorry.explunit
I don't have time to compare your code exactly, but see here a working example from Kentor.AuthServices. It's LGPL licensed. github.com/KentorIT/authservices/blob/v0.21.2/…explunit

1 Answers

1
votes

The problem is that the string that needs to be signed is in a text file, which (as a rule) includes a newline at the end of each line. The string that should be signed, however, does not include the newline.

That means the solution is to trim the newline from the string, e.g. by replacing

byte[] data = File.ReadAllBytes(FileToSign);

from the first block of C# code with

string signString = File.ReadAllText(FileToSign);
signString = signString.TrimEnd('\r', '\n');
byte[] data = Encoding.UTF8.GetBytes(signString);