9.5.2 实现

Java 6对数字签名算法的支持很有限,除了RSA算法就只有DSA算法了。作者曾寻觅椭圆曲线加密算法许久,但至今未果,在研究Bouncy Castle时意外发现了椭圆曲线数字签名算法ECDSA。

Bouncy Castle支持多种数字签名算法,单纯ECDSA算法系列包括NONEwithECDSA、RIPEMD160withECDSA、SHA1withECDSA、SHA224withECDSA、SHA256withECDSA、SHA384withECDSA和SHA512withECDSA共7种算法。除此之外,Bouncy Castle还支持其他RSA数字签名算法,如SHA224withRSA、SHA256withRSA、SHA384withRSA和SHA512withRSA等。

ECDSA数字签名算法的密钥算法同为“ECDSA”,实现方式与RSA和DSA算法产生密钥的方式相类似。

有关ECDSA算法的Bouncy Castle实现如表9-3所示。

figure_0325_0077

Bouncy Castle未给出ECDSA算法的密钥长度及默认值信息,ECDSA算法密钥生成仅能通过算法材料的方式产生。这也是ECDSA数字签名算法与RSA和DSA数字签名算法在实现方面的主要差异。ECDSA算法密钥构建实现如代码清单9-7所示。

代码清单9-7 ECDSA数字签名算法生成密钥


import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

//省略

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

BigInteger p=new BigInteger

("883423532389192164791648750360308885314476597252960362792450860609699839");

ECFieldFp ecFieldFp=new ECFieldFp(p);

BigInteger a=new BigInteger

("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc",16);

BigInteger b=new BigInteger

("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a",16);

EllipticCurve ellipticCurve=new EllipticCurve(ecFieldFp, a,b);

BigInteger x=new BigInteger

("110282003749548856476348533541186204577905061504881242240149511594420911");

BigInteger y=new BigInteger

("869078407435509378747351873793058868500210384946040694651368759217025454");

ECPoint g=new ECPoint(x, y);

BigInteger n=new BigInteger

("883423532389192164791648750360308884807550341691627752275345424702807307");

ECParameterSpec ecParameterSpec=new ECParameterSpec(ellipticCurve, g,n,1);

//实例化密钥对生成器

KeyPairGenerator kpg=KeyPairGenerator.getInstance(KEY_ALGORITHM);

//初始化密钥对生成器

kpg.initialize(ecParameterSpec, new SecureRandom());


作者按照Bouncy Castle提供的资料构建了上述密钥生成代码,该代码主要是为了产生算法参数ECParameterSpec实例ecParameterSpec对象,即构建ECC算法材料。

很遗憾,Bouncy Castle提供的ECDSA算法未能提供自动化密钥生成实现,这可能跟ECC算法自身的实现难度较高有关。

本书旨在介绍Bouncy Castle对于数字签名算法的支持,有关ECC算法的Java API请读者朋友阅读相关文档。完整代码实现如代码清单9-8所示。

代码清单9-8 ECDSA算法实现


import java.math.BigInteger;

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.SecureRandom;

import java.security.Security;

import java.security.Signature;

import java.security.interfaces.ECPrivateKey;

import java.security.interfaces.ECPublicKey;

import java.security.spec.ECFieldFp;

import java.security.spec.ECParameterSpec;

import java.security.spec.ECPoint;

import java.security.spec.EllipticCurve;

import java.security.spec.PKCS8EncodedKeySpec;

import java.security.spec.X509EncodedKeySpec;

import java.util.HashMap;

import java.util.Map;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**

*ECDSA安全编码组件

*@author梁栋

*@version 1.0

*@since 1.0

*/

