No subject alternative names那些事

最近做了一个项目,需要java调用WCF服务,其中为了安全性,使用了https的basic身份认证来做这件事情

而关键的WCF配置如下所示:

      <basicHttpBinding>
        <binding name="test1">
          <!--当前绑定的安全认证模式-->
          <security mode="Transport" >
            <!--定义消息级安全性要求的类型,为证书-->
            <transport clientCredentialType="Basic"/>
            <!--<message clientCredentialType="UserName" />-->
          </security>
        </binding>

         <serviceBehaviors>
            <behavior name="httpsBasicBindings">
          <!--makecert -sr LocalMachine -ss My -n CN=ejiyuan -sky exchange -pe -r-->
          <serviceCredentials >
            <!--指定一个 X.509 证书,用户对认证中的用户名密码加密解密-->
            <serviceCertificate  x509FindType="FindByThumbprint" findValue="6404232d69dbb8eb85ca4dca6fd44cde345d5731" storeLocation="LocalMachine" storeName="My"/>
            <clientCertificate>
              <!--自定义对客户端进行证书认证方式 这里为 None-->
              <authentication certificateValidationMode="None"/>
            </clientCertificate>
            <!--自定义用户名和密码验证的设置-->
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="<继承System.IdentityModel.Selectors.UserNamePasswordValidator的类名>" />
          </serviceCredentials>

其中FindByThumbprint表示按照证书的指纹(哈希值)查找证书storeLocation在本地计算机LocalMachine而不是本用户(LocalUser),

这里的证书只用于对用户名密码的加密而不会用于https连接,

在Win7等现代一点的Windows版本中,使用

netsh http sslcert ipport=0.0.0.0:9988 certhash=<证书指纹> appid=<应用的GUID>
来在本地计算机的个人证书目录中使用指定的证书作为https传输用服务器端证书,而查看本地计算机已经安装的证书可以通过mmc命令行打开的窗体的菜单->添加删除管理单元来查看和管理本计算机和本用户的证书

证书可以用sdk工具makecert生成,其中参数-n "CN=xxx.xxx.xxx“为此https服务的域名

然后使用apachede CXF生成java soap代码,为了避免证书信任问题,可以通过浏览器导出此https的证书,然后通过keytool工具导入jre/jdk的信任库:

%JAVA_HOME%\bin>keytool -import -file "浏览器导出的.cer文件全路径" -keystore "C:\Program %JAVA_HOME%/JRE/LIB/SECURITY/CACERTS"
 其中默认的信任库密码为changeit 

之后,只要CXF生成的soap代码是在此jdk、jre上运行,就不会报证书信任错误

或者,在程序运行之前,插入如下代码:

//先定义个trust定制类
static class miTM implements javax.net.ssl.TrustManager,
	 javax.net.ssl.X509TrustManager {
	 public java.security.cert.X509Certificate[] getAcceptedIssuers() {
	 return null;
	 }

	 public boolean isServerTrusted(
	 java.security.cert.X509Certificate[] certs) {
	 return true;
	 }

	 public boolean isClientTrusted(
	 java.security.cert.X509Certificate[] certs) {
	 return true;
	 }

	 public void checkServerTrusted(
	 java.security.cert.X509Certificate[] certs, String authType,SSLEngine e)
	 throws java.security.cert.CertificateException {
	 return;
	 }

	 public void checkClientTrusted(
	 java.security.cert.X509Certificate[] certs, String authType)
	 throws java.security.cert.CertificateException {
	 return;
	 }

	public void checkServerTrusted(X509Certificate[] arg0, String arg1)
			throws CertificateException {
		return;
		
	}
}

//在soap代码运行之前执行如下代码
javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
			 javax.net.ssl.TrustManager tm = new miTM();
			 trustAllCerts[0] = tm;
			 javax.net.ssl.SSLContext sc;
			try {
				sc = javax.net.ssl.SSLContext
				 .getInstance("SSL");
				sc.init(null, trustAllCerts, null);
				 javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc
						 .getSocketFactory());
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

这样,证书信任问题就解决了。


本来到这里就完了,接下来,闲着无聊,把soap的地址改成ip地址,运行代码,于是出现了那个著名的No subject alternative names错误

