CAS + SSLHandshakeException + ValidatorException + PKIX path building failed + 无法找到有效的证书路径以请求目标

5
我正在使用Central Authentication System (jasig.org)来实现我的内部网站应用程序的单点登录功能。我在同一台Windows机器上运行了两个Tomcat实例。这两个Tomcat实例都已配置为使用SSL,并且已经使用自签名证书(使用Java keytool创建)。

Tomcat1

Cas服务器。

server.xml

<Connector port="8443" maxHttpHeaderSize="8192" SSLEnabled="true"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" 
           keystoreFile="C:/Users/sandip.paul/.keystore"
           keystorePass="changeit"
           truststoreFile="C:/Program Files/Java/jdk1.6.0_20/jre/lib/security/cacerts" />

Tomcat2

我的Web应用程序(使用Spring Security)

server.xml

<Connector port="8663" maxHttpHeaderSize="8192" SSLEnabled="true"
               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
               enableLookups="false" disableUploadTimeout="true"
               acceptCount="100" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />

以下是myWebApp的applicationContext-security.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:security="http://www.springframework.org/schema/security"
    xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                        http://www.springframework.org/schema/security
                        http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">

    <!--
        Enable security, let the casAuthenticationEntryPoint handle all intercepted urls.
        The CAS_FILTER needs to be in the right position within the filter chain.
    -->
    <security:http entry-point-ref="casProcessingFilterEntryPoint" auto-config="true">
        <security:intercept-url pattern="/protected/**" access="IS_AUTHENTICATED_FULLY" />
    </security:http>

    <!--
        Required for the casProcessingFilter, so define it explicitly set and
        specify an Id Even though the authenticationManager is created by
        default when namespace based config is used.
    -->
    <security:authentication-manager alias="authenticationManager" />

    <!--
        This section is used to configure CAS. The service is the
        actual redirect Client URL that will be triggered after the CAS login sequence.
    -->
    <bean id="serviceProperties" class="org.springframework.security.ui.cas.ServiceProperties">
        <property name="service" value="https://obll1973.abc.com:8663/myWebApp/j_spring_cas_security_check"/>
        <property name="sendRenew" value="false"/>
    </bean>

    <!-- 
        The customUserDetailsService provides ROLE & other Details and 
        create an object of UserDetail for this application.
    -->
    <bean id="customUserDetailsService"
        class="edu.sandip.cas.client.authentication.CustomUserDetailsService">
    </bean>

    <!-- 
        The CasProcessingFilter has very similar properties to the 
        AuthenticationProcessingFilter (used for form-based logins).

        The CAS Processing filter handles the redirect from the CAS server 
        and starts the ticket validation.
    -->
    <bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
        <security:custom-filter after="CAS_PROCESSING_FILTER"/>
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationFailureUrl" value="/casfailed.jsp"/>
        <property name="defaultTargetUrl" value="/"/>
    </bean>

    <!--
        The entryPoint intercepts all the CAS authentication requests.
        It redirects to the CAS loginUrl for the CAS login page.
    -->
    <bean id="casProcessingFilterEntryPoint" 
        class="org.springframework.security.ui.cas.CasProcessingFilterEntryPoint">
        <property name="loginUrl" value="https://obll1973.abc.com:8443/cas/login"/>
        <property name="serviceProperties" ref="serviceProperties"/>
    </bean>

    <!-- 
        Handles the CAS ticket processing.
    -->
    <bean id="casAuthenticationProvider" 
        class="org.springframework.security.providers.cas.CasAuthenticationProvider">
        <security:custom-authentication-provider />
        <property name="userDetailsService" ref="customUserDetailsService" />
        <property name="serviceProperties" ref="serviceProperties" />
        <property name="ticketValidator">
            <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <constructor-arg index="0" value="https://obll1973.abc.com:8443/cas" />
            </bean>
        </property>
        <property name="key" value="an_id_for_this_auth_provider_only"/>    
    </bean>

    <!-- 
        To access request.getRemoteUser() from client application  
    -->
    <bean id="wrappingFilter" class="org.jasig.cas.client.util.HttpServletRequestWrapperFilter" />