public abstract class ECDSACoder{

/**

*数字签名

*密钥算法

*/

private static final String KEY_ALGORITHM="ECDSA";

/**

*数字签名

*签名/验证算法

*Bouncy Castle支持以下7种算法

*NONEwithECDSA

*RIPEMD160withECDSA

*SHA1withECDSA

*SHA224withECDSA

*SHA256withECDSA

*SHA384withECDSA

*SHA512withECDSA

*/

private static final String SIGNATURE_ALGORITHM="SHA512withECDSA";

//公钥

private static final String PUBLIC_KEY="ECDSAPublicKey";

//私钥

private static final String PRIVATE_KEY="ECDSAPrivateKey";

/**

*初始化密钥

*@return Map密钥Map

*@throws Exception

*/

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

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

BigInteger p=new BigInteger

("883423532389192164791648750360308885314476597252960362792450860609699839");

ECFieldFp ecFieldFp=new ECFieldFp(p);

BigInteger a=new BigInteger

("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc",16);

BigInteger b=new BigInteger

(6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a",16);

EllipticCurve ellipticCurve=new EllipticCurve(ecFieldFp, a,b);

BigInteger x=new BigInteger

("1102820037495488564763485335411862045779050615048812422401495115944

20911");

BigInteger y=new BigInteger

("8690784074355093787473518737930588685002103849460406946513687592170

25454");

ECPoint g=new ECPoint(x, y);

BigInteger n=new BigInteger

("8834235323891921647916487503603088848075503416916277522753454247028

07307");

ECParameterSpec ecParameterSpec=new ECParameterSpec(ellipticCurve,

g, n,1);

//实例化密钥对生成器

KeyPairGenerator kpg=KeyPairGenerator.getInstance(KEY_ALGORITHM);

//初始化密钥对生成器

kpg.initialize(ecParameterSpec, new SecureRandom());

//生成密钥对

KeyPair keypair=kpg.generateKeyPair();

ECPublicKey publicKey=(ECPublicKey)keypair.getPublic();

ECPrivateKey privateKey=(ECPrivateKey)keypair.getPrivate();

//封装密钥

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

map.put(PUBLIC_KEY, publicKey);

map.put(PRIVATE_KEY, privateKey);

return map;

}

/**

*取得私钥

*@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();

}

/**

*签名

*@param data待签名数据

*@param privateKey私钥

*@return byte[]数字签名

*@throws Exception

*/

public static byte[]sign(byte[]data, byte[]privateKey)throws Exception{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//转换私钥材料

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{

//加入BouncyCastleProvider支持

Security.addProvider(new BouncyCastleProvider());

//转换公钥材料

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);

}

}


对于上述代码的校验与其他数字签名算法无异,如代码清单9-9所示。

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


import static org.junit.Assert.*;

import java.util.Map;

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

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

import org.junit.Before;

import org.junit.Test;

/**

*ECDSA数字签名校验

*@author梁栋

*@version 1.0

*/

public class ECDSACoderTest{

//公钥

private byte[]publicKey;

//私钥

private byte[]privateKey;

/**

*初始化密钥

*@throws Exception

*/

@Before

public void initKey()throws Exception{

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

publicKey=ECDSACoder.getPublicKey(keyMap);

privateKey=ECDSACoder.getPrivateKey(keyMap);

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

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

}

/**

*校验

*@throws Exception

*/

@Test

public void test()throws Exception{

String inputStr="ECDSA数字签名";

byte[]data=inputStr.getBytes();

//产生签名

byte[]sign=ECDSACoder.sign(data, privateKey);

System.err.println("签名:\r"+Hex.encodeHexString(sign));

//验证签名

boolean status=ECDSACoder.verify(data, publicKey, sign);

System.err.println("状态:\r"+status);

//验证

assertTrue(status);

}

}


ECDSA算法与DSA算法共同的特点在于公钥和密钥长度上的差异,ECDSA算法密钥同样是公钥长度略长于私钥,如下所示:


公钥:

MIIBITCB3gYHKoZIzj0CATCB0gIBATApBgcqhkjOPQEBAh5///////////////9///////+AAAAA

AAB///////8wQAQef///////////////f///////gAAAAAAAf//////8BB5rAWw73PGJQdDWVJIUdcpxqdsv

sn0dN3lhhcKULAoEPQQP+pY83KiBbMwzuGQr7fkFw9NYVz0/J/u9Ozy5qq996+jk6Qpdrm5AVMpTC6BGVLNoG

M4iazn8y3sC8a4CHn///////////////3///55emp9dkHH70VImiJCdCwIBAQM+AARVICaPidHhLwSf3Vu8URt

BaOleb6csh0cmWhBAqkp/hHGCIaN9iMbUyhxUrO3UyegLZvN6wGgBYK7cIy0=

私钥:

MIIBCwIBADCB3gYHKoZIzj0CATCB0gIBATApBgcqhkjOPQEBAh5///////////////9///////+AAAAAAAB///////8wQA

Qef///////////////f///////gAAAAAAAf//////8BB5rAWw73PGJQdDWVJIUdcpxqdsvsn0dN3lhhcKULAoEPQQP+pY83

KiBbMwzuGQr7fkFw9NYVz0/J/u9Ozy5qq996+jk6Qpdrm5AVMpTC6BGVLNoGM4iazn8y3sC8a4CHn///////////////3///55emp

9dkHH70VImiJCdCwIBAQQlMCMCAQEEHi4tPTGcWH8OxoPquDEuYTmmobCqO/GC696avIFEJw==


签名验证结果如下所示:

签名:

3040021e66913eccf0cb3ccb16b224ee6c53069ccea90a3da89d113c7d585bcedd35021e2707af2442e2d1

4ad68b364a0a068e39896daed5daed09df676b8e521e0c

状态:

true


作者根据Bouncy Castle提供的资料构建了ECDSA算法实现,其密钥的产生主要依赖于算法参数。ECDSA算法参数构建复杂,需要对算法相关的数学公式有一定的了解。上述签名长度是否与密钥长度或待签名信息有关,我们无从得知。