9.3.2 实现
Java 6不仅提供了RSA加密算法相关实现,同时也提供了RSA数字签名算法实现。
RSA数字签名算法的密钥实现与RSA加密算法一致,算法名称同为“RSA”,密钥产生与转换完全一致,读者朋友可阅读第8章相关内容。
R S A数字签名算法主要可以分为M D系列和S H A系列两大类。M D系列主要包括MD2withRSA和MD5withRSA共2种数字签名算法;SHA系列主要包括SHA1withRSA、SHA224withRSA、SHA256withRSA、SHA384withRSA和SHA512withRSA共5种数字签名算法。其中,SHA224withRSA、SHA256withRSA、SHA384withRSA和SHA512withRSA这4种数字签名算法需要由第三方加密组件包提供,例如Bouncy Castle。Java 6则只提供了MD2withRSA、MD5withRSA和SHA1withRSA共3种数字签名算法。
有关RSA数字签名算法的Java 6实现与Bouncy Castle实现细节如表9-1所示。
经作者反复测试,RSA数字签名算法的签名值与密钥长度相同。
我们只需要注意签名和验证两个方法的实现,如代码清单9-1和9-2所示。相关Java API内容请阅读第3章。
代码清单9-1 RSA数字签名算法—签名
//实例化Signature
Signature signature=Signature.getInstance("MD5withRSA");
//初始化Signature
signature.initSign(privateKey);
//更新
signature.update(data);
//签名
byte[]sign=signature.sign();
注意:上述代码清单中使用到的变量privateKey是私钥对象,data则是待签名数据,sign就是我们需要的签名。
代码清单9-2 RSA数字签名算法—验证
//实例化Signature
Signature signature=Signature.getInstance("MD5withRSA");
//初始化Signature
signature.initVerify(publicKey);
//更新
signature.update(data);
//验证
boolean status=signature.verify(sign);
注意上述代码,验证签名时使用到的变量publicKey是公钥对象,data则是待验证数据,sign就是我们之前得到的签名。
数字签名与验证实现如上述代码清单9-1和9-2所示,较为简单。
需要注意的是,RSA密钥长度默认1024位,密钥长度必须是64的倍数,范围在512~65536位之间。
我们以RSA数字签名算法MD5withRSA为例,展示完整的数字签名算法实现,如代码清单9-3所示。
代码清单9-3 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.Signature;
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;
/**
*RSA签名算法组件
*@author梁栋
*@version 1.0
*/
public abstract class RSACoder{
/**
*数字签名
*密钥算法
*/
public static final String KEY_ALGORITHM="RSA";
/**
*数字签名
*签名/验证算法
*/
public static final String SIGNATURE_ALGORITHM="MD5withRSA";
//公钥
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 privateKey私钥
*@return byte[]数字签名
*@throws Exception
*/
public static byte[]sign(byte[]data, byte[]privateKey)throws Exception{
//转换私钥材料
PKCS8EncodedKeySpec pkcs8KeySpec=new PKCS8EncodedKeySpec(privateKey);
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//取私钥匙对象
PrivateKey priKey=keyFactory.generatePrivate(pkcs8KeySpec);
//实例化Signature
Signature signature=Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initSign(priKey);
//更新
signature.update(data);
//签名
return signature.sign();
}
/**
*校验
*@param data待校验数据
*@param publicKey公钥
*@param sign数字签名
*@return boolean校验成功返回true失败返回false
*@throws Exception
*/
public static boolean verify(byte[]data, byte[]publicKey, byte[]sign)throws
Exception{
//转换公钥材料
X509EncodedKeySpec keySpec=new X509EncodedKeySpec(publicKey);
//实例化密钥工厂
KeyFactory keyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//生成公钥
PublicKey pubKey=keyFactory.generatePublic(keySpec);
//实例化Signature
Signature signature=Signature.getInstance(SIGNATURE_ALGORITHM);
//初始化Signature
signature.initVerify(pubKey);
//更新
signature.update(data);
//验证
return signature.verify(sign);
}
/**
*取得私钥
*@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;
}
}
在这里,我们用十六进制字符串形式来表示数字签名,密钥仍以Base64编码表示,上述代码对应的测试用例如代码清单9-4所示。
代码清单9-4 RSA数字签名算法实现测试用例
import static org.junit.Assert.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
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 testSign()throws Exception{
String inputStr="RSA数字签名";
byte[]data=inputStr.getBytes();
//产生签名
byte[]sign=RSACoder.sign(data, privateKey);
System.err.println("签名:\n"+Hex.encodeHexString(sign));
//验证签名
boolean status=RSACoder.verify(data, publicKey, sign);
System.err.println("状态:\n"+status);
//验证
assertTrue(status);
}
}
控制台中得到的密钥信息如下所示:
公钥:
FwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKW1jHvKUUwN3zXAtooVJMRn57viQ
PspDgnluB7Madjdn++vb5MfyNSc4gIMBKT7t+5H1hPEhHSoDCIDWYHuoFUCAwEAAQ==
私钥:
MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEApbWMe8pRTA3fNcC2i
hUkxGfnu+JA+ykOCeW4Hsxp2N2f769vkx/I1JziAgwEpPu37kfWE8SEdKgMIgNZge6gVQIDAQA
BAkAc4/QkOPfHjLuXwYuRs3H/lCYAyceOgm/iJdzd8cGaf3DO1r65fikzMJtchsUfXCkqUHawDhOezb6BRc
AoWVzZAiEA6qcVL/V43ugHDcw5UViI0/7o0hCe/FoOTc7OxMZUi/8CIQC0yNBvBQJPkWatJ7/uzbJ2z9VKt
gLmlrGkiVSLGK7jqwIgUkWWtiP45x6vKnVKO20xPDMJ6m0NcSkaDsnN0UxAZH0CIAUUx9b2+xOlcI9ZWK
mrmmKAgOwypW45uhB306p9LR01AiABytSbhV3FsoR+Ypc8H7vMLhvqhHM1gt2GoGlFCdbcpA==
毋庸置疑,对于RSA算法密钥,公钥通常要比私钥短。
注意控制台输出的签名及验证结果,如下所示:
签名:
6246e5efec4aa3dd054f846695ac6549e4a90c4e3625a11127d836ce5667
1fb32f100f7c8a662a3853ff2e7a1d25c49dd85e67fc240c7fab2cd0ddc422acf55a
状态:
true
这个签名串是一个128位的十六进制串,也就是512位的二进制串。在上述测试用例中,我们定义的密钥长度是512位,与签名长度相符。