`

安全网络通信——SSL.

阅读更多

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

    这里,我们将证书导入到我们的密钥库。

    Shell代码

    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

    控制台输出:

    Console代码

 输入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.将如下内容加入配置文件

    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的访问是不是需要把浏览器的这些个功能都实现呢?不需要!

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

    Java代码

 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.

给出相应测试类:

    Java代码

 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访问没有差别,我们来看控制台输出:

    Console代码

 <!--
  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双向认证!

分享到:
评论

相关推荐

    HTTPS:网络安全攻坚战(csdn)————程序.pdf

    《HTTPS:网络安全攻坚战》这篇文稿深入探讨了互联网安全的核心技术——HTTPS协议,它是HTTP的扩展,用于确保网络通信的安全性。HTTPS通过在HTTP的基础上加入SSL或TLS安全传输层,实现了对敏感信息的加密传输,如...

    Web安全技术——HTTPS.pdf

    HTTPS,全称HyperText Transfer Protocol Secure,是基于HTTP的安全协议,通过使用SSL/TLS协议来加密数据传输,确保网络通信的安全性。HTTPS的应用广泛,尤其是在互联网领域,如搜索引擎、电子商务网站、社交媒体...

    秋招面经和总结(csdn)————程序.pdf

    16. **HTTPS SSL握手过程**:包含客户端hello、服务器hello、证书交换、密钥交换、确认握手等步骤,保证通信安全。 17. **数据结构优化**:如使用HashSet优化查询性能,利用索引优化数据库查询,使用并发容器提高多...

    安卓Android源码——ScrambledNet.zip

    2. **安全套接层(SSL/TLS)**:作为网络通信安全的基石,SSL/TLS协议用于建立安全的网络连接。ScrambledNet可能在源码中实现了这些协议,确保数据在互联网上传输时的完整性与隐私性。 3. **网络编程接口(NET API)**...

    安卓Android源码——KeyChain.rar

    在安卓开发中,KeyChain是一个重要的系统服务,它负责管理设备的安全证书,如用户用于安全网络通信的SSL/TLS证书。Android源码中的KeyChain模块,是Android系统中用于安全身份验证的核心组件,它允许应用程序请求...

    linux布署war环境处理(csdn)————程序.pdf

    在Linux环境中部署WAR应用,通常涉及的关键步骤包括安装Java开发工具包(JDK)、安装Tomcat服务器以及配置网络服务。以下是对这些知识点的详细...同时,确保服务器的安全性,例如通过限制远程访问、使用SSL加密通信等。

    openssl-1.0.2e.tar.gz——1.rar

    - **SSL/TLS协议**:用于加密网络通信,确保数据在传输过程中的安全性。 - **加密算法**:如RSA、AES、DES等,用于数据加密和解密。 - **证书管理**:创建、管理和验证X.509数字证书,用于身份验证。 - **命令行工具...

    python smtp给多人发送邮件(csdn)————程序.pdf

    Python SMTP 是一个用于发送电子邮件的标准库,通过 Simple Mail Transfer Protocol (SMTP) 协议与邮件服务器进行通信。在这个示例中,我们看到如何使用 Python 的 `smtplib` 库来给多人发送邮件,主要涉及以下几个...

    [HTTP] 学习HTTP——HTTPS.doc

    SSL最初由网景公司开发,后来演进为TLS,成为IETF的标准,用于确保网络通信的安全。SSL/TLS可以理解为一种加密方式,它位于HTTP协议与TCP/IP协议之间,使得HTTP通信首先通过SSL/TLS层进行加密,再通过TCP传输。 **...

    安卓Android源码——http.rar

    `HttpURLConnection`是Java标准库的一部分,提供了更现代、更高效的方式进行网络通信。而`HttpClient`虽然功能强大,但在Android 6.0(API级别23)后被官方标记为弃用,推荐使用`HttpURLConnection`。 2. **...

    经典的Android项目——RemoteDroidServer.zip

    通过研究RemoteDroidServer项目,开发者不仅可以学习到Android应用的基本开发,还能深入理解网络通信、多线程处理、服务管理以及安全性等高级主题,这些知识对于进阶Android开发大有裨益。同时,这个项目也为自定义...

    ActiveMQ 入门实战(3)--SpringBoot 整合 ActiveMQ(csdn)————程序.pdf

    在生产环境中,确保 ActiveMQ 的安全性至关重要,这包括设置访问控制、启用 SSL/TLS 加密以及监控 ActiveMQ 的性能和健康状态。 总结,本篇文章介绍了如何在 SpringBoot 项目中集成 ActiveMQ,通过引入依赖、配置...

    安卓Android源码——GetPostSample.rar

    8. **HTTPS安全通信**: - 对于涉及用户隐私或敏感信息的请求,应使用HTTPS确保数据传输的安全性。 - 配置HTTPS涉及到SSL证书、TrustManager和HostnameVerifier的设置。 9. **缓存策略**: - 为了提高用户体验和...

    IOS应用源码——UseNetWork.rar

    在iOS应用开发中,网络通信是至关重要的一个环节,它使得应用能够获取远程数据、更新内容、实现在线交互等功能。这个名为"UseNetWork"的压缩包文件包含了一个iOS应用的源码,专注于网络功能的实现。通过分析和学习这...

    安卓Android源码——TestAirPlus.zip

    - **网络通信**:可能涉及到网络请求,如使用HttpURLConnection、OkHttp或Retrofit进行数据交换。 - **UI设计和布局**:通过XML布局文件理解界面设计,学习如何动态改变UI元素。 此外,TestAirPlus可能还包含对...

    apache_2.0.55-win32-x86-no_ssl.rar

    这个"apache_2.0.55-win32-x86-no_ssl.rar"压缩包包含的是Apache服务器的一个特定版本——2.0.55,针对Windows 32位操作系统,并且不包含SSL支持。下面我们将详细探讨Apache HTTP Server的基础知识、这个版本的特点...

    2. 从 Apache APISIX 来看 API 网关的演进——王院生.pdf

    Apache APISIX 是一款云原生的微服务 API 网关,它的设计与实现基于 Nginx 和 etcd,提供了高效、灵活的插件机制,使得动态路由、动态上游、动态 SSL 证书以及插件热加载等功能得以实现。自2020年6月6日开源以来,...

    用MFC实现局域网内点对点的大文件传输——说明.rar_MFC_mfc文件_mfc文件传输_文件_文件传输

    在实际应用中,我们还需要考虑到安全因素,如使用SSL/TLS加密数据流,防止数据被窃取或篡改。此外,为了优化性能,可能需要实现多线程或异步传输,以便在传输文件的同时,用户仍能执行其他操作。 总结起来,用MFC...

    PKI_SET_SSL.rar_PKI_ssl

    标题"PKI_SET_SSL.rar_PKI_ssl"暗示了主题是关于公钥基础设施(Public Key Infrastructure, PKI)和安全套接层(Secure Socket Layer, SSL)。这里的".rar"是一个文件压缩格式,表明这些资料是打包在一起的。描述...

    Apache ——SSL服务器证书视频部署操作指南

    SSL(Secure Sockets Layer)或其更新版本TLS(Transport Layer Security)是网络安全的重要组成部分,用于加密网络通信,确保数据在传输过程中的安全。SSL证书是建立安全HTTPS连接的关键,它们验证服务器的身份,并...

Global site tag (gtag.js) - Google Analytics