生成证书及代码中有关密码的操作,请按照你们自己的需要修改成自己的
使用keytool生成证书
这个命令一般在JDKjrelibsecurity目录下操作
keytool常用命令
参数 | 释义 |
---|---|
-alias | 证书的别名 |
-keystore | 证书库的名称 |
-storepass | 证书库的密码 |
-keypass | 证书的密码 |
-list | 显示密钥库中的证书信息 |
-v | 显示密钥库中的证书详细信息 |
-export | 显示密钥库中的证书信息 |
-file | 指定导出证书的文件名和路径 |
-delete | 删除密钥库中某条目 |
-import | 将已签名数字证书导入密钥库 |
-keypasswd | 修改密钥库中指定条目口令 |
-dname | 指定证书拥有者信息 |
-keyalg | 指定密钥的算法 |
-validity | 指定创建的证书有效期多少天 |
-keysize | 指定密钥长度 |
具体生成证书操作
- 创建服务端秘钥
keytool -genkey -alias nettyServer -keysize 1024 -validity 3650 -keyalg RSA -dname "CN=localhost" -keypass 证书密码 -storepass 服务端的证书仓库密码 -keystore serverCerts.jks
- 导出服务端秘钥
keytool -export -alias nettyServer -keystore serverCerts.jks -storepass 服务端的证书仓库密码 -file serverCert.cer
- 创建客户端秘钥
keytool -genkey -alias nettyClient -keysize 1024 -validity 3650 -keyalg RSA -dname "CN=PF,OU=YJC,O=YJC,L=BJ,S=BJ,C=ZN" -keypass 证书密码 -storepass 客户端的证书仓库密码 -keystore clientCerts.jks
- 导出客户端秘钥
keytool -export -alias nettyClient -keystore clientCerts.jks -file nettyclientCert.cer -storepass 客户端的证书仓库密码
- 将客户端的证书导入到服务端的信任证书仓库中
keytool -import -trustcacerts -alias smcc -file nettyClientCert.cer -storepass 服务端的证书仓库密码 -keystore serverCerts.jks
- 将服务端的证书导入到客户端的信任证书仓库中
keytool -import -trustcacerts -alias smccClient -file serverCert.cer -storepass 客户端的证书仓库密码 -keystore clientCerts.jks
服务端创建ContextSSLFactory
package com.yjc.rpc.ssl;
import org.springframework.core.io.ClassPathResource;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import javax.net.ssl.*;
public class ContextSSLFactory {
private static final SSLContext SSL_CONTEXT_S ;
static{
SSLContext sslContextServer = null ;
try {
sslContextServer = SSLContext.getInstance("SSLv3") ;
} catch (NoSuchAlgorithmException e1) {
e1.printStackTrace();
}
try{
if(getKeyManagersServer() != null && getTrustManagersServer() != null ){
sslContextServer.init(getKeyManagersServer(), getTrustManagersServer(), null);
}
}catch(Exception e){
e.printStackTrace() ;
}
sslContextServer.createSSLEngine().getSupportedCipherSuites() ;
SSL_CONTEXT_S = sslContextServer ;
}
public ContextSSLFactory(){
}
public static SSLContext getSslContext(){
return SSL_CONTEXT_S ;
}
/**
* 获取服务端信任的证书
* @param: @return
* @return: TrustManager[]
* @throws
*/
private static TrustManager[] getTrustManagersServer(){
FileInputStream is = null ;
TrustManager[] trustManagersw=null;
TrustManagerFactory trustManagerFactory=null;
KeyStore ks=null;
try {
trustManagerFactory = TrustManagerFactory.getInstance("SunX509") ;
is =is =new FileInputStream( (new ClassPathResource("certs/serverCerts.jks")).getFile() );
String keyStorePass = "服务端的证书仓库密码" ;
ks=KeyStore.getInstance("JKS");
ks.load(is,keyStorePass.toCharArray());
trustManagerFactory.init(ks);
trustManagersw=trustManagerFactory.getTrustManagers();
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(is != null ){
try {
is.close() ;
} catch (IOException e) {
e.printStackTrace();
}
}
}
return trustManagersw;
}
/**
* 获取keymanager列表
* @param: @return
* @return: KeyManager[]
* @throws
*/
private static KeyManager[] getKeyManagersServer(){
FileInputStream is = null ;
KeyStore ks = null ;
KeyManagerFactory keyFac = null ;
KeyManager[] kms = null ;
try {
// 获得KeyManagerFactory对象. 初始化位默认算法
keyFac = KeyManagerFactory.getInstance("SunX509") ;
// String keyStorePath = PropertyUtil.getProperty("httpsKeyStorePath");
is =new FileInputStream( (new ClassPathResource("certs/serverCerts.jks")).getFile() );
ks = KeyStore.getInstance("JKS") ;
String keyStorePass = "服务端的证书仓库密码";
ks.load(is , keyStorePass.toCharArray()) ;
keyFac.init(ks, keyStorePass.toCharArray()) ;
kms = keyFac.getKeyManagers() ;
} catch (Exception e) {
e.printStackTrace();
}
finally{
if(is != null ){
try {
is.close() ;
} catch (IOException e) {
e.printStackTrace();
}
}
}
return kms ;
}
}
在pipeline中添加netty自带的sslHandler
这里需要注意的是 SslHandler需要添加到pipeline的最前面,否则即使加上了,不合法的客户端一样可以正常连接
try {
//设置事件处理
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 添加心跳支持
pipeline.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
// 基于定长的方式解决粘包/拆包问题
// pipeline.addLast(new LengthFieldBasedFrameDecoder(nettyConfig.getMaxFrameLength()
// , 0, 2, 0, 2));
// pipeline.addLast(new LengthFieldPrepender(2));
// 序列化
// pipeline.addLast(new MessagePackDecoder());
// pipeline.addLast(new MessagePackEncoder());
pipeline.addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
pipeline.addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
pipeline.addLast(channelHandlerAdapter);
SSLEngine engine = ContextSSLFactory.getSslContext().createSSLEngine();
engine.setUseClientMode(false); //设置为服务端模式
engine.setNeedClientAuth(true); //需要验证客户端
pipeline.addFirst("ssl", new SslHandler(engine)); //这个handler需要加到最前面
}
});
LOGGER.info("netty服务器在[{}]端口启动监听", port);
ChannelFuture f = serverBootstrap.bind(port).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
LOGGER.info("[出现异常] 释放资源");
boss.shutdownGracefully();
work.shutdownGracefully();
}
客户端代码基本是类似的,这里就不贴了。