细说Keytool与tomcat配置https、netty ssl

2017-01-12 09:52:50来源:oschina作者:一只小桃子人点击

因为ios最近要求app接口必须是https才能审核上架,给组里的同志们分享了一下https配置。讲起来简单,做起来很蛋疼。当时讲了两种方式,keytool+tomcat,或者openssl+nginx。大家毕竟是搞java的,普遍选择了前者,但仍然问题重重,主要是概念搞不清楚。具体ssl流程、对称非对称加密就不聊了。主要聊一下keytool这个工具以及配置流程。


一、keytool工具

keytool是jdk自带的,用来管理密钥和证书的工具集。他让java开发者可以管理证书、以及生成自己的证书、密钥。但是这个东西没有CA认证的功能,只能自签名。openssl就可以自己做CA认证。keytool的详细文档:http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html


-export, renamed to -exportcert
-genkey, renamed to -genkeypair
-import, renamed to -importcert

不得不说,其中有些命令在老版本jdk里是简写的,jdk会在将来的版本继续支持简写,但推荐用新命令。目前大家一搜一堆,有的是-genkey、有的是-genkeypair,让很多人迷惑,到底是什么鬼。其实这些命令等同。


二、核心概念

(一)KeyStore


A keystore is a storage facility for cryptographic keys and certificates. keyStore就是用来保存密钥和证书的文件。


keytool -genkeypair -dname "cn=Mark Jones, ou=JavaSoft, o=Sun, c=US"
-alias business -keypass kpi135 -keystore /working/mykeystore
-storepass ab987c -validity 180

上面命令生成了一对密钥,其中-keystore选项指定了keystore文件的路径。如果我们没有敲这个选项,则会在用户主目录下生成隐藏文件.keystore。linux下当然是/home/username,windows下是C:/Users/username。.开头的是隐藏文件,所以linux要 ls -a,Windows要显示隐藏文件才能看到。这个命令生成了一对公私密钥,一个自签名证书,将其放到了/working/mykeystore这个文件中。如果mykeystore不存在,会被创建。如果存在,则密钥和证书被添加进去。


keyStore里存两种数据。一是key entries,也就是私钥,这是很隐私的东西,最好有密码保护,所以我们也看到有两个密码,一个是操作keyStore的时候要用的-storepass,一个是操作私钥用的-keypass。第二种数据是trusted certificate entries ,也就是我已经信任的公钥或证书(证书包含公钥)。证书是可以被导入进keyStore的,比如双向认证,我需要把客户端的证书导入到我的keyStore中。又比单向认证,客户端要把服务端的证书导入到自己的keyStore里才可以调用(除非你trust any host)。


keyStore里存了这么多个证书或者私钥,怎么区分呢?每个证书或者私钥都有alias,上面的命令里指定了alias为business。alias应该是唯一不重复的。


java里如何使用keyStore呢?tomcat连接器上配置的就是这个。如果在代码里使用则


KeyStore keyStore = KeyStore.getInstance("JKS");

(二)证书 certificates


证书一般来讲是签过名的公钥,两个要点1.公钥,2.签名了。除了这些之外,还包括证书版本、证书发布者的信息等等。比如:


"cn=Mark Jones, ou=JavaSoft, o=Sun, c=US"

目前keytool的证书格式是按照X.509规范来的。体现在代码里就是


KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");

证书链:使用keytool生成密钥对的时候,生成了一个私钥,和与私钥对应的公钥,这个公钥被自签名成证书。运行了上面的第一条命令之后,我们的mykeystore里就包含了私钥和自签名证书。这个时候就可以配置到tomcat使用了,问题是证书没有CA认证,浏览器会报警告。所以我们把证书提交到CA机构做认证。他们认证之后,证书就不是一个单个的证书了,而是一个证书链。证书链的尾部是我们的自签名,往上是下一家签名机构,再往上再下一家,一直这么签下去。


三、tomcat单向配置流程
(第一步)生成一对密钥和对应的证书。
keytool -genkeypair -alias myTomcat -keypass myKeyPassword -keystore
D://myKeyStore -storepass myStorePass -validity 3650 -keyalg RSA

上面命令生成私钥+自签名证书。对应的keystore文件在D盘下。keystore的密码是"myStorePass",私钥的密码是“myKeyPassword”,有效期是10年。别名是myTomcat。输入命令后,信息要如实填写,尤其是省份城市国家,不然CA到时候签不了。填名字(CN)的时候注意,填自己的域名或者ip,我这里填写的localhost,如果这个不填或者乱填,之后会一堆毛病(现在IETF and CA/B Forums废弃掉了填域名的方式,CN应该填个随意的名字,可以使用-ext SAN=dns:xxxx,ip:xxx.xxx.xxx.xxx附加站点信息。老浏览器会警告证书域名和实际域名不一致。老实点填域名守旧一点不会错的)。如果不想挨个输入,可以变成字符串,作为-dname选项加到命令里,我的是。CN=域名,ou和o=组织名称,L=城市,ST=省,c=国家。省市和国家最好先问问CA机构,填的不对到时候没法认证。


-dname "CN=localhost, OU=bluemobi, O=bluemobi, L=WUHAN, ST=HUBEI, C=CN"