</beans>

我面临的问题是以下异常:

java.lang.RuntimeException: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:341)
    org.jasig.cas.client.util.CommonUtils.getResponseFromServer(CommonUtils.java:305)
    org.jasig.cas.client.validation.AbstractCasProtocolUrlBasedTicketValidator.retrieveResponseFromServer(AbstractCasProtocolUrlBasedTicketValidator.java:50)
    org.jasig.cas.client.validation.AbstractUrlBasedTicketValidator.validate(AbstractUrlBasedTicketValidator.java:207)
    org.springframework.security.providers.cas.CasAuthenticationProvider.authenticateNow(CasAuthenticationProvider.java:145)
    org.springframework.security.providers.cas.CasAuthenticationProvider.authenticate(CasAuthenticationProvider.java:131)
    org.springframework.security.providers.ProviderManager.doAuthentication(ProviderManager.java:188)
    org.springframework.security.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:46)
    org.springframework.security.ui.cas.CasProcessingFilter.attemptAuthentication(CasProcessingFilter.java:94)
    org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:258)
    org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
    org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
    org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:236)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)

复现上述异常的步骤: 1) 我尝试使用 https://obll1973.abc.com:8663/myWebApp/protected/ 访问 myWebApp 2) 会被重定向到cas服务器,即 https://obll1973.abc.com:8443/cas/login 并提供用户名/密码 在这里,当myWebApp再次向cas服务器发送请求以验证生成的令牌时,出现了异常。
如有任何建议或帮助,将不胜感激。
1个回答

9
您的Spring-CAS配置看起来很好。
唯一出现 SSLHandshakeException 的原因是由于SSL密钥不正确地导入到创建的密钥库或JVM密钥库中。

在创建密钥时,您是否遵循以下步骤

注意:别名名称即CN(这里是cas)应与机器的主机名相同。

要查找机器的主机名,请在控制台或命令提示符中键入“hostname”(不带引号)。

在根目录下创建一个名为app的目录。

[root@localhost app]# keytool -genkey -alias cas -keyalg RSA -keystore .cas -storepass caspasswd

你的名字和姓氏是什么?[未知]: cas 你的组织单位名称是什么?[未知]: MYCOMPANY 你的组织名称是什么?[未知]: MYCOMPANY 你所在城市或地区的名称是什么?[未知]: Bangalore 你所在州或省的名称是什么?[未知]: Karnataka 这个单元的两位国家代码是什么?[未知]: IN CN=cas, OU=MYCOMPANY, O=MYCOMPANY, L=Bangalore, ST=Karnataka, C=IN 正确吗?[否]: 是

输入密钥密码(使用上面提到的密码,即在这种情况下为caspasswd) (如果与密钥库密码相同,则返回):重新输入新密码:

[root@localhost app]# keytool -exportcert -alias cas -file cas.crt -keystore .cas 输入密钥库密码:证书存储在文件中

[root@localhost app]# ls cas.crt softwares test.class test.java tomcat7027CAS

[root@localhost app]# keytool -import -alias cas -file cas.crt -keystore /app/softwares/jdk1.6.0_27/jre/lib/security/cacerts 输入密钥库密码:所有者:CN=cas,OU=MYCOMPANY,O=MYCOMPANY, L=Bangalore,ST=Karnataka,C=IN 发行者:CN=cas,OU=MYCOMPANY, O=MYCOMPANY,L=Bangalore,ST=Karnataka,C=IN 序列号:510a6a63 有效期从:Thu Jan 31 18:28:11 IST 2013 到:Wed May 01 18:28:11 IST 2013 证书指纹: MD5: 52:8E:2E:74:C6:57:CD:B3:B0:B6:6C:17:D9:0D:77:F3 SHA1: 1F:AA:4C:22:B9:16:DC:AA:D4:87:07:CF:DD:B2:11:A6:AE:36:9A:DB 签名算法名称:SHA1withRSA 版本:3 信任这个证书?[否]: 是 证书已添加到密钥库

