如何从 EC 公钥字节中获取 PublicKey 对象?

2022-01-10 00:00:00 cryptography bouncycastle java

我正在开发一个应用程序,该应用程序需要在 secp256r1(NIST P-256、P-256、prime256v1)公钥的帮助下验证 SHA256withECDSA签名.p>

公钥由不同的应用程序在较早的某个时间点生成,并以十六进制编码存储在我的数据库中.这里的十六进制字符串的格式等同于 OpenSSL 在对文件 x.pem 调用 openssl ec -in x.pem -noout -text 时生成的十六进制字符串之前由 openssl ecparam -genkey -name secp256r1 -out x.pem 生成.消息和签名是从不同的应用程序接收的.考虑以下测试数据:

//存储在数据库中byte[] pubKey = DatatypeConverter.parseHexBinary("049a55ad1e210cd113457ccd3465b930c9e7ade5e760ef64b63142dad43a308ed08e2d85632e8ff0322d3c7fda14409eafdc4c5b8ee0882fe885c92e//从其他应用程序接收字节[]消息= DatatypeConverter.parseHexBinary( 54686973206973206a75737420736f6d6520706f696e746c6573732064756d6d7920737472696e672e205468616e6b7320616e7977617920666f722074616b696e67207468652074696d6520746f206465636f6465206974203b2d29");字节[]签名= DatatypeConverter.parseHexBinary( 304402205fef461a4714a18a5ca6dce6d5ab8604f09f3899313a28ab430eb9860f8be9d602203c8d36446be85383af3f2e8630f40c4172543322b5e8973e03fff2309755e654");

现在这个应该是一个有效的签名.

我的目标是使用 Java 和/或 Bouncycastle 加密 API 验证消息上的签名.我为此创建了一个方法 isValidSignature:

private static boolean isValidSignature(byte[] pubKey, byte[] message,byte[] 签名)抛出 NoSuchAlgorithmException、NoSuchProviderException、InvalidKeyException、SignatureException、InvalidKeySpecException {签名 ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());ecdsaVerify.initVerify(getPublicKeyFromHex(pubKey));ecdsaVerify.update(消息);返回 ecdsaVerify.verify(签名);}

我已尝试提取公钥:

KeyFactory.generatePublic:

private static PublicKey getPublicKeyFromHex(byte[] pubKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {KeyFactory 事实 = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());return fact.generatePublic(new X509EncodedKeySpec(pubKey));}

但这会引发 java.security.spec.InvalidKeySpecException(DER 长度超过 4 个字节:26).我该怎么做才能解析这个?

解决方案

Bouncy Castle 椭圆曲线密钥对生成和密钥工厂示例代码让我非常接近.

一旦我设法为 secp256r1/NIST P-256/P-256/创建了 ECDSA 密钥工厂和曲线规范prime256v1 曲线 我能够使用 ECPointUtil.decodePoint 来获得曲线点.然后我可以生成一个公钥规范,使我能够生成这样的公钥:

private PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException {ECNamedCurveParameterSpec 规范 = ECNamedCurveTable.getParameterSpec("prime256v1");KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());ECNamedCurveSpec params = new ECNamedCurveSpec("prime256v1", spec.getCurve(), spec.getG(), spec.getN());ECPoint 点 = ECPointUtil.decodePoint(params.getCurve(), pubKey);ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);返回PK;}

I am developing an application that needs to validate SHA256withECDSAsignatures with the help of secp256r1 (NIST P-256, P-256, prime256v1) public keys.

The public keys are generated by a different application at some earlier point in time and stored in my database in hex encoding. The format of the hex string here is equivalent to the hex string OpenSSL would generate when calling openssl ec -in x.pem -noout -text on a file x.pem that has previously been generated by openssl ecparam -genkey -name secp256r1 -out x.pem. The message and signature are received from a different application. Consider the following test data:

// Stored in Database
byte[] pubKey = DatatypeConverter.parseHexBinary("049a55ad1e210cd113457ccd3465b930c9e7ade5e760ef64b63142dad43a308ed08e2d85632e8ff0322d3c7fda14409eafdc4c5b8ee0882fe885c92e3789c36a7a");

// Received from Other Application
byte[] message = DatatypeConverter.parseHexBinary("54686973206973206a75737420736f6d6520706f696e746c6573732064756d6d7920737472696e672e205468616e6b7320616e7977617920666f722074616b696e67207468652074696d6520746f206465636f6465206974203b2d29");
byte[] signature = DatatypeConverter.parseHexBinary("304402205fef461a4714a18a5ca6dce6d5ab8604f09f3899313a28ab430eb9860f8be9d602203c8d36446be85383af3f2e8630f40c4172543322b5e8973e03fff2309755e654");

Now this should be a valid signature.

My objective is to validate the signature over the message using the Java and/or Bouncycastle crypto API. I have created a method isValidSignaturefor that:

private static boolean isValidSignature(byte[] pubKey, byte[] message,
        byte[] signature) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException, InvalidKeySpecException {
    Signature ecdsaVerify = Signature.getInstance("SHA256withECDSA", new BouncyCastleProvider());
    ecdsaVerify.initVerify(getPublicKeyFromHex(pubKey));
    ecdsaVerify.update(message);
    return ecdsaVerify.verify(signature);
}

I have tried to extract the public key:

KeyFactory.generatePublic:

private static PublicKey getPublicKeyFromHex(byte[] pubKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
    KeyFactory fact = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
    return fact.generatePublic(new X509EncodedKeySpec(pubKey));
}

But this throws a java.security.spec.InvalidKeySpecException (DER length more than 4 bytes: 26). What can I do to parse this?

解决方案

The Bouncy Castle example code on elliptic curve key pair Generation and key factories got me pretty close.

Once I managed to create a ECDSA key factory and a curve specification for the secp256r1/NIST P-256/P-256/prime256v1 curve I was able to use ECPointUtil.decodePoint to obtain a curve point. I could then generate a public key specification that enabled me to generate a public key like this:

private PublicKey getPublicKeyFromBytes(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
    ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec("prime256v1");
    KeyFactory kf = KeyFactory.getInstance("ECDSA", new BouncyCastleProvider());
    ECNamedCurveSpec params = new ECNamedCurveSpec("prime256v1", spec.getCurve(), spec.getG(), spec.getN());
    ECPoint point =  ECPointUtil.decodePoint(params.getCurve(), pubKey);
    ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
    ECPublicKey pk = (ECPublicKey) kf.generatePublic(pubKeySpec);
    return pk;
}

相关文章