您的位置:首页 > 编程语言 > Java开发

Java安全系列-RSA加密

2017-11-16 11:08 211 查看

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 安全 信息安全