java.security.InvalidAlgorithmParameterException 在 Java 8 但在 Java 11 上有效
java.security.InvalidAlgorithmParameterException on Java 8 but works on Java 11
因此,假设您想使用 public 密钥验证签名,该密钥使用 ECDSA 算法和 brainpoolP256r1 椭圆曲线。
对于运行下面的代码,请执行以下步骤:
- 使用这些命令生成 brainpool 密钥对:
openssl ecparam -name brainpoolP256r1 -genkey -noout -out ec-brainpoolP256r1-priv-key.pem
openssl ec -in ec-brainpoolP256r1-priv-key.pem -pubout > ec-brainpoolP256r1-pub-key.pem
openssl pkcs8 -topk8 -nocrypt -in ec-brainpoolP256r1-priv-key.pem -out ec-brainpoolP256r1-priv-key-pkcs8.pem (to read it with tomitribe PEM utility, we need the PKCS8 format)
- 运行 使用您生成的密钥后的以下代码:
package com.test.ecdsa;
import org.tomitribe.auth.signatures.PEM;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
public class Main {
public static void main(String[] args) throws Exception {
String privateKeyPem = "[use here the content from ec-brainpoolP256r1-priv-key-pkcs8.pem]";
Signature signer = Signature.getInstance("SHA256withECDSA");
PrivateKey privateKey = PEM.readPrivateKey(new ByteArrayInputStream(privateKeyPem.getBytes(StandardCharsets.UTF_8)));
signer.initSign(privateKey);
signer.update("testMessage".getBytes(StandardCharsets.UTF_8));
byte[] signature = signer.sign();
String publicKeyPem = "[use here the content from ec-brainpoolP256r1-pub-key.pem]";
PublicKey publicKey = PEM.readPublicKey(new ByteArrayInputStream(publicKeyPem.getBytes(StandardCharsets.UTF_8)));
Signature signatureVerifier = Signature.getInstance("SHA256withECDSA");
signatureVerifier.initVerify(publicKey);
signatureVerifier.update("testMessage".getBytes(StandardCharsets.UTF_8));
boolean result = signatureVerifier.verify(signature);
System.out.println(result);
}
}
运行在 java 8 上使用此代码将导致:
Exception in thread "main" java.security.SignatureException: Could not sign data
at sun.security.ec.ECDSASignature.signDigestNative(ECDSASignature.java:367)
at sun.security.ec.ECDSASignature.engineSign(ECDSASignature.java:386)
at java.security.Signature$Delegate.engineSign(Signature.java:1382)
at java.security.Signature.sign(Signature.java:698)
at com.ing.obp.jws.lib.Main.main(Main.java:29)
Caused by: java.security.InvalidAlgorithmParameterException
at sun.security.ec.ECDSASignature.signDigest(Native Method)
at sun.security.ec.ECDSASignature.signDigestNative(ECDSASignature.java:364)
... 4 more
使用 java 11 控制台将打印“true”。
这是为什么?
好吧,在 java 11 中添加了 Brainpool 和其他 ECC 支持(自 java 7 后不可用,我发现这个 link 非常有趣:https://bugs.openjdk.java.net/browse/JDK-7007966)
如果您仍想使用 java 8 来读取密钥、签名、验证签名,那么您将需要 BouncyCastleProvider。
Maven 依赖项:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
</dependency>
要使用它,您可以将提供程序注册到 java 的安全层,然后使用 BouncyCastleProvider.PROVIDER_NAME 在 Signature.getInstance 中引用它。
像这样:
...
import org.bouncycastle.jce.provider.BouncyCastleProvider;
...
public class Main {
static {
Security.addProvider(new BouncyCastleProvider());
}
...
Signature signer = Signature.getInstance("SHA256withECDSA", BouncyCastleProvider.PROVIDER_NAME);
...
Signature signatureVerifier = Signature.getInstance("SHA256withECDSA", BouncyCastleProvider.PROVIDER_NAME);
...
}
因此,假设您想使用 public 密钥验证签名,该密钥使用 ECDSA 算法和 brainpoolP256r1 椭圆曲线。
对于运行下面的代码,请执行以下步骤:
- 使用这些命令生成 brainpool 密钥对:
openssl ecparam -name brainpoolP256r1 -genkey -noout -out ec-brainpoolP256r1-priv-key.pem
openssl ec -in ec-brainpoolP256r1-priv-key.pem -pubout > ec-brainpoolP256r1-pub-key.pem
openssl pkcs8 -topk8 -nocrypt -in ec-brainpoolP256r1-priv-key.pem -out ec-brainpoolP256r1-priv-key-pkcs8.pem (to read it with tomitribe PEM utility, we need the PKCS8 format)
- 运行 使用您生成的密钥后的以下代码:
package com.test.ecdsa;
import org.tomitribe.auth.signatures.PEM;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
public class Main {
public static void main(String[] args) throws Exception {
String privateKeyPem = "[use here the content from ec-brainpoolP256r1-priv-key-pkcs8.pem]";
Signature signer = Signature.getInstance("SHA256withECDSA");
PrivateKey privateKey = PEM.readPrivateKey(new ByteArrayInputStream(privateKeyPem.getBytes(StandardCharsets.UTF_8)));
signer.initSign(privateKey);
signer.update("testMessage".getBytes(StandardCharsets.UTF_8));
byte[] signature = signer.sign();
String publicKeyPem = "[use here the content from ec-brainpoolP256r1-pub-key.pem]";
PublicKey publicKey = PEM.readPublicKey(new ByteArrayInputStream(publicKeyPem.getBytes(StandardCharsets.UTF_8)));
Signature signatureVerifier = Signature.getInstance("SHA256withECDSA");
signatureVerifier.initVerify(publicKey);
signatureVerifier.update("testMessage".getBytes(StandardCharsets.UTF_8));
boolean result = signatureVerifier.verify(signature);
System.out.println(result);
}
}
运行在 java 8 上使用此代码将导致:
Exception in thread "main" java.security.SignatureException: Could not sign data
at sun.security.ec.ECDSASignature.signDigestNative(ECDSASignature.java:367)
at sun.security.ec.ECDSASignature.engineSign(ECDSASignature.java:386)
at java.security.Signature$Delegate.engineSign(Signature.java:1382)
at java.security.Signature.sign(Signature.java:698)
at com.ing.obp.jws.lib.Main.main(Main.java:29)
Caused by: java.security.InvalidAlgorithmParameterException
at sun.security.ec.ECDSASignature.signDigest(Native Method)
at sun.security.ec.ECDSASignature.signDigestNative(ECDSASignature.java:364)
... 4 more
使用 java 11 控制台将打印“true”。
这是为什么?
好吧,在 java 11 中添加了 Brainpool 和其他 ECC 支持(自 java 7 后不可用,我发现这个 link 非常有趣:https://bugs.openjdk.java.net/browse/JDK-7007966)
如果您仍想使用 java 8 来读取密钥、签名、验证签名,那么您将需要 BouncyCastleProvider。
Maven 依赖项:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
</dependency>
要使用它,您可以将提供程序注册到 java 的安全层,然后使用 BouncyCastleProvider.PROVIDER_NAME 在 Signature.getInstance 中引用它。
像这样:
...
import org.bouncycastle.jce.provider.BouncyCastleProvider;
...
public class Main {
static {
Security.addProvider(new BouncyCastleProvider());
}
...
Signature signer = Signature.getInstance("SHA256withECDSA", BouncyCastleProvider.PROVIDER_NAME);
...
Signature signatureVerifier = Signature.getInstance("SHA256withECDSA", BouncyCastleProvider.PROVIDER_NAME);
...
}