Got java.security.cert.CertificateException: No subject alternative names matching IP address xx.x.xxx.xxx found while opening stream from https://xx.x.xxx.xxx :9988/?wsdl.
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.parseWSDL(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(Unknown Source)
at com.sun.xml.internal.ws.spi.ProviderImpl.createServiceDelegate(Unknown Source)
at javax.xml.ws.Service.<init>(Unknown Source)
at Test.SqlServerService.<init>(SqlServerService.java:47)
at Test.Test.main(Test.java:148)
Caused by: java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names matching IP address xx.x.xxx.xxx  found while opening stream from https://10.0.194.206:9988/?wsdl
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.createReader(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.resolveWSDL(Unknown Source)
... 9 more
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names matching IP address xx.x.xxx.xxx  found
at sun.security.ssl.Alerts.getSSLException(Unknown Source)
at sun.security.ssl.SSLSocketImpl.fatal(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)
at sun.security.ssl.Handshaker.fatalSE(Unknown Source)

...

...

于是上网搜索,发现可以通过绕过java的hostvalidator来解决这个问题,同样在程序运行之前,执行如下代码:

		   javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier()  
	        {        



				public boolean verify(String hostname,
						SSLSession sslsession) {
					return true;
				}  
	        });

上面的方法简单直接,粗暴,也能解决问题,但是人家说了,这只能在开发的时候使用,正式环境上不推荐,


于是就研究在正式环境上,如何在https服务上使用ip地址而不是使用域名访问服务的方法

首先这个问题是由于在证书中没有【IP Address】这个扩展属性引起的,而微软的makecert多地址解决方案与java的不同,微软是通过多CN用逗号隔开的方式来支持多地址的,而sun却是通过使用x509证书v3中的扩展属性来指明证书的多地址的,找了好半天,也没有发现怎样通过makecert来生成x509证书的扩展属性,于是只能求助于openssl.


首先安装windows的openssl,我安装的是win32的,另外还需要用到C++ 2008再发布包在安装openssl的时候会出来这个提示,接着需要修改openssl的openssl.cfg文件

如有人写的这样,不过这位兄弟增加的是DNS,而这里需要增加DNS(域名)和IP(服务IP):

http://colinzhouyj.blog.51cto.com/2265679/1566438

文章中修改方法

[ alt_names ]
DNS.1 = abc.example.com
DNS.2 = dfe.example.org
DNS.3 = ex.abcexpale.net

需要改成

[ alt_names ]
IP.1 = xx.xxx.xxx.xxx
DNS.1 = xxx.xxx.com

其中DNS.1为机器名(如果没有域,也不会包含.com,只有单独机器名)


 

然后照着文章那样生成ca.crt,server.key,server.crt,同时,可以通过window证书管理器把ca.crt导入到计算机的[受信任的根证书颁发机构]

此时点击server.crt查看,可以看到


此时,证书的扩展属性有了[Ip Address]这项,

接下来,如果把这证书导入本地计算机,然后用netsh绑定到某个端口,就一定会出现如下错误:

未能添加 SSL 证书,错误: 1312
指定的登录会话不存在。可能已被终止。

 

如果发生这种错误,还可能是生成证书的电脑与要安装此证书的电脑不是一台电脑


同时如果在证书管理器中查看此证书,会发现少了红框中这一块:



只有把这个server.key和server.crt结合生成.p12个人证书:

openssl  pkcs12 -export -inkeyserver.key -in server.crt -out  server.p12

然后把这个server.p12证书通过mmc导入到系统中。

而之前通过 certutil -store My 查看证书,可以发现导入的证书有如下的消息:



而导入了p12个人证书之后:




此时使用IE通过IP浏览此https站点:


而用chrome:


可以看出IE是不认识证书中Ip Address这个扩展名的,而chrome却认识

但不管怎么说,现在在java soap程序中,把那个hostvalidator自定义验证去掉,

则程序直接能运行成功了,而之前,却是报No subject alternative names错误的



另外,如果在生成证书的时候报”

the xxxName field needed to be the same

则可以参考http://xiuxian1.iteye.com/blog/719668说的把cfg文件中的policy_match相关字段改成optional

如果报”TXT_DB error number 2“,则可以找到demoCA文件夹下的index.txt,把他删掉,然后把index.txt.old拷贝一份其中一个重命名为index.txt

则证书数据库恢复成初始安装状态


  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CertificateException: No subject alternative names present是一个SSL握手过程中可能出现的异常。它表示在证书中没有找到主体备用名称(Subject Alternative Names)。主体备用名称是用于指定与证书关联的主体的其他名称,例如域名或IP地址。 这个异常通常发生在以下情况下: 1. 证书中没有包含主体备用名称。 2. 证书中的主体备用名称与请求的主机名不匹配。 要解决这个问题,可以尝试以下方法: 1. 确保证书中包含了正确的主体备用名称。可以使用openssl命令检查证书的主体备用名称: ```shell openssl x509 -in <证书文件> -text -noout ``` 在输出中查找"Subject Alternative Name"字段,确保其中包含了正确的主体备用名称。 2. 如果证书中没有包含主体备用名称,可以尝试使用通配符证书或使用IP地址作为主体备用名称。 3. 如果证书中的主体备用名称与请求的主机名不匹配,可以尝试使用正确的主机名进行请求,或者更新证书以包含正确的主体备用名称。 4. 如果是在开发或测试环境中遇到这个问题,可以尝试忽略主体备用名称的验证。在Java中,可以通过设置SSLParameters的setEndpointIdentificationAlgorithm方法来实现: ```java SSLParameters sslParams = new SSLParameters(); sslParams.setEndpointIdentificationAlgorithm(""); SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(host, port); sslSocket.setSSLParameters(sslParams); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值