原文地址

http://snowolf.iteye.com/blog/397693

我们需要构建一个由CA机构签发的有效证书,这里我们使用上文中生成的自签名证书zlex.cer 
    这里,我们将证书导入到我们的密钥库。

keytool -import -alias www.zlex.org -file d:/zlex.cer -keystore d:/zlex.keystore  

其中 
-import表示导入 
-alias指定别名,这里是www.zlex.org 
-file指定算法,这里是d:/zlex.cer 
-keystore指定存储位置,这里是d:/zlex.keystore 
在这里我使用的密码为654321

控制台输出:

输入keystore密码:
再次输入新密码:
所有者:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
签发人:CN=www.zlex.org, OU=zlex, O=zlex, L=BJ, ST=BJ, C=CN
序列号:4a1e48df
有效期: Thu May 28 16:18:39 CST 2009 至Wed Aug 26 16:18:39 CST 2009
证书指纹:
MD5:19:CA:E6:36:E2:DF:AD:96:31:97:2F:A9:AD:FC:37:6A
SHA1:49:88:30:59:29:45:F1:69:CA:97:A9:6D:8A:CF:08:D2:C3:D5:C0:C4
签名算法名称:SHA1withRSA
版本: 3
信任这个认证? [否]: y
认证已添加至keystore中

OK,最复杂的准备工作已经完成。 
接下来我们将域名www.zlex.org定位到本机上。打开C:\Windows\System32\drivers\etc\hosts文件,将www.zlex.org绑定在本机上。在文件末尾追加127.0.0.1       www.zlex.org。现在通过地址栏访问http://www.zlex.org,或者通过ping命令,如果能够定位到本机,域名映射就搞定了。 
现在,配置tomcat。先将zlex.keystore拷贝到tomcat的conf目录下,然后配置server.xml。将如下内容加入配置文件:

<Connector
SSLEnabled="true"
URIEncoding="UTF-8"
clientAuth="false"
keystoreFile="conf/zlex.keystore"
keystorePass="123456"
maxThreads="150"
port="443"
protocol="HTTP/1.1"
scheme="https"
secure="true"
sslProtocol="TLS" />

注意clientAuth="false"测试阶段,置为false,正式使用时建议使用true。现在启动tomcat,访问https://www.zlex.org/

显然,证书未能通过认证,这个时候你可以选择安装证书(上文中的zlex.cer文件就是证书),作为受信任的根证书颁发机构导入,再次重启浏览器(IE,其他浏览器对于域名www.zlex.org不支持本地方式访问),访问https://www.zlex.org/,你会看到地址栏中会有个小锁,就说明安装成功。所有的浏览器联网操作已经在RSA加密解密系统的保护之下了。但似乎我们感受不到。

这个时候很多人开始怀疑,如果我们要手工做一个这样的https的访问是不是需要把浏览器的这些个功能都实现呢?不需要!

