java - 签名作为身份验证请求中的参数

标签 java single-sign-on saml-2.0 shibboleth opensaml

我应该得到一个带有特定参数集的 GET 身份验证请求:

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

因此,我对参数感兴趣:

  • SAML请求
  • 信号算法
  • 签名

当我使用 OIOSAML 时, 请求的所有这些部分都是自动形成的,但不可能灵活地管理它们并获得完全控制。

所以我决定使用 OpenSAML 2.4.1 编写基于 sp 的 Web SSO

考虑如何创建 SAMLRequest。我的 idP 要求 sP-AuthnRequest 请求采用以下格式:

<?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>

因此我形成的SAMLRequest如下:

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;
}

然后这条消息被压缩并用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();
}

因此,这部分 - "SAMLRequest ="+ SAMLRequest - 形成了。

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

至于 SigAlg。我使用 http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23dsa-sha1

因此,这部分也就形成了。

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

这部分的形成问题:

"&Signature=" + 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;
}

在方法 buildAuthnRequestObject() 中添加:

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

   return authRequest;
}

此消息也使用 Base64 进行压缩和编码,并采用以下形式:

<?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>

可以看出元素 SignatureValue 是空的。 如何使用 OpenSaml 获取签名并将其添加为 GET 请求参数?为此:"&Signature ="+ Signature.

最佳答案

有一个名为 HTTPRedirectDeflateEncoder 的组件可以帮助解决这个问题,添加信号、编码和压缩以及重定向用户。

我的博客上有一个使用示例,https://blog.samlsecurity.com/2011/01/redirect-with-authnrequest.html

我的书 A Guide to OpenSAML 中也有更广泛的用法示例

关于java - 签名作为身份验证请求中的参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25598788/

相关文章:

java - Liferay 6.2 CE-GA6 使 NTLM 适用于 chrome 和 firefox?

openid - SSO,Facebook连接,用户个人资料合并

python - Django 和 Shibboleth

java - 是否有类似于 ImageMagick 的简单 Java 图像处理库?

java - 锁定性能 : Lock Longer vs. 更频繁地锁定

java - 该系统找不到指定的文件?

java - 使用java代码进行Saml断言解密

java - 从对象数组访问对象类型方法 java

reactjs - 如何在react-router onEnter中等待状态变化?

java - 通过 Java struts Web 应用程序在 Azure AD 中进行 SAML 身份验证