8.3.3 实现

Java 6提供了RSA算法实现,相关内容请读者朋友阅读第3章。RSA算法在理解上较之DH算法更为简单,在实现层面上也同样如此。

RSA算法与DH算法在密钥管理和加密/解密两方面有所不同:一方面,甲方保留了私钥,而将公钥公布于乙方,甲乙双方密钥一一对应;另一方面,私钥用于解密,公钥则用于加密,反之亦然。

这里要注意一点,在RSA算法中,公钥既可用于解密也可用于加密,私钥也是如此。但公钥加密的数据只能用私钥对其解密,而私钥加密的数据只能用公钥对其解密,这种对应关系是不能违背的。

RSA算法实现较之DH算法实现较为简单,大部分内容与DH算法较为接近。与DH算法不同的是,RSA算法仅需要一套密钥即可完成加密/解密操作。Bouncy Castle对RSA算法做了相应扩充,增加了ISO9796-1Padding填充方式。

有关RSA算法的Java 6实现与Bouncy Castle实现细节如表8-2所示。

figure_0284_0068

完整实现如代码清单8-8所示。

代码清单8-8 RSA算法实现


import java.security.Key;

import java.security.KeyFactory;

import java.security.KeyPair;

import java.security.KeyPairGenerator;

import java.security.PrivateKey;

import java.security.PublicKey;

import java.security.interfaces.RSAPrivateKey;

import java.security.interfaces.RSAPublicKey;

import java.security.spec.PKCS8EncodedKeySpec;

import java.security.spec.X509EncodedKeySpec;

import java.util.HashMap;

import java.util.Map;

import javax.crypto.Cipher;

/**

*RSA安全编码组件

*@author梁栋

*@version 1.0

*/

public abstract class RSACoder{

//非对称加密密钥算法

public static final String KEY_ALGORITHM="RSA";

//公钥

private static final String PUBLIC_KEY="RSAPublicKey";

//私钥

private static final String PRIVATE_KEY="RSAPrivateKey";

/**

*RSA密钥长度

*默认1024位,

*密钥长度必须是64的倍数,

*范围在512~65536位之间。

*/

private static final int KEY_SIZE=512;

/**

*私钥解密

*@param data待解密数据

*@param key私钥

*@return byte[]解密数据

*@throws Exception

*/

public static byte[]decryptByPrivateKey(byte[]data, byte[]key)throws Exception{

//取得私钥

PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);

KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);

//生成私钥

PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);

//对数据解密

Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.DECRYPT_MODE, privateKey);

return cipher.doFinal(data);

}

/**

*公钥解密

*@param data待解密数据

*@param key公钥

*@return byte[]解密数据

*@throws Exception

*/

public static byte[]decryptByPublicKey(byte[]data, byte[]key)throws Exception{

//取得公钥

X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);

KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);

//生成公钥

PublicKey publicKey=keyFactory.generatePublic(x509KeySpec);

//对数据解密

Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.DECRYPT_MODE, publicKey);

return cipher.doFinal(data);

}

/**

*公钥加密

*@param data待加密数据

*@param key公钥

*@return byte[]加密数据

*@throws Exception

*/

public static byte[]encryptByPublicKey(byte[]data, byte[]key)throws Exception{

//取得公钥

X509EncodedKeySpec x509KeySpec=new X509EncodedKeySpec(key);

KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);

PublicKey publicKey=keyFactory.generatePublic(x509KeySpec);

//对数据加密

Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.ENCRYPT_MODE, publicKey);

return cipher.doFinal(data);

}

/**

*私钥加密

*@param data待加密数据

*@param key私钥

*@return byte[]加密数据

*@throws Exception

*/

public static byte[]encryptByPrivateKey(byte[]data, byte[]key)throws Exception{

//取得私钥

PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(key);

KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);

//生成私钥

PrivateKey privateKey=keyFactory.generatePrivate(pkcs8KeySpec);

//对数据加密

Cipher cipher=Cipher.getInstance(keyFactory.getAlgorithm());

cipher.init(Cipher.ENCRYPT_MODE, privateKey);

return cipher.doFinal(data);

}

/**

*取得私钥

*@param keyMap密钥Map

*@return byte[]私钥

*@throws Exception

*/

public static byte[]getPrivateKey(Map<String, Object>keyMap)throws Exception{

Key key=(Key)keyMap.get(PRIVATE_KEY);

return key.getEncoded();

}

/**

*取得公钥

*@param keyMap密钥Map

*@return byte[]公钥

*@throws Exception

*/

public static byte[]getPublicKey(Map<String, Object>keyMap)throws Exception{

Key key=(Key)keyMap.get(PUBLIC_KEY);

return key.getEncoded();

}

/**

*初始化密钥

*@return Map密钥Map

*@throws Exception

*/

public static Map<String, Object>initKey()throws Exception{

//实例化密钥对生成器

KeyPairGenerator keyPairGen=KeyPairGenerator.getInstance(KEY_ALGORITHM);

//初始化密钥对生成器

keyPairGen.initialize(KEY_SIZE);

//生成密钥对

KeyPair keyPair=keyPairGen.generateKeyPair();

//公钥

RSAPublicKey publicKey=(RSAPublicKey)keyPair.getPublic();

//私钥

RSAPrivateKey privateKey=(RSAPrivateKey)keyPair.getPrivate();

//封装密钥

Map<String, Object>keyMap=new HashMap<String, Object>(2);

keyMap.put(PUBLIC_KEY, publicKey);

keyMap.put(PRIVATE_KEY, privateKey);

return keyMap;

}

}


