微信号:importnew

介绍:伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等.

Java 结合 keytool 实现非对称加密和解密

2018-08-09 11:30 ImportNew

(点击上方公众号,可快速关注)


来源:王洁 ,

imaidata.github.io/blog/2017/08/08/java结合keytool实现非对称加密和解密/


那一篇讲签名,这一篇将加密解密。在Java安全体系中,签名属于JAAS模块,加解密属于JCE模块。


keytool的使用


keytool是JDK自带的一个密钥库管理工具。这里只用到了keytool的部分功能,包括生成密钥对,导出公钥等。keytool生成的公钥/私钥对存放到一个到了一个文件中,这个文件有密码保护,通称为keystore。


生成密钥对


$ keytool -genkey -alias signLegal -keystore examplestanstore2 -validity 1800 -keyalg RSA


生成别名为signLegal的密钥对,存放在密钥库examplestanstore2中,证书的有效期是1800天(默认是90天)。


输入一系列的参数。输入的参数遵循了LDAP的风格和标准。可以想象,生成的密钥对可以看成LDAP的一个条目。


命令执行成功后会在当前目录下创建一个叫examplestanstore2的文件。相对另一篇博文,增加了一个keyalg参数。因为keytool默认算法是DSA,而DSA只能用于签名。RSA既能用于签名,也能用于加密。而本文是研究加密问题,只能用RSA算法。


查看密钥对


$ keytool -list -keystore examplestanstore2 -v


列出了examplestanstore2密钥库的中所有密钥对。-v参数表示详细信息,详细信息中有证书的失效时间。


导出公钥证书


$ keytool -export -keystore examplestanstore2 -alias signLegal -file StanSmith.crt -rfc


导出的公钥存放在当前目录的StanSmith.crt文件中。讲“签名”的那篇博文没有加-rfc参数,导出是个二进制文件(CER格式)。加上-rfc后,导出的是文本文件(PEM)格式。在下面的测试中,如果使用CER格式,会报错 ` No installed provider supports this key: sun.security.provider.DSAPublicKeyImpl`。


Java加密和解密


在Java程序中,首先从密钥库取出私钥和公钥,然后对测试字符串进行加密。二进制的密文转换成字符串输出到屏幕,然后解密成明文再输出到屏幕。


GenSig2.java


import java.io.*;

import java.security.KeyStore;

import java.security.PrivateKey;

import java.security.PublicKey;

import javax.crypto.Cipher;

import sun.security.provider.*;

 

public class RSAEntry {

    public static void main(String[] args) {

        try {

        //1.从密钥库中取私钥

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

        FileInputStream ksfis = new FileInputStream("examplestanstore2");

        BufferedInputStream ksbufin = new BufferedInputStream(ksfis);

 

        // open keystore and get private key

        // alias is 'signLeal', kpasswd/spasswd is 'vagrant'

        ks.load(ksbufin, "vagrant".toCharArray());

        PrivateKey prikey = (PrivateKey) ks.getKey("signLegal", "vagrant".toCharArray());

 

        //2.根据命令行参数取公钥

        FileInputStream certfis = new FileInputStream(args[0]);

        java.security.cert.CertificateFactory cf =

            java.security.cert.CertificateFactory.getInstance("X.509");

        java.security.cert.Certificate cert =  cf.generateCertificate(certfis);

        PublicKey pubKey = cert.getPublicKey();

 

        //3.使用公钥进行加密

        String data = "测试数据";

        //构建加密解密类

        Cipher cipher = Cipher.getInstance("RSA");

        cipher.init(Cipher.ENCRYPT_MODE, pubKey);//设置为加密模式

        byte[] jmdata = cipher.doFinal(data.getBytes());

        //打印加密后数据

        System.out.println(bytesToHexString(jmdata));

        //改为解密模式进行解密

        cipher.init(Cipher.DECRYPT_MODE, prikey);//会用私钥解密

        jmdata = cipher.doFinal(jmdata);

        System.out.println(new String(jmdata));

        }catch (Exception e) {

                e.printStackTrace();

        }

    }

    //这个方法用于把二进制转换成ASCII字符串。

    public static String bytesToHexString(byte[] bytes) {

        if (bytes == null)

            return "null!";

        int len = bytes.length;

        StringBuilder ret = new StringBuilder(2 * len);

 

        for (int i = 0; i < len; ++i) {

            int b = 0xF & bytes[(i)] >> 4;

            ret.append("0123456789abcdef".charAt(b));

            b = 0xF & bytes[(i)];

            ret.append("0123456789abcdef".charAt(b));

        }

 

        return ret.toString();

    }

}


编译,并运行


$ javac RSAEntry.java

$ java RSAEntry StanSmith.crt

8fceea48e34fdc786bde05459f3366714b650ff04f4e81e52eca139d8ee0b4acbcad019cd496de3589765894b2d5f4a2af38914af614d9e9b73e551ae01830cd6f49505685d7e527e3adc2b7a2a75608068627c0a12b338d3c743a5de2af2de327a0de14b548604e5c8905747aef077852ecfd2eb4a134ca0f3a56b23db8ae4beb07add5ba3725ab3ee0ffa7481494856144ba5004a329cfe2c43078f0cd95aebcbbfc6c1894efafacac90615e549cb8432c125d912a5e54ce4884f633f3e96bd7b61c1d538e38713716367f7ec6f5ca01288e6d96ad9e3d6515147369144390e1d002b1beaf5797966e3b498cc7def754816c99456ef380b3a83366a44415f6

测试数据


本文展示的算法是一种非对称算法,计算较慢。在SSL中,非对称算法用于客户端和服务器之间交换对称加密的一次性密钥。客户端将一个随机数用服务器的公钥加密发给服务器,如果服务器持有私钥,就能解开密文获得随机数(这个随机数就是对称算法的密钥)。有了对称算法密钥,双方就可以用对称加密进行安全通信了。


参考


  • Java结合keytool实现非对称签名与验证

    https://imaidata.github.io/blog/2017/07/29/java结合keytool实现非对称签名与验证/

  • Java-web下使用RSA进行加密解密操作

    http://blog.csdn.net/niemingming/article/details/10082975


【关于投稿】


如果大家有原创好文投稿,请直接给公号发送留言。


① 留言格式:
【投稿】+《 文章标题》+ 文章链接

② 示例:
【投稿】《不要自称是程序员,我十多年的 IT 职场总结》:http://blog.jobbole.com/94148/

③ 最后请附上您的个人简介哈~



看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

 
ImportNew 更多文章 JDK源码阅读:InterruptibleChannel 与可中断 IO 机器学习模型,能分清川菜和湘菜吗? 深入理解单例模式 ( 上 ) JVM 面试题 使用 SpringAOP 获取一次请求流经方法的调用次数和调用耗时
猜您喜欢 Android 网络请求库的比较及实战(一) 8 月 29 日创业明星 & 技术大咖齐聚上海是为了什么 知乎爆料美团亏损严重 疑融资受阻 10个免费创建动画加载器的CSS&amp;JavaScript代码片段 汇编基础知识一之汇编基本指令