非对称加密算法(RSA)是第一个既可以用于加密数据也可以应用数字签名的算法。RAS算法相对于DES和AES等对称加密算法在速度上要慢很多。使用RSA加密数据时需要使用密钥对,也就是一个公钥,一个私钥。如A、B双方发送数据,A生成密钥对,将公钥发送给B,A将数据用私钥加密后发送给B,而B用A提供的公钥对数据进行解密。如果是B向A发送数据,B用公钥加密数据并发送给A,A使用私钥对数据进行解密。
例:使用RSA生成一对密钥
使用KeyPairGenerator类的generateKeyPair()方法得到KeyPair类,再通过KeyPair类的getPublic()和getPrivate()方法分别生成公钥和密钥,下面代码省略了将密钥保存到文件的方法
public static void createrKeyFile() {
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
KeyPair kp = kpg.generateKeyPair();//用于生成密钥对
PublicKey publicKey = kp.getPublic();//得到公钥
createFile(publicKey.getEncoded(),"D:is/publicKey.dat");
//得到私钥
PrivateKey privateKey = kp.getPrivate();
createFile(privateKey.getEncoded(),"D:is/privateKey.dat");
}
运行上面该方法,输出如下(RSA的密钥对需要保持一对,它的生成和使用必须配对)
如服务端使用RSA算法将信息加密再发送到客户端,其中服务端需要分别生成公钥、密文和数字签名并将这3种数据传输给客户端。
RSAServert关键代码 用于生成密钥对(公钥和私钥)、数字签名,并将生成的数据以文件形式保存下来,客户端得到密文、公钥和数字签名才能对密文进行解密。
//生成密钥对方法
public void generateKey() {
KeyPairGenerator kpg = null;
try {
//KeyPairGenerator类用于生成密钥对,基于RSA算法生成对象
kpg = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
KeyPair kp = kpg.generateKeyPair();
//得到 公钥
PublicKey publicKey = kp.getPublic();
writeFile(publicKey.getEncoded(), "D:/encryptedFile/publicKey.dat");//将数据以文件的形式保存下来
PrivateKey privateKey = kp.getPrivate();// 得到私钥
writeFile(privateKey.getEncoded(), "D:/encryptedFile/PrivateKey.dat");
}
下面方法使用私钥对数据进行加密,先获取到私钥对象,使用私钥对数据进行加密,将加密后的数据以文件形式保存下来。(抛异常应该以try catch语句抛出异常,这篇幅有限所以使用throws Execption统一将异常抛给调用处理)
public void privateKeyEncryption (byte[] data) throws Exception {
// 读取私钥数据(以byte数组形式返回)
byte[] by = readFile("D:/encryptedFile/PrivateKey.dat");
//使用得到byte数组构造PKCS8EncodedKeySpec对象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(by);
KeyFactory keyFactory = null;
Key privateKey = null;
keyFactory = KeyFactory.getInstance("RSA");
//使用KeyFactory类的generatePrivate()方法获取私钥对象 pkcs8KeySpec 表示根据提供的密钥材料生成私钥对象
privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
// 对数据加密 Cipher.ENCRYPT_MODE表示加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
writeFile(cipher.doFinal(data), "ServerData.dat");
}
RSA生成数字签名,需要使用私钥和密文生成签名。其中使用Signature类的sign()方法生成数字签名
//根据私钥生成数字签名
public void GenerateDigitalSignature() throws Exception {
//获取私钥
byte[] privateKey = readFile("D:/encryptedFile/PrivateKey.dat");
byte[] serverData = readFile("ServerData.dat");//密文
// 构造PKCS8EncodedKeySpec对象
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(privateKey);
KeyFactory keyFactory = null; //声明
PrivateKey priKey = null;
keyFactory = KeyFactory.getInstance("RSA");
priKey = keyFactory.generatePrivate(pkcs8KeySpec); //得到私钥
//指定证书算法为MD5withRSA
Signature signature = Signature.getInstance("MD5withRSA");
signature.initSign(priKey);
signature.update(serverData);
//以文件形式保存数字签名
writeFile(signature.sign(), "D:/encryptedFile/SignData.dat");
}
编写测试方法,分别实例化RSAServer类和RSAClient类,调用方法生成密钥对,对发送的数据进行加密后,再调用方法生成数字签名,生成数字签名后验证数字签名的有效性,验证有效的话才可以解密。
@Test
public void test() {
String msg = "这是服务端加密后发送的数据";
//实例化当前类
RSAServer server = new RSAServer();
server.generateKey();// 调用方法生成密钥对
server.privateKeyEncryption(msg.getBytes());// 加密发送数据
server.GenerateDigitalSignature();// 生成数字签名
RSAClient client = new RSAClient();// 实例化客户端类
byte[] by =null; //声明数组
//调用RSAClient类的方法校对数字签名是否有效
if(client.VerifyDigitalSignature ()){
by= client.decryptByPublicKey();
}
System.out.println("服务端加密发送的数据:"+msg);
System.out.println("客户端解密的数据:"+new String (by));
}
RSAClient类关键代码 该类用于解密RSAServert类加密的数据
下面方法用于对数字签名的验证,因为RSAServert类的方法生成了数字签名,所以需要RSAClient类需要编写对数字签名的验证方法,如RSAServert类不生成数字签名,则RSAClient也不需要验证数字签名。
//验证数字签名
public boolean VerifyDigitalSignature() throws Exception {
byte[] data =readFile("ServerData.dat");//读取RSAServert加密的密文文件
byte[] publicKey = readFile("D:/encryptedFile/publicKey.dat");//公钥
//读取RSAServer生成数字签名文件
byte[] sign = readFile("D:/encryptedFile/SignData.dat");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey);
KeyFactory keyFactory = null;
PublicKey pubKey = null;
keyFactory = KeyFactory.getInstance("RSA");
pubKey = keyFactory.generatePublic(keySpec); //得到公钥对象
Signature signature = Signature.getInstance("MD5withRSA");
signature.initVerify(pubKey); /*初始化此用于验证的公钥对象,如果密钥无效,则会抛出invalidkeyException异常*/
signature.update(data); //使用指定的 byte 数组更新要签名或验证的数据
return signature.verify(sign); //验证传入的签名有效性,有效返回true,反之false
}
decryptByPublicKey方法需要注意的是X509EncodedKeySpec用于构建公钥规范,PKCS8EncodedKeySpec用于构造私钥规范
//使用公钥对数据进行解密
public byte[] decryptByPublicKey() {
byte[] data = readFile("ServerData.dat");
byte[] key = readFile("D:/encryptedFile/publicKey.dat");
//构建公钥规范
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = null;
Key publicKey = null;
keyFactory = KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(x509KeySpec); // 取得公钥
//根据算法名称返回指定转换的Cipher对象
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
// 初始化 Cipher.DECRYPT_MODE表示解密
cipher.init(Cipher.DECRYPT_MODE, publicKey);
//完成解密返回包含缓冲区
return cipher.doFinal(data);
}
下面这里RSAClient类的加密方法跟RSAServert类的加密方法都是大同小异的,读取公钥,构建公钥规范,使用KeyFactory类的generatePublic()方法生成公钥对象(KeyFactory类最常用的操作就是通过密钥规范生成相关密钥对象),然后就是加密操作了。
//使用公钥加密数据
public void encryptOfClient(byte[] data) {
byte[] key = readFile("D:/encryptedFile/publicKey.dat");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(key);
KeyFactory keyFactory = null;
Key publicKey = null;
keyFactory = KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(x509KeySpec);
// 下面是加密操作
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
writeFile(cipher.doFinal(data), "ClientData.dat");//保存密文
}
编写测试方法,分别实例化相关类,调用方法加密、解密,然后输出数据
@Test
public void test() {
//实例化
RSAClient client = new RSAClient();
RSAServer server = new RSAServer();
String msg = "客户端加密后发送的信息";
client.encryptOfClient(msg.getBytes());//调用方法对数据进行加密
byte [] data = server.decryptByPrivateKey();//调用RSAServert的方法解密
System.out.println("客户端加密发送的数据:"+msg);
System.out.println("服务端解密后数据:"+new String (data));
}
编写riteFile()方法将byte[]的数据写入文件
public void writeFile(byte[] data, String fileName) {
//省略代码
}
编写readFile()方法读取文件并以byte[]返回数据
public byte[] readFile(String fileName) {
//省略代码
}
运行RSAClient类的Test方法,客户端将数据加密后发送到服务端,效果如下
运行RSAServert类的Test方法,服务端加密数据并发送到客户端,效果如下