注意命令要在一行,这里为了显示换行了,直接复制粘贴后几个参数会被忽略。相信我,最好指定密钥生成算法,-keyalg,不指定默认使用的是DSA,对应签名算法是SHA1withDSA。本身用默认的也没有问题,但是实践过程中发现还是有点兼容问题,尤其是火狐浏览器等有的不太兼容,有可能导致无法握手。文件名没有.jks结尾,这个不要计较,文件名和文件内容没有什么关系。这个时候,配置到tomcat就可以了。打开8443端口,添加配置,启动Tomcat。其中keyPass不是必须的,如果你的keyPass和keyStorePass是一致的话。


maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keyPass="myKeyPassword" keyAlias="myTomcat"
keystoreFile="D://myKeyStore" keystorePass="myStorePass"/>

此时访问8080端口是http,访问https://localhost:8443是https。但是浏览器会报警告是否要信任证书,需要手动点确定。如果需要强制https访问,打开tomcat的web.xml,增加配置,根据url-pattern判断是否必须https。





SSL
/*


CONFIDENTIAL


这时候可以找ios和安卓联调了。如果他们必须要证书才能访问,怎么获取呢。首先,我们在自己浏览器上信任了自己的站点之后,证书就安装到了浏览器。直接到浏览器里,找到自己的证书,导出一个给前端就可以了。或者使用命令行导出


keytool -exportcert -keystore D://myKeyStore -alias myTomcat -file D://myTomcat.cer

前端拿到这个就可以导入到他们自己的库里。比如对方是android或者其他的java程序,他们


keytool -importcert -keystore androidJKS -alias myTomcat -file D://myTomcat.cer

导入到他们自己的keyStore里,他们就可以作为客户端访问我们了。


(第二步)生成证书请求,拿去认证
keytool -certreq -keystore D://myKeyStore -alias myTomcat -file D://request.csr

此命令为myTomcat生成一个证书请求,把这个文件提交给认证机构,他们给签名。


(第三步)信任CA机构的证书

我们需要把CA机构的证书导入自己的keyStore进来做为信任的证书。如果CA机构一层层是证书链,则每一个都要导入。这一步不做的话,一些比较严格的安全认证仍然走不通,比如ios


keytool -importcert -keystore D://myKeyStore -alias abc -file D://ABCCA.cer
(第四步)把CA机构的回执导入keyStore

CA机构认证后会返回我们一个证书,把他导进来,替换掉自己的自签名证书。比如人家返回给我们的是VSMarkJ.cer。 导入。


keytool -importcert -trustcacerts -keystore D://myKeyStore -file D://VSMarkJ.cer
四、netty单向配置。

假设我就用上面的myTomcat证书配置netty服务器。已聊天室为例,netty版本:4.1.6.Final


KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("D://myKeyStore"), "myStorePass".toCharArray());
KeyManagerFactory kms = KeyManagerFactory.getInstance("SunX509");
kms.init(ks, "myKeyPassword".toCharArray());
SslContext sslCtx = SslContextBuilder.forServer(kms).clientAuth(ClientAuth.NONE).build();
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup(1);
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChatServerInitializer(sslCtx));
b.bind("127.0.0.1", 8080).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}

然后给客户端也生成keyStore,并把服务端的证书导入进去


keytool -genkeypair -alias nettyClient -keypass 123456 -keystore D://nettyClient
-storepass 123456 -validity 180
-dname "CN=localhost, OU=bluemobi, O=bluemobi, L=WUHAN, ST=HUBEI, C=CN" -keyalg RSA
keytool -exportcert -keystore D://myKeyStore -alias myTomcat -file D://myTomcat.cer
keytool -importcert -keystore D://nettyClient -alias myTomcat -file D://myTomcat.cer

客户端需要信任服务端,所以加个trustManager


KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("D://nettyClient"), "123456".toCharArray());
KeyManagerFactory kms = KeyManagerFactory.getInstance("SunX509");
kms.init(ks, "123456".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
final SslContext sslCtx = SslContextBuilder.forClient().keyManager(kms).trustManager(tmf).build();
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(new ChatClientInitializer(sslCtx));
Channel ch = b.connect("127.0.0.1", 8080).sync().channel();
ChannelFuture lastWriteFuture = null;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
String line = in.readLine();
if (line == null) {
break;
}
lastWriteFuture = ch.writeAndFlush(line + "/r/n");
if ("bye".equals(line.toLowerCase())) {
ch.closeFuture().sync();
break;
}
}
if (lastWriteFuture != null) {
lastWriteFuture.sync();
}
} finally {
group.shutdownGracefully();
}

netty官方给的chatServer其实有点误导,


public void channelActive(ChannelHandlerContext ctx) throws Exception {
group.add(ctx.channel());
ctx.writeAndFlush("欢迎来到加密聊天室" + InetAddress.getLocalHost().getHostName());
ctx.writeAndFlush("Your session is protected by "
+ ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite()
+ " cipher suite./n");
}

在channelActive的时候,ssl还没建立呢!所以这里输出的是


欢迎来到加密聊天室
CNWHWANGWQD001Your session is protected by SSL_NULL_WITH_NULL_NULL cipher suite.

因为还没握手完,所以这个输出是ok的,并不是错误。在会话中的时候再输出CipherSuite,就会发现有值了。调试的时候可以在加个jvm参数看到握手过程-Djavax.net.debug=ssl,handshake

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台