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所示。

figure_0313_0075

经作者反复测试,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位,与签名长度相符。