接着上篇内容,给出如下代码实现:

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Date; import javax.crypto.Cipher;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory; /**
* 证书组件
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public abstract class CertificateCoder extends Coder { /**
* Java密钥库(Java Key Store,JKS)KEY_STORE
*/
public static final String KEY_STORE = "JKS"; public static final String X509 = "X.509";
public static final String SunX509 = "SunX509";
public static final String SSL = "SSL"; /**
* 由KeyStore获得私钥
*
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
private static PrivateKey getPrivateKey(String keyStorePath, String alias,
String password) throws Exception {
KeyStore ks = getKeyStore(keyStorePath, password);
PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());
return key;
} /**
* 由Certificate获得公钥
*
* @param certificatePath
* @return
* @throws Exception
*/
private static PublicKey getPublicKey(String certificatePath)
throws Exception {
Certificate certificate = getCertificate(certificatePath);
PublicKey key = certificate.getPublicKey();
return key;
} /**
* 获得Certificate
*
* @param certificatePath
* @return
* @throws Exception
*/
private static Certificate getCertificate(String certificatePath)
throws Exception {
CertificateFactory certificateFactory = CertificateFactory
.getInstance(X509);
FileInputStream in = new FileInputStream(certificatePath); Certificate certificate = certificateFactory.generateCertificate(in);
in.close(); return certificate;
} /**
* 获得Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
private static Certificate getCertificate(String keyStorePath,
String alias, String password) throws Exception {
KeyStore ks = getKeyStore(keyStorePath, password);
Certificate certificate = ks.getCertificate(alias); return certificate;
} /**
* 获得KeyStore
*
* @param keyStorePath
* @param password
* @return
* @throws Exception
*/
private static KeyStore getKeyStore(String keyStorePath, String password)
throws Exception {
FileInputStream is = new FileInputStream(keyStorePath);
KeyStore ks = KeyStore.getInstance(KEY_STORE);
ks.load(is, password.toCharArray());
is.close();
return ks;
} /**
* 私钥加密
*
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私钥
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password); // 对数据加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey); return cipher.doFinal(data); } /**
* 私钥解密
*
* @param data
* @param keyStorePath
* @param alias
* @param password
* @return
* @throws Exception
*/
public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,
String alias, String password) throws Exception {
// 取得私钥
PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password); // 对数据加密
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey); return cipher.doFinal(data); } /**
* 公钥加密
*
* @param data
* @param certificatePath
* @return
* @throws Exception
*/
public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
throws Exception { // 取得公钥
PublicKey publicKey = getPublicKey(certificatePath);
// 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey); return cipher.doFinal(data); } /**
* 公钥解密
*
* @param data
* @param certificatePath
* @return
* @throws Exception
*/
public static byte[] decryptByPublicKey(byte[] data, String certificatePath)
throws Exception {
// 取得公钥
PublicKey publicKey = getPublicKey(certificatePath); // 对数据加密
Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, publicKey); return cipher.doFinal(data); } /**
* 验证Certificate
*
* @param certificatePath
* @return
*/
public static boolean verifyCertificate(String certificatePath) {
return verifyCertificate(new Date(), certificatePath);
} /**
* 验证Certificate是否过期或无效
*
* @param date
* @param certificatePath
* @return
*/
public static boolean verifyCertificate(Date date, String certificatePath) {
boolean status = true;
try {
// 取得证书
Certificate certificate = getCertificate(certificatePath);
// 验证证书是否过期或无效
status = verifyCertificate(date, certificate);
} catch (Exception e) {
status = false;
}
return status;
} /**
* 验证证书是否过期或无效
*
* @param date
* @param certificate
* @return
*/
private static boolean verifyCertificate(Date date, Certificate certificate) {
boolean status = true;
try {
X509Certificate x509Certificate = (X509Certificate) certificate;
x509Certificate.checkValidity(date);
} catch (Exception e) {
status = false;
}
return status;
} /**
* 签名
*
* @param keyStorePath
* @param alias
* @param password
*
* @return
* @throws Exception
*/
public static String sign(byte[] sign, String keyStorePath, String alias,
String password) throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) getCertificate(
keyStorePath, alias, password);
// 获取私钥
KeyStore ks = getKeyStore(keyStorePath, password);
// 取得私钥
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password
.toCharArray()); // 构建签名
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
signature.initSign(privateKey);
signature.update(sign);
return encryptBASE64(signature.sign());
} /**
* 验证签名
*
* @param data
* @param sign
* @param certificatePath
* @return
* @throws Exception
*/
public static boolean verify(byte[] data, String sign,
String certificatePath) throws Exception {
// 获得证书
X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);
// 获得公钥
PublicKey publicKey = x509Certificate.getPublicKey();
// 构建签名
Signature signature = Signature.getInstance(x509Certificate
.getSigAlgName());
signature.initVerify(publicKey);
signature.update(data); return signature.verify(decryptBASE64(sign)); } /**
* 验证Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
*/
public static boolean verifyCertificate(Date date, String keyStorePath,
String alias, String password) {
boolean status = true;
try {
Certificate certificate = getCertificate(keyStorePath, alias,
password);
status = verifyCertificate(date, certificate);
} catch (Exception e) {
status = false;
}
return status;
} /**
* 验证Certificate
*
* @param keyStorePath
* @param alias
* @param password
* @return
*/
public static boolean verifyCertificate(String keyStorePath, String alias,
String password) {
return verifyCertificate(new Date(), keyStorePath, alias, password);
} /**
* 获得SSLSocektFactory
*
* @param password
* 密码
* @param keyStorePath
* 密钥库路径
*
* @param trustKeyStorePath
* 信任库路径
* @return
* @throws Exception
*/
private static SSLSocketFactory getSSLSocketFactory(String password,
String keyStorePath, String trustKeyStorePath) throws Exception {
// 初始化密钥库
KeyManagerFactory keyManagerFactory = KeyManagerFactory
.getInstance(SunX509);
KeyStore keyStore = getKeyStore(keyStorePath, password);
keyManagerFactory.init(keyStore, password.toCharArray()); // 初始化信任库
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(SunX509);
KeyStore trustkeyStore = getKeyStore(trustKeyStorePath, password);
trustManagerFactory.init(trustkeyStore); // 初始化SSL上下文
SSLContext ctx = SSLContext.getInstance(SSL);
ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory
.getTrustManagers(), null);
SSLSocketFactory sf = ctx.getSocketFactory(); return sf;
} /**
* 为HttpsURLConnection配置SSLSocketFactory
*
* @param conn
* HttpsURLConnection
* @param password
* 密码
* @param keyStorePath
* 密钥库路径
*
* @param trustKeyStorePath
* 信任库路径
* @throws Exception
*/
public static void configSSLSocketFactory(HttpsURLConnection conn,
String password, String keyStorePath, String trustKeyStorePath)
throws Exception {
conn.setSSLSocketFactory(getSSLSocketFactory(password, keyStorePath,
trustKeyStorePath));
}
}

