javasingle-sign-onsaml-2.0shibbolethopensaml

Signature as parameter in the authentication request


I should get a GET authentication request with a certain set of parameters:

    "https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO?" + 
"SAMLRequest=" + SAMLRequest + "&SigAlg=" + SigAlg + "&Signature=" + Signature

Thus, I'm interested in the parameters:

When I used OIOSAML, all these parts of the request formed automatically, but it was not possible to flexibly manage them and get full control.

So I decided to write my sP- based Web SSO by using OpenSAML 2.4.1

Consider how to create SAMLRequest. My idP requires that sP- AuthnRequest requests come in this format:

<?xml version="1.0" encoding="UTF-8"?>
<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" 
   AssertionConsumerServiceURL=
      "https://ip_of_my_sp_here/oiosaml/saml/SAMLAssertionConsumer" 
   Destination="https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO" 
   ForceAuthn="false" 
   ID="_054240e4-...-e0b5e84d3a35" 
   IsPassive="false" 
   IssueInstant="2012-02-28T06:43:35.704Z" 
   ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" 
   Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
  test_issuer
</saml2:Issuer>

Therefore SAMLRequest I form as follows:

private AuthnRequest buildAuthnRequestObject() {
    DateTime issueInstant = new DateTime();

    IssuerBuilder issuerBuilder = new IssuerBuilder();
    Issuer issuer = issuerBuilder.buildObject(
       "urn:oasis:names:tc:SAML:2.0:assertion", 
       "Issuer", "samlp");
    issuer.setValue(issuerId);

    AuthnRequestBuilder authRequestBuilder = new AuthnRequestBuilder();
    AuthnRequest authRequest = 
        authRequestBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:protocol", 
           "AuthnRequest", "samlp");
    authRequest.setForceAuthn(new Boolean(false));
    authRequest.setIsPassive(new Boolean(false));
    authRequest.setIssueInstant(issueInstant.plusHours(4));
    authRequest.setProtocolBinding(
       "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
    authRequest.setAssertionConsumerServiceURL(consumerUrl);
    authRequest.setDestination(idPDestinationUrlSSO);
    authRequest.setIssuer(issuer);
    authRequest.setID(authReqRandomId);
    authRequest.setVersion(SAMLVersion.VERSION_20); 

    return authRequest;
}

Then this message is compressed and encoded in Base64:

private String encodeRequestMessage(RequestAbstractType requestMessage)
        throws MarshallingException, IOException {
    Marshaller marshaller = 
       Configuration.getMarshallerFactory().getMarshaller(requestMessage);
    Element authDOM = marshaller.marshall(requestMessage);

    Deflater deflater = new Deflater(Deflater.DEFLATED, true);
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    DeflaterOutputStream deflaterOutputStream =
            new DeflaterOutputStream(byteArrayOutputStream,
                    deflater);

    StringWriter rspWrt = new StringWriter();
    XMLHelper.writeNode(authDOM, rspWrt);
    deflaterOutputStream.write(rspWrt.toString().getBytes());
    deflaterOutputStream.close();

    String encodedRequestMessage =
            Base64.encodeBytes(byteArrayOutputStream.toByteArray(),
                    Base64.DONT_BREAK_LINES);
    return URLEncoder.encode(encodedRequestMessage, "UTF-8").trim();
}

Thus, this part - "SAMLRequest =" + SAMLRequest - is formed.

"https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO?" + 
  "SAMLRequest=" + encodedRequestMessage

As for SigAlg. I use http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23dsa-sha1

Therefore, this part also is formed.

"https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO?" + 
   "SAMLRequest=" + encodedRequestMessage + "&SigAlg=" + SigAlg

Question in forming of this part:

"&Signature=" + Signature

I get a signature so (but this method does not give the signature):

public Signature getSignature() {
    KeyStore keyStore = 
       KeyStore.getInstance(KeyStore.getDefaultType()); 
    FileInputStream fileInputStream = 
       new FileInputStream(new File(jksFileName));
    keyStore.load(fileInputStream, jksPassword);
    fileInputStream.close();        
    KeyStore.PrivateKeyEntry privateKeyEntry =          
       (KeyStore.PrivateKeyEntry) keyStore.getEntry(alias, 
          new KeyStore.PasswordProtection(jksPassword));
    PrivateKey privateKey = privateKeyEntry.getPrivateKey();
    X509Certificate certificate = 
       (X509Certificate) privateKeyEntry.getCertificate();
    BasicX509Credential credential = new BasicX509Credential();
    credential.setEntityCertificate(certificate);
    credential.setPrivateKey(privateKey);

    Signature signature = 
    (Signature) org.opensaml.xml.Configuration.getBuilderFactory()               
     .getBuilder(org.opensaml.xml.signature.Signature.DEFAULT_ELEMENT_NAME)
       .buildObject(org.opensaml.xml.signature.Signature.DEFAULT_ELEMENT_NAME);
    signature.setSigningCredential(credential);
    SecurityConfiguration securityConfiguration = 
       Configuration.getGlobalSecurityConfiguration();
    String keyInfoGeneratorProfile = null;
    SecurityHelper.prepareSignatureParams(signature, 
      credential, securityConfiguration, keyInfoGeneratorProfile);

    return signature;
}

In the method buildAuthnRequestObject() is added:

private AuthnRequest buildAuthnRequestObject() {
   ...   
   authRequest.setSignature(getSignature()); // <---- to sign the request

   return authRequest;
}

This message also compressed and encoded in Base64 and takes the form:

<?xml version="1.0" encoding="UTF-8"?>
<samlp:AuthnRequest AssertionConsumerServiceURL=
    "https://ip_of_my_sp_here/EsiaChecker_war/resp/"
                    Destination=
    "https://domain_name_of_idp_here/idp/profile/SAML2/Redirect/SSO"
                    ForceAuthn="false" 
                    ID="0" 
                    IsPassive="false" 
                    IssueInstant="2014-09-1T19:07:42.718Z"
                    ProtocolBinding=
         "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0"
                    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
    <samlp:Issuer xmlns:samlp=
         "urn:oasis:names:tc:SAML:2.0:assertion">my_issuer_here
    </samlp: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/2000/09/xmldsig#rsa-sha1"/>
            <ds:Reference URI="#0">
                <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#"/>
                </ds:Transforms>
                <ds:DigestMethod Algorithm=
                      "http://www.w3.org/2000/09/xmldsig#sha1"/>
                <ds:DigestValue/>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue/>
        <ds:KeyInfo>
            <ds:X509Data>
                <ds:X509Certificate>MIIEXD...0CFtd6whg==
                </ds:X509Certificate>
            </ds:X509Data>
        </ds:KeyInfo>
    </ds:Signature>
</samlp:AuthnRequest>

It is seen that the element SignatureValue is empty. How can I get the signature using OpenSaml and add it as a GET request parameter? To make it so: "&Signature =" + Signature.


Solution

  • There is a component called HTTPRedirectDeflateEncoder that will help with this, adding the sigalg, encoding and deflating and redirecting the user.

    I have an example of usage on my blog, https://blog.samlsecurity.com/2011/01/redirect-with-authnrequest.html

    I also have wider examples of its usage in my book, A Guide to OpenSAML