RSA算法实现易于理解,对于RSA算法的测试只需要注意经公钥加密的数据是否可以通过私钥将其解密,反之,经私钥加密的数据是否可以通过公钥将其解密。完整的测试用例如代码清单8-9所示。

代码清单8-9 RSA算法实现测试用例


import static org.junit.Assert.*;

import org.apache.commons.codec.binary.Base64;

import org.junit.Before;

import org.junit.Test;

import java.util.Map;

/**

*RSA校验

*@author梁栋

*@version 1.0

*/

public class RSACoderTest{

//公钥

private byte[]publicKey;

//私钥

private byte[]privateKey;

/**

*初始化密钥

*@throws Exception

*/

@Before

public void initKey()throws Exception{

//初始化密钥

Map<String, Object>keyMap=RSACoder.initKey();

publicKey=RSACoder.getPublicKey(keyMap);

privateKey=RSACoder.getPrivateKey(keyMap);

System.err.println("公钥:\n"+Base64.encodeBase64String(publicKey));

System.err.println("私钥:\n"+Base64.encodeBase64String(privateKey));

}

/**

*校验

*@throws Exception

*/

@Test

public void test()throws Exception{

System.err.println("\n—-私钥加密—公钥解密—-");

String inputStr1="RSA加密算法";

byte[]data1=inputStr1.getBytes();

System.err.println("原文:\n"+inputStr1);

//加密

byte[]encodedData1=RSACoder.encryptByPrivateKey(data1,privateKey);

System.err.println("加密后:\n"+Base64.encodeBase64String(encodedData1));

//解密

byte[]decodedData1=RSACoder.decryptByPublicKey(encodedData1,

publicKey);

String outputStr1=new String(decodedData1);

System.err.println("解密后:\n"+outputStr1);

//校验

assertEquals(inputStr1,outputStr1);

System.err.println("\n—-公钥加密—私钥解密—-");

String inputStr2="RSA Encypt Algorithm";

byte[]data2=inputStr2.getBytes();

System.err.println("原文:\n"+inputStr2);

//加密

byte[]encodedData2=RSACoder.encryptByPublicKey(data2,publicKey);

System.err.println("加密后:\n"+Base64.encodeBase64String(encodedData2));

//解密

byte[]decodedData2=RSACoder.decryptByPrivateKey(encodedData2,

privateKey);

String outputStr2=new String(decodedData2);

System.err.println("解密后:"+outputStr2);

//校验

assertEquals(inputStr2,outputStr2);

}

}


在控制台中输出了密钥的Base64编码信息,如下文所示:


公钥:

MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJ0PYlHiU439wWfWvQKMEgjjE/swks9KCG0UJy2qmSBs

2e76f0eTswnNl7nFxbBr5TRRyl4EhkudWDIr0u6kBgcCAwEAAQ==

私钥:

MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAnQ9iUeJTjf3BZ9a9AowSCOMT+zCS

z0oIbRQnLaqZIGzZ7vp/R5OzCc2XucXFsGvlNFHKXgSGS51YMivS7qQGBwIDAQABAkAjMB4sEFP9

/PtG43KHTpB/0zhXz8MklAadQaWhcpZKEB3LRINLH4jJ2UJxGWXMDS5nZtbG3/1jYd2Ee0bwGb7B

AiEA8M/t/6MB4Yx4C4OBkd51TDByKJmKIEnFu2HSyzA6c2ECIQCm9zi9OuX2N/5rVytXfA+Oj7L1

wetSOrGbEHXX3D/aZwIhAMRHJlyr13eoj5wK1ww2/vJXtmSjKPNCThl6FV8p8yphAiBVJyC44aEG

wefvtrVUGOGWQ5Nx40Sw215ZRzvSq3GlYQIhALIHY3fxmLlEg3NC269ouFsYeTF/EO+M02+pazz+

3UPv


与DH算法不同,RSA算法仅需要一套密钥即可完成加密/解密操作,并且我们发现公钥的密钥长度明显小于私钥的密钥长度,更便于发送和携带。

私钥加密公钥解密,如以下代码所示:


—-私钥加密—公钥解密—-

原文:

RSA加密算法

加密后:

EePGm+yWtFvgSvc1pmh1hNoy3KyH0gssjc2FlvPSNkFAOOFOvvVIPQAmeRtTD+L3oUKUC61zQeqf

N2B/t0ylxg==

解密后:

RSA加密算法


反之,公钥加密私钥解密,如以下代码所示:

—-公钥加密—私钥解密—-

原文:

RSA Encypt Algorithm

加密后:

hehNcGA8EGilNk3FJ7snGOU9XKGHN7t6DJlHQG9Ddi+h/xdk/IzWs3+SJfFsEnrTQe+96UvpEmF2

atA7+Fndgw==

解密后:RSA Encypt Algorithm


RSA算法公钥长度远小于私钥长度,并遵循“公钥加密,私钥解密”和“私钥加密,公钥解密”这两项加密/解密原则。