增加了configSSLSocketFactory方法供外界调用,该方法为HttpsURLConnection配置了SSLSocketFactory。当HttpsURLConnection配置了SSLSocketFactory后,我们就可以通过HttpsURLConnection的getInputStream、getOutputStream,像往常使用HttpURLConnection做操作了。尤其要说明一点,未配置SSLSocketFactory前,HttpsURLConnection的getContentLength()获得值永远都是-1

给出相应测试类:

import static org.junit.Assert.*;  

import java.io.DataInputStream;
import java.io.InputStream;
import java.net.URL; import javax.net.ssl.HttpsURLConnection; import org.junit.Test; /**
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public class CertificateCoderTest {
private String password = "123456";
private String alias = "www.zlex.org";
private String certificatePath = "d:/zlex.cer";
private String keyStorePath = "d:/zlex.keystore";
private String clientKeyStorePath = "d:/zlex-client.keystore";
private String clientPassword = "654321"; @Test
public void test() throws Exception {
System.err.println("公钥加密——私钥解密");
String inputStr = "Ceritifcate";
byte[] data = inputStr.getBytes(); byte[] encrypt = CertificateCoder.encryptByPublicKey(data,
certificatePath); byte[] decrypt = CertificateCoder.decryptByPrivateKey(encrypt,
keyStorePath, alias, password);
String outputStr = new String(decrypt); System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr); // 验证数据一致
assertArrayEquals(data, decrypt); // 验证证书有效
assertTrue(CertificateCoder.verifyCertificate(certificatePath)); } @Test
public void testSign() throws Exception {
System.err.println("私钥加密——公钥解密"); String inputStr = "sign";
byte[] data = inputStr.getBytes(); byte[] encodedData = CertificateCoder.encryptByPrivateKey(data,
keyStorePath, alias, password); byte[] decodedData = CertificateCoder.decryptByPublicKey(encodedData,
certificatePath); String outputStr = new String(decodedData);
System.err.println("加密前: " + inputStr + "\n\r" + "解密后: " + outputStr);
assertEquals(inputStr, outputStr); System.err.println("私钥签名——公钥验证签名");
// 产生签名
String sign = CertificateCoder.sign(encodedData, keyStorePath, alias,
password);
System.err.println("签名:\r" + sign); // 验证签名
boolean status = CertificateCoder.verify(encodedData, sign,
certificatePath);
System.err.println("状态:\r" + status);
assertTrue(status); } @Test
public void testHttps() throws Exception {
URL url = new URL("https://www.zlex.org/examples/");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); conn.setDoInput(true);
conn.setDoOutput(true); CertificateCoder.configSSLSocketFactory(conn, clientPassword,
clientKeyStorePath, clientKeyStorePath); InputStream is = conn.getInputStream(); int length = conn.getContentLength(); DataInputStream dis = new DataInputStream(is);
byte[] data = new byte[length];
dis.readFully(data); dis.close();
System.err.println(new String(data));
conn.disconnect();
}
}

注意testHttps方法,几乎和我们往常做HTTP访问没有差别,我们来看控制台输出:

<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD><TITLE>Apache Tomcat Examples</TITLE>
<META http-equiv=Content-Type content="text/html">
</HEAD>
<BODY>
<P>
<H3>Apache Tomcat Examples</H3>
<P></P>
<ul>
<li><a href="servlets">Servlets examples</a></li>
<li><a href="jsp">JSP Examples</a></li>
</ul>
</BODY></HTML>

通过浏览器直接访问https://www.zlex.org/examples/你也会获得上述内容。也就是说应用甲方作为服务器构建tomcat服务,乙方可以通过上述方式访问甲方受保护的SSL应用,并且不需要考虑具体的加密解密问题。甲乙双方可以经过相应配置,通过双方的tomcat配置有效的SSL服务,简化上述代码实现,完全通过证书配置完成SSL双向认证!

来自:http://www.cnblogs.com/bjlhx/p/6565699.html

java-信息安全(十四)-初探SSL的更多相关文章

  1. “全栈2019”Java第九十四章:局部内部类详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  2. “全栈2019”Java第十四章:二进制、八进制、十六进制

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  3. “全栈2019”Java第二十四章:流程控制语句中决策语句switch下篇

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. 菜鸟学Java(十四)——Java反射机制(一)

    说到反射,相信有过编程经验的人都不会陌生.反射机制让Java变得更加的灵活.反射机制在Java的众多特性中是非常重要的一个.下面就让我们一点一点了解它是怎么一回事. 什么是反射 在运行状态中,对于任意 ...

  5. 《Thinking in Java》十四章类型信息_习题解

    1~10    Page 318 练习1. 在ToyTest.java中,将Toy的默认构造器注释掉,并解释发生的现象. 书中代码如下(略有改动): package org.cc.foo_008; p ...

  6. Java笔记(十四)……抽象类与接口

    抽象类概念 抽象定义: 抽象就是从多个事物中将共性的,本质的内容抽取出来. 例如:狼和狗共性都是犬科,犬科就是抽象出来的概念. 抽象类: Java中可以定义没有方法体的方法,该方法的具体实现由子类完成 ...

  7. Java Web(十四) 编写MyBookStore项目的总结

    这几天一直没有发博文,原因是在写一个书城的小项目,作为web学习的最后沉淀,接下来就要到框架的学习了. --WH 一.项目介绍 从网上找的一个培训机构的小项目,名称叫做 书城购物网站 吧,其中就是分前 ...

  8. JAVA提高十四:HashSet深入分析

    前面我们介绍了HashMap,Hashtable,那么还有一个hash家族,那就是HashSet;在讲解HashSet前,大家先要知道的是HashSet是单值集合的接口,即是Collection下面的 ...

  9. Java进阶(十四)实现每天定时对数据库的操作

    Java实现每天定时对数据库操作 现在有一个很棘手的问题:客户要求实现一个功能,就是每日凌晨自动计算慢性病订单是否有需要在今日提醒的,如果有则生成一条提醒记录到lm_notice之中. 如何在Web工 ...

  10. java基础(十四)-----详解匿名内部类——Java高级开发必须懂的

    在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.匿名内部类使用的形参为何要为final. 使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式如下: n ...

随机推荐

  1. BZOJ.3585.mex(线段树)

    题目链接 题意:多次求区间\(mex\). 考虑\([1,i]\)的\(mex[i]\),显然是单调的 而对于\([l,r]\)与\([l+1,r]\),如果\(nxt[a[l]]>r\),那么 ...

  2. 面向对象&网络编程

    1 接口与归一化设计 1.1 归一化概念: 归一化的好处: 1.归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大降低了使用者的使用难度. 2.归一化使得高层的外部 ...

  3. PID控制器(比例-积分-微分控制器)- IV

    调节/测量放大电路电路图:PID控制电路图 如图是PlD控制电路,即比例(P).积分(I).微分(D)控制电路. A1构成的比例电路与环路增益有关,调节RP1,可使反相器的增益在0·5一∞范围内变化; ...

  4. CentOS禁止packagekit离线更新服务的办法

    以CentOs7为例,以root身份登录系统,然后执行以下命令: [root@localhost ~]# systemctl disable packagekit-offline-update.ser ...

  5. java监控指定路径下文件及文件夹变化

    之前用jdk7的WatchService API(java.nio.file包)来做目录下的子文件监控,后改为使用commons-io包.主要有下面几点不同:1. WatchService是采用扫描式 ...

  6. Asp.Net Core 自定义设置Http缓存处理

    一.使用中间件 拦截请求自定义输出文件 输出前自定义指定响应头 public class OuterImgMiddleware { public static string RootPath { ge ...

  7. JAVA中通过JavaCV实现跨平台视频/图像处理-调用摄像头

    一.简介 JavaCV使用来自计算机视觉领域(OpenCV, FFmpeg, libdc1394, PGR FlyCapture, OpenKinect, librealsense, CL PS3 E ...

  8. 【PMP】项目的定义和特点

    1.定义 项目是为创建独特的产品.服务和成果而进行的的临时性工作. 2.特点 2.1 独特的产品.服务或成果 实现项目目标可能产生一个或多个可交付成果.例如:即便采用相同的材料或者相同的施工单位来建设 ...

  9. vbox磁盘空间如何扩容

    vbox磁盘空间如何扩容   为虚拟机硬盘扩容(Oracle VM VirtualBox) VBoxManage modifyhd         <uuid>|<filename& ...

  10. 11G新特性 -- variable size extents

    AU是asm磁盘分配的基本单元.在oracle10g中,一个AU对应一个extent(这会增加对内存的使用),因为一个大的数据库如果含有大量的默认大小的AU,会导致数据库的share pool的大量使 ...