[root@localhost app]# keytool -list -keystore /app/softwares/jdk1.6.0_27/jre/lib/security/cacerts -alias cas 输入密钥库密码:cas, Jan 31, 2013, trustedCertEntry, Certificate fingerprint (MD5): 52:8E:2E:74:C6:57:CD:B3:B0:B6:6C:17:D9:0D:77:F3

[root@localhost app]#

在您的Tomcat server.xml文件中,您还可以使用以下https连接器:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true"
               maxThreads="150" scheme="https" keystoreFile="/app/.cas" keystorePass="mykeypassword" 
    secure="true" connectionTimeout="240000" 
               clientAuth="false" sslProtocol="TLS" allowUnsafeLegacyRenegotiation="true" />
    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8069" protocol="AJP/1.3" redirectPort="8443" />

上面的密钥是用于CAS服务器的。 您可以为应用程序创建自己的独立密钥。 但是,两个密钥都应导入到JVM cacerts密钥库中(如何:在下面提到)。 现在最重要的部分: 假设您的CAS服务器别名为cas, 您的应用程序服务器别名为myapp。 保持cas.crt和myapp.crt两个密钥在两个服务器中都进行导入到JVM。 在两个服务器上执行以下操作: 1)keytool -import -alias cas -file /app/cas.crt -keystore /usr/java/jdk1.5.0_22/jre/lib/security/cacerts 2)keytool -import -alias myapp -file /app/cas.crt -keystore /usr/java/jdk1.5.0_22/jre/lib/security/cacerts 使用以下命令进行验证。

1) keytool -list -v -keystore /usr/java/jdk1.5.0_22/jre/lib/security/cacerts -alias cas

2) keytool -list -v -keystore /usr/java/jdk1.5.0_22/jre/lib/security/cacerts -alias myapp

如果使用多个Java版本,请确保Java--JRE--LIB--SECURITY--CACERTS文件正确

这将解决您的错误。

注意:

如果在提供正确的用户凭据后,浏览器显示空白页面,请修改系统的主机文件(Linux中的/etc/hosts和Windows中的c:\windows\system32\driver\etc\hosts)

在那里添加cas服务器IP

例如:

162.25.250.60 myapp 162.25.250.81 cas

任何疑问都可以澄清。

您可以参考以下内容:

https://wiki.jasig.org/display/CASUM/SSL+Troubleshooting+and+Reference+Guide


讲解得非常清晰明了。谢谢。 - Sandy
1
谢谢,虽然问题已经晚些时候得到了解答,但我认为这可能会帮助其他寻求相同答案的人。 - Imdad Areeph
嗨,Imdad,我也在JBoss上遇到了同样的问题,我尝试了很多方法,但都没有用。请查看此链接[1]: http://stackoverflow.com/questions/20369726/unable-to-find-valid-certification-path-jboss-5-1-0?noredirect=1#comment30411005_20369726 并帮助我解决这个问题... - user1441341
1
请检查新证书是否已添加到cacert。检查命令:C:\keytool -list -keystore "c:\Program Files\Java\jdk1.7.0_02\jre\lib\security\cacerts" -alias yourCertificateAliasHere如果您收到以下错误信息keytool error: java.lang.Exception: Alias <yourCertificateAliasHere> does not exist,则表示证书未添加到cacert。 - Imdad Areeph
另外,双击证书文件并转到第三个标签页“证书路径”。如果出现红色叉号并且证书状态显示为“此CA根证书不受信任,因为它不在受信任的根证书颁发机构存储中”。然后转到常规标签页,在受信任的根证书颁发机构下安装该证书。 - Imdad Areeph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接