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所示。
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算法参数构建复杂,需要对算法相关的数学公式有一定的了解。上述签名长度是否与密钥长度或待签名信息有关,我们无从得知。