Java安全系列-RSA加密
Posted Zip Zou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java安全系列-RSA加密相关的知识,希望对你有一定的参考价值。
Java安全系列-RSA加密
在现在的信息安全体系中,加密已经成为了一个常识。在诸多场合下为了数据安全和可靠,很多情况下需要使用加密,如:密码体系、安全邮件、网络传输等等方面。
在现有的加密算法中,大致可以分为两大类:对称加密和非对称加密。
在对称加密中,较为典型的算法为:DES、IDEA、AES等;
在非对称加密体系中,其中较为常用的算法有:RSA加密体系,ECC加密等。
在上述的加密过程中,都需要借助密钥。在对称加密算法体系中,其加密解密,需要使用相同的密钥进行加密和解密。在该过程中,产生密钥E,同时有明文数据P,为此采用相应的对称算法对其进行加密,会得到密文C。该过程则为对称加密过程。在加密后,若需要对数据进行解密,则同样需要使用密钥E,对密文C进行解密,得到明文P,该过程为解密过程。
在对称加密中,有:加密速度快、计算量小等优点,并可公开算法,只需做到密钥的管理,即可保证加密的安全性。但是在对称加密体系中,密钥是多种多样的,并且为了保证数据可靠,必须做到严格的密钥管理,会对用户造成负担,从而造成数据的不安全性。
在非对称加密体系中,需要协商产生密钥对:私钥-公钥,产生一对密钥后,公钥可以公开使用,交由客户端或用户使用,令客户在加密时,采用公钥进行加密,产生密文,交由私钥拥有者进行使用。拥有私钥的一方,接收到经过公钥加密的密文后,使用只有其保留的私钥对密文进行解密,从而可以得到明文。该过程则为普遍的“公钥加密,私钥解密”的过程体系。并且在非对称加密中,可以使用“私钥加密,公钥解密”的过程,来实现数字签名,因为私钥加密,可以实现不可否认性。由于在非对称加密体系中,只有一部分有权限的用户,可以保留私钥,其他用户只能保留公钥,公钥可以公开在网络上进行传输。因此用户通过使用私钥对数据进行加密,则一定能够保证该数据是由私钥持有者进行加密(签名),其他用户在接收到密文时,采用公钥进行解密,则一定能够确认信息发送者是唯一的。
RSA数字签名算法的过程为:A对明文m用解密变换作: s Dk (m)=md mod n,其中d,n为A的私人密钥,只有A才知道它;B收到A的签名后,用A的公钥和加密变换得到明文,因: Ek(s)= Ek(Dk (m))= (md)e mod n,又 de1 mod (n)即de=l(n)+1,根据欧拉定理m(n)=1 mod n,所以Ek(s)=ml(n)+1=[m(n)]em=m mod n.若明文m和签名s一起送给用户B,B可以确信信息确实是A发送的.同时A也不能否认送给这个信息,因为除了A本人外,其他任何人都无法由明文m产生s.因此RSA数字签名方案是可行的. —-引用至RSA算法和RSA数字签名算法的实现
在非对称加密体系中,其理论支撑为“大数分解“的数学难题,若数足够大,我们可以认定,该难题是无解的,从而保证加密的安全性。
其中在Linux的SSH连接中,可以配置免密钥登录,实际上,依然采用的是非对称加密体系,对连接进行加密。如以下过程:
- A主机希望通过SSH免密钥连接到B主机;
- 在A主机中,产生密钥对,E1(私钥)、E2(公钥);
- 将E2分发到B主机中;
- A主机连接B主机时,A主机发送连接请求到B主机,其中携带信息包括:IP地址、用户名等;
- B主机接收到数据后,查询已知主机列表中是否有该IP地址和用户,若没有则提示用户是否要添加到已知列表。若有,则B主机会随机产生一个字符串S,用于身份认证,该字符串S使用E2公钥进行加密发送到主机A中;
- 主机A接受到加密后的随机字符串,主机A使用其私钥对其进行解密,得到明文数据P,发送到主机B中;
- B接受该明文数据,对明文进行判断,若和产生的随机数一致,则连接成功;
图引用至:ssh免密码登录的原理
其他加密算法,如:MD5、SHA-1、SHA2等,则为Hash算法,该过程一般来说,是不可逆的。
Java实现RSA加密
准备工作:借助apache commons codec工具进行Base64编解码,实现密钥的编解码。
在实现RSA时,通常分为2大步:
- 协商密钥对,并产生一对密钥;
- 利用公钥进行加密,利用私钥进行解密。
产生密钥可以使用如下代码:
package site.franksite.encrpt.rsaencrypt;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.util.Date;
import org.apache.commons.codec.binary.Base64;
/**
* RSA密钥生成类
* @author frank
*
*/
public class RSAKeyGenerator
private byte[] publicKeyEncoded;
private byte[] privateKeyEncoded;
private static final int KEY_LENGTH = 1024;
public void generate()
try
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
SecureRandom secRandom = new SecureRandom();
secRandom.setSeed(new Date().getTime());
generator.initialize(KEY_LENGTH, secRandom);
// 产生键值对
KeyPair pair = generator.generateKeyPair();
PublicKey pubKey = pair.getPublic();
PrivateKey priKey = pair.getPrivate();
// 加密公钥私钥
publicKeyEncoded = Base64.encodeBase64(pubKey.getEncoded());
privateKeyEncoded = Base64.encodeBase64(priKey.getEncoded());
catch (NoSuchAlgorithmException e)
e.printStackTrace();
/**
* @return the publicKeyEncoded
*/
public byte[] getPublicKeyEncoded()
if (null == publicKeyEncoded)
generate();
return publicKeyEncoded;
/**
* @return the privateKeyEncoded, encrypt by Base64
*/
public byte[] getPrivateKeyEncoded()
if (null == privateKeyEncoded)
generate();
return privateKeyEncoded;
在获得到密钥对时,我们需要将其保存,或持久化到数据库,或持久化到文件,供加密解密过程中使用。
在加密时,可以如下实现:
public byte[] encrypt(byte[] pubKey, byte[] data)
PublicKey key = restorePublicKey(pubKey);
try
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
catch (NoSuchAlgorithmException e)
e.printStackTrace();
catch (NoSuchPaddingException e)
e.printStackTrace();
catch (InvalidKeyException e)
e.printStackTrace();
catch (IllegalBlockSizeException e)
e.printStackTrace();
catch (BadPaddingException e)
e.printStackTrace();
return null;
在该代码中,首先将公钥字节码,还原为公钥实例,再调用Cipher对数据进行加密。
其中还原公钥代码为:
/**
* 将公钥字节码转为公钥实例
* @param publicKey RSA公钥字节码
* @return 公钥实例
*/
public PublicKey restorePublicKey(byte[] publicKey)
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
try
KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPT_ALGORITHM);
PublicKey pubKey = keyFactory.generatePublic(keySpec);
return pubKey;
catch (NoSuchAlgorithmException e)
e.printStackTrace();
catch (InvalidKeySpecException e)
e.printStackTrace();
return null;
加密后,密文将作为字节流的方式返回,我们可以对该二进制字节码文件采用Base64进行编码,产生Base64字符串,进行存储或转发。
同样的解密过程也分为上述过程,首先还原私钥的二进制流数据:
/**
* 将私钥字节码转为私钥实例
* @param pivateKey RSA私钥字节码
* @return 私钥实例
*/
public PrivateKey restorePrivateKey(byte[] pivateKey)
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pivateKey);
try
KeyFactory keyFactory = KeyFactory.getInstance(ENCRYPT_ALGORITHM);
PrivateKey key = keyFactory.generatePrivate(keySpec);
return key;
catch (NoSuchAlgorithmException e)
e.printStackTrace();
catch (InvalidKeySpecException e)
e.printStackTrace();
return null;
在获得到该私钥实例后,使用该私钥进行解密:
public byte[] dencrypt(byte[] priKey, byte[] data)
// 还原私钥
PrivateKey privateKey = restorePrivateKey(priKey);
try
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(data);
catch (NoSuchAlgorithmException e)
e.printStackTrace();
catch (NoSuchPaddingException e)
e.printStackTrace();
catch (InvalidKeyException e)
e.printStackTrace();
catch (IllegalBlockSizeException e)
e.printStackTrace();
catch (BadPaddingException e)
e.printStackTrace();
return null;
解密后的数据将以字节流的形式返回!
注:上述代码中的常量
CIPHER_ALGORITHM
值为:”RSA/ECB/PKCS1Padding”,ENCRYPT_ALGORITHM
为:”RSA”
通过上述方式,即可完成加密及解密。
同样的,采用类似的过程,可以借助RSA实现数字签名:
public byte[] sign(byte[] priKey, byte[] data)
PrivateKey privateKey = restorePrivateKey(priKey);
try
Signature sign = Signature.getInstance("MD5withRSA");
sign.initSign(privateKey);
sign.update(data);
return Base64.encodeBase64(sign.sign());
catch (NoSuchAlgorithmException e)
e.printStackTrace();
catch (InvalidKeyException e)
e.printStackTrace();
catch (SignatureException e)
e.printStackTrace();
return null;
上述为签名过程。
public boolean verify(byte[] pubKey, byte[] data, byte[] originData)
PublicKey publicKey = restorePublicKey(pubKey);
Signature sign;
try
sign = Signature.getInstance("MD5withRSA");
sign.initVerify(publicKey);
sign.update(originData);
return sign.verify(data);
catch (NoSuchAlgorithmException e)
e.printStackTrace();
catch (InvalidKeyException e)
e.printStackTrace();
catch (SignatureException e)
e.printStackTrace();
return false;
上述为签名验证过程。
项目下载
本项目实现了JAVA RSA加密及数字签名,有需要的用户可以到github检出或浏览:JAVA RSAEncrypt
以上是关于Java安全系列-RSA加密的主要内容,如果未能解决你的问题,请参考以下文章