Java 带JCE的固定长度64字节EC P-256签名
我需要一个具有NIST p-256曲线的固定长度64字节ECDSA签名 实现必须使用JCE 下面的代码示例可以生成签名并对其进行验证Java 带JCE的固定长度64字节EC P-256签名,java,cryptography,jce,elliptic-curve,ecdsa,Java,Cryptography,Jce,Elliptic Curve,Ecdsa,我需要一个具有NIST p-256曲线的固定长度64字节ECDSA签名 实现必须使用JCE 下面的代码示例可以生成签名并对其进行验证 Provider provSign = new SunEC(); Provider provVerify = new SunEC(); // generate EC key KeyPairGenerator kg = KeyPairGenerator.getInstance("EC", provSign); ECGenParameter
Provider provSign = new SunEC();
Provider provVerify = new SunEC();
// generate EC key
KeyPairGenerator kg = KeyPairGenerator.getInstance("EC", provSign);
ECGenParameterSpec ecParam = new ECGenParameterSpec("secp256r1");
kg.initialize(ecParam);
KeyPair keyPair = kg.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
try
{
// export public key
KeyFactory kf = KeyFactory.getInstance("EC", provSign);
ECPublicKeySpec publicKeySpec = kf.getKeySpec(keyPair.getPublic(), ECPublicKeySpec.class);
// import public key into other provider
kf = KeyFactory.getInstance("EC", provVerify);
publicKey = (PublicKey)kf.generatePublic(publicKeySpec);
}
catch (InvalidKeySpecException ex)
{
ex.printStackTrace();
}
// do test
Signature sig = Signature.getInstance("SHA256withECDSA", provSign);
Signature ver = Signature.getInstance("SHA256withECDSA", provVerify);
byte[] data = new byte[64];
// sign
sig.initSign(privateKey);
sig.update(data);
byte [] sign = sig.sign();
// Working Signature verification
ver.initVerify(publicKey);
ver.update(data);
if (ver.verify(sign) == false)
{
throw new Exception("Signature Verification failed");
}
问题是这个符号是以某种方式编码的(我认为是DER格式),长度在70到72字节之间,但我需要一个64字节(未编码/原始)的签名
我所尝试的:
转换为固定长度64字节签名
DerInputStream derInputStream = new DerInputStream(sign);
DerValue[] values = derInputStream.getSequence(2);
byte[] random = values[0].getPositiveBigInteger().toByteArray();
byte[] signature = values[1].getPositiveBigInteger().toByteArray();
// r and s each occupy half the array
// Remove padding bytes
byte[] tokenSignature = new byte[64];
System.arraycopy(random, random.length > 32 ? 1 : 0, tokenSignature, random.length < 32 ? 1 : 0,
random.length > 32 ? 32 : random.length);
System.arraycopy(signature, signature.length > 32 ? 1 : 0, tokenSignature, signature.length < 32 ? 33 : 32,
signature.length > 32 ? 32 : signature.length);
System.out.println("Full Signature length: "+tokenSignature.length+" r length: "+random.length+" s length"+signature.length);
我已经用BouncyCastle ECDSASigner实现了64字节签名验证。但是我不能使用ECDSASigner,因为它没有扩展SignatureSpi,因此不能与JCE complient crypto server一起工作。多亏了@MaartenBodewes,我现在可以使用它了
//How to Check Signature
byte[] r = Arrays.copyOfRange(tokenSignature, 0,tokenSignature.length/2);
byte[] s = Arrays.copyOfRange(tokenSignature, tokenSignature.length/2,tokenSignature.length);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream);
ASN1EncodableVector v=new ASN1EncodableVector();
v.add(new ASN1Integer(new BigInteger(1,r)));
v.add(new ASN1Integer(new BigInteger(1,s)));
derOutputStream.writeObject(new DERSequence(v));
byte[] derSignature = byteArrayOutputStream.toByteArray();
ver.update(data);
if (ver.verify(derSignature) == false)
{
throw new Exception("Signature Verification failed");
}
它使用带有普通ECDSA的
SHA256而不是带有ECDSA的SHA256工作
请看下面的内容
使用DEROutputStream
将两个整数放在一个序列中?谢谢Maarten现在我得到了它。我已经用DEROutputStream尝试过,但第一次失败。@MaartenBodewes您可以将您的评论添加为答案:-)我今天会尝试,但如果没有,您可能希望添加您的代码作为答案。@MaartenBodewes好的,我添加了看起来像我的代码的答案。当然,我不会对签名大小(在copyOfRange
中)使用文字,但解决方案应该是正确的。快速问题:ASN1Integer的构造函数是否总是导致正整数?我还想先使用新的BigInteger(1,r),但签名总是正确的。(在for循环中测试了100次)比抱歉更安全。百万分之一的几率是10的9倍。我还打印了r的第一个字节,它适用于负数和正数。但我也认为这样更好,因此改变了答案。
//How to Check Signature
byte[] r = Arrays.copyOfRange(tokenSignature, 0,tokenSignature.length/2);
byte[] s = Arrays.copyOfRange(tokenSignature, tokenSignature.length/2,tokenSignature.length);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DEROutputStream derOutputStream = new DEROutputStream(byteArrayOutputStream);
ASN1EncodableVector v=new ASN1EncodableVector();
v.add(new ASN1Integer(new BigInteger(1,r)));
v.add(new ASN1Integer(new BigInteger(1,s)));
derOutputStream.writeObject(new DERSequence(v));
byte[] derSignature = byteArrayOutputStream.toByteArray();
ver.update(data);
if (ver.verify(derSignature) == false)
{
throw new Exception("Signature Verification failed");
}