如何利用java程序实现加密所需的公钥密钥数字证书
Posted 大黄奔跑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何利用java程序实现加密所需的公钥密钥数字证书相关的知识,希望对你有一定的参考价值。
本篇的主要目的在于实现pdf的数字签名问题,只是作为我学习知识的总结。
1、数字签名算法的概述
本部分主要参考于:https://blog.csdn.net/lovelichao12/article/details/75007189
数字签名:私钥用于签名,公钥用于验证。
数字签名的作用:
验证数据的完整性,认证数据来源,抗否认。
数字签名实现的具体原理:
1、 将报文按双方约定的HASH算法计算得到一个固定位数的报文摘要。在数学上保证,只要改动报文中任何一位,重新计算出的报文摘要值就会与原先的值不相符。这样就保证了报文的不可更改性。(详见参考资料的"公钥密码技术原理"章节)
2、 将该报文摘要值用发送者的私人密钥加密,然后连同原报文和数字证书(包含公钥)一起发送给接收者而产生的报文即称数字签名。
3、接收方收到数字签名后,用同样的HASH算法对报文计算摘要值,然后与用发送者的公开密钥进行解密解开的报文摘要值相比较,如相等则说明报文确实来自所称的发送者。
4、同时通过证书颁发机构CA确认证书的有效性即可确认发送的真实身份。
常用的数字签名有:RSA、DSA、ECDSA
2、RSA算法概述
RSA是目前为止应用最为广泛的非对称加密算法。非对称加密算法简单的说就是分成公钥和私钥。加密和解密采用不同的算法实现,这样的好处是不需要像传统对称加密算法一样将相同算法的密钥分发给对方,从而减少密钥被获取所带来的严重危害,目前基本上都是采用非对称算法,而RSA是最为广泛的。理论上1024位以上的RSA是无法破解的(或者未公开)。
基本原理:
非对称算法将密码将密码分为公钥和私钥,公钥发送给用户(可以是多个),用户用公钥加密想要发送的数据,然后发送给服务器,服务器通过私钥解密加密后的数据。
基本步骤:
生成公钥和私钥步骤:
- 随机选择两个不相等的质数p和q
- 计算p和q的乘积n (n的长度就是密钥长度。3233写成二进制是110010100001,一共有12位,所以这个密钥就是12位。实际应用中,RSA密钥一般是1024位,重要场合则为2048位。)
- 计算n的欧拉数?(n)=(p-1)(q-1)(欧拉数即为小于等于n的所有质数
- 随机选择一个整数e,条件时1<e<?(n)1<e<?(n),且e与?(n)?(n)互质。
- 计算e对于的模反元素d(模反元素,就是指有个整数可以使得e×d/?(n)e×d/?(n)的余数是1。 ed - 1 = kφ(n) )
- (n,e)为公钥,(n,d)为私钥。
加密过程:
- 求需要加密数据m的e次方,然后用结果对n求膜,结果为c。
解密过程:
- 对需要解密的数据c求d次方,然后用结果对n求膜,结果为m。
RSA算法的限制:
首先一般的RSA算法膜长位1024位,也就是128个字节。加密的信息必须小于这个值。而且有时候会需要一些额外的信息比如java的实现还需要11个字节的额外信息,隐藏加密数据不能超过过117个字节。
解决方案有两种一种是对信息分段加密,另一种是先选择一种”对称性加密算法”(比如DES),用这种算法的密钥加密信息,再用RSA公钥加密DES密钥。
3、数字签名和数字证书
如果说RSA解决的是信息加密传输的问题,那数字签名解决的就是用户验证回送数据是否来自于服务器的问题,数字证书解决的是用户验证当前通信的服务器是否是用户期望的服务器的问题。
服务器接收到用户加密信息后需要给用户回信,为了使得用户确认所发送的信息是来自服务器的,需要采用数字签名。
步骤:
- 服务器先使用散列函数生成摘要。
- 服务器然后使用摘要和其他验证信息,使用私钥加密生成数字签名。
- 服务器将数字签名附在回送数据上一起发回给用户
- 用户使用公钥解密数字签名得到摘要和,得以确定回送数据来自服务器。
- 用户对回送数据使用相同的散列函数与摘要对比,如果相同则回送数据未被修改过。
数字证书
更为复杂的情况:由于公钥是很容易获取的,如果一位用户a将另一位用户b的服务器公钥给修改成为用户a自己生成的公钥则用户a可以冒充服务器域用户b通信。
用户b如何验证当前通信的服务器是否是正确的服务器?
用户b需要去CA(证书中心 certificate authority),为公钥做认证。
CA用自己的私钥对服务器的公钥和相关验证信息进行加密,生成数字证书(Digital Certificate)
这样服务器拿到数字证书,再给用户回信的时候附上这个数字证书,用户收到这个数字证书然后用的CA的公钥解密,就可以拿到服务器的公钥了,然后就能证明数字签名是否是服务器的。
BASE64算法:
BASE64是一种将二进制数转换成ACSii码的可打印字符的表示方式。一共需要表示64个可打印字符,大小写字母52个加上10个数字0-9加上+号和斜杠/一共64字符,另外需要一个等号=作为后缀。
BASE64将3个字符换成4个字符。3*8=4*6;
转换后数据大小会变大三分之一左右
为何需要进行BASE64的转换?
BASE64转换主要是为了避免敏感字符。比如有一些%或者回车等等在网络协议中有特殊的含义,为了避免解析错误通常发送数据都会进行BASE64的转换。
其次,有些数据时不看可以见的数据,有时候需要复制粘贴,使用BASE64编码可以将不可见的数据转成可见的数据,更容易复制粘贴或其他操作。
另外,进行转换也可以避免数据直接暴露,算是一种非正式的加密算法。
RSA、数字签名和base64的实现
主要参考:Java使用RSA加密解密签名及校验
RSA的java实现
密钥的获取
首先按照java面向对象的惯例,任何东西都有对象,也就是说公钥和私钥都是需要用类(或者接口)表示的。公钥和私钥的类型很多。java提供了一个接口RSAPrivateKey和RSAPublicKey。用于分别引用RSA类型的公钥和私钥接口。
通常来说密钥都有两个接口,一个接口说明时公钥还是私钥如PrivateKey和PublicKey,另一个接口说明了的是什么算法如RSA或者DSA等等。类似多重继承。
其次再来看获得密钥的方式有两类,一类是自己生成,一类是从网络中得到的字符串转换。
自己生成密钥:
自己生成密钥使用的是KeyPairGenerator这是一个java自带的密钥生成类,生成步骤分为三步:
- 通过
getInstance(算法名)
的方式获得特点算法的生成类, - 然后用
initialize
初始化参数,对于RSA来说通常就是指定一个位数1024,然后指定一个随机数生成器如new SecureRandom。 -
最后用
generateKeyPair()
生成KeyPair密钥对,再对密钥对KeyPair使用get方法获得公钥和私钥。从网络中获取:
通常来说从网络中获取的都是字符串或者时byte[]格式,注意这里通常会使用base编码,使用时通常需要转换。
这样的数据都有固定的数据格式典型的如ASN.1格式。X509EncodedKeySpec是RSA的公钥ASN.1格式,PKCS8EncodedKeySpec是RSA的私钥的ASN.1的格式。这样对不同的密钥不同的格式使用对于的类进行转换就可以得到PrivateKey和PublicKey的格式。
这里还需要用到一个KeyFactory类用于得到转换器。
转换的步骤:
- 首先通过
KeyFactory.getInstance("RSA");
获得一个RSA的工厂类。 - 通过X509EncodedKeySpec或者PKCS8EncodedKeySpec的带参构造器(参数为byte[]的密钥数据)获得一个指定表示特定格式的类。
- 最后通过
keyFactory.generatePublic(keySpec)
和(RSAPrivateCrtKey) keyFactory.generatePrivate(keySpec)
来获得,记得类型转换
密钥的加密和解密
对于公钥和私钥,无论时那种类型的密钥,无论是进行加密还是进行解密,过程是一致的。而Cipher是完成这个任务的核心类。
基本过程:
- 首先通过
Cipher.getInstance("RSA")
获得一个Cipher。 - 初始化:
cipher.init(Cipher.ENCRYPT_MODE, privatekey);
只有两个参数,第一个设定模式,加密为ENCRYPT_MODE,解密为DECRYPT_MODE,第二个为公钥或者私钥。 - 最后通过
dofine
实现流程,输出为byte[];
数字签名的java实现
数字签名的过程和上述第二小节加密解密的过程时类似的,甚至更简化,因为数字签名只用私钥签名,公钥证人。而signature是完成这个任务的核心类:
基本过程:
- 获得key,流程如上述:
Signature.getInstance("SHA256withRSA");
使用getInstance获得Signature类。并指定签名的算法。常用的算法java reference中有指定。- 使用signatur实例的
initSign(PriKey)
进行签名初始化,或者initVerify(pubkey)
进行认证的初始化。 - 使用signatur实例的
update(bytes)
输入需要进行签名的数据。 - 使用signatur实例的
sign()
或者verify()
实现签名或者认证。注意前者返回byte[]后者返回boolean。
base64
目前来说有三类方法
- Apache Commons Codec
- sun.misc
- java8之后自带的BASE64.Decoder和Encoder。
这里之介绍最后一个,其他版本的应用网上也很多教程。其实都很简单。
基本步骤:
- 首先你要生成一编码和解码器通过,
Encoder encoder = Base64.getEncoder();
和Decoder decoder = Base64.getDecoder();
- 通过
encoder.encodeToString(bytes);
编码通过ecoder.decode(string)
解码
以上是关于如何利用java程序实现加密所需的公钥密钥数字证书的主要内容,如果未能解决你的问题,请参考以下文章