I want to produce 384-bit elliptical curve signature with SHA-384, to generate the keys I run the steps below by looking at this SO Generate EC KeyPair from OpenSSL command line
openssl ecparam -name secp384r1 -genkey -noout -out d:/private.ec.key
openssl ec -in d:/private.pem -pubout -out d:/public.pem
openssl pkcs8 -topk8 -nocrypt -in d:/private.ec.key -out d:/private.pem
I pressed enter when I was asked for password (as I do not want to have password protected keys).
Then I downloaded bcprov-jdk15on-168.jar Bouncy Castle lib, for my JRE 14 and added it to my class path. And made small test application, copied the content of these keys directly into the java source code to simply things.
Made a simple stripping method to remove the 'text' part of the key and decode its base64 content:
private static byte[] bytesFromKeyStrings(String string) {
string = string.replaceAll("-----BEGIN PRIVATE KEY-----", "");
string = string.replaceAll("-----BEGIN EC PRIVATE KEY-----", "");
string = string.replaceAll("-----END EC PRIVATE KEY-----", "");
string = string.replaceAll("-----END PRIVATE KEY-----", "");
string = string.replaceAll("\r", "");
string = string.replaceAll("\n", "");
var bytes = Base64.getDecoder().decode(string);
return bytes;
}
Then from these bytes I produce a PrivateKey:
private static PrivateKey keyFromBytes(byte[] pkcs8key) throws NoSuchAlgorithmException, InvalidKeySpecException {
Security.addProvider(BOUNCY_CASTLE_PROVIDER);
var spec = ECNamedCurveTable.getParameterSpec("secp384r1");;
var ecPrivateKeySpec = new ECPrivateKeySpec(new BigInteger(1, pkcs8key), spec);
var factory = KeyFactory.getInstance("ECDSA", BOUNCY_CASTLE_PROVIDER);
return factory.generatePrivate(ecPrivateKeySpec);
}
But when I'm going to use the PrivateKey to sign the message (in this case array of zeros) then I get the exception:
var key = keyFromBytes(bytesFromKeyStrings(privatePem));
var ecdsaSign = Signature.getInstance("SHA384withECDSA", BOUNCY_CASTLE_PROVIDER);
ecdsaSign.initSign(key, new SecureRandom());
ecdsaSign.update(content);
var signature = ecdsaSign.sign();
Caused on the line:
ecdsaSign.initSign(key, new SecureRandom());
java.lang.IllegalArgumentException: Scalar is not in the interval [1, n - 1]
at org.bouncycastle.provider/org.bouncycastle.crypto.params.ECDomainParameters.validatePrivateScalar(ECDomainParameters.java:146)
at org.bouncycastle.provider/org.bouncycastle.crypto.params.ECPrivateKeyParameters.<init>(ECPrivateKeyParameters.java:16)
at org.bouncycastle.provider/org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil.generatePrivateKeyParameter(ECUtil.java:245)
at org.bouncycastle.provider/org.bouncycastle.jcajce.provider.asymmetric.ec.SignatureSpi.engineInitSign(Unknown Source)
at java.base/java.security.SignatureSpi.engineInitSign(SignatureSpi.java:131)
at java.base/java.security.Signature$Delegate.engineInitSign(Signature.java:1364)
at java.base/java.security.Signature.initSign(Signature.java:658)
at ecdsa/ecdsa.Main.main(Main.java:75)
If I understand ECDSA correctly that we need to multiply the private key by some random value, so I'm providing it secureRandom. What is strange that my previous attempt when I was not trying to read the keys and generated them them on the fly with KeyPairGenerator.getInstance("EC") (without bouncycastle library) then these generated keys can be used to sign the message without a exception and I verified it separately that produced signature was correct.
Do I produce the keys badly, or is there some other way to provide the multiplier constant so then it is not trying to sign directly with the private key and giving me the exception?
Or am I missing something silly?
Below is full listing of my test application which can reproduce the exception:
package ecdsa;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
public class Main {
private final static String privatePem = "-----BEGIN PRIVATE KEY-----\r\n"
+ "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBBdsy5smSA2+DvnIdx\r\n"
+ "bqq6GwwHadEdtHqcKO9N8hB0/NKvFOCmHBSfBpYPgCWlybGhZANiAAQ0iyw2wPjs\r\n"
+ "zhale0mPkiCCTzcNzTW1g7zqUoN3XNuZbaK1FHhVVMsywUaS6MSUJI6r1QcUziMm\r\n"
+ "MWGYZUSSq4noniTDt8INj/ElndKhbh3oSCq3j1oKK9cYXi3uGuDFXks=\r\n"
+ "-----END PRIVATE KEY-----\r\n";
private final static String privateEc = "-----BEGIN EC PRIVATE KEY-----\r\n"
+ "MIGkAgEBBDBBdsy5smSA2+DvnIdxbqq6GwwHadEdtHqcKO9N8hB0/NKvFOCmHBSf\r\n"
+ "BpYPgCWlybGgBwYFK4EEACKhZANiAAQ0iyw2wPjszhale0mPkiCCTzcNzTW1g7zq\r\n"
+ "UoN3XNuZbaK1FHhVVMsywUaS6MSUJI6r1QcUziMmMWGYZUSSq4noniTDt8INj/El\r\n"
+ "ndKhbh3oSCq3j1oKK9cYXi3uGuDFXks=\r\n"
+ "-----END EC PRIVATE KEY-----\r\n";
private static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();
private static byte[] bytesFromKeyStrings(String string) {
string = string.replaceAll("-----BEGIN PRIVATE KEY-----", "");
string = string.replaceAll("-----BEGIN EC PRIVATE KEY-----", "");
string = string.replaceAll("-----END EC PRIVATE KEY-----", "");
string = string.replaceAll("-----END PRIVATE KEY-----", "");
string = string.replaceAll("\r", "");
string = string.replaceAll("\n", "");
var bytes = Base64.getDecoder().decode(string);
return bytes;
}
private static PrivateKey keyFromBytes(byte[] pkcs8key) throws NoSuchAlgorithmException, InvalidKeySpecException {
Security.addProvider(BOUNCY_CASTLE_PROVIDER);
var spec = ECNamedCurveTable.getParameterSpec("secp384r1");;
var ecPrivateKeySpec = new ECPrivateKeySpec(new BigInteger(1, pkcs8key), spec);
var factory = KeyFactory.getInstance("ECDSA", BOUNCY_CASTLE_PROVIDER);
return factory.generatePrivate(ecPrivateKeySpec);
}
public static void main(String[] args) {
var content = new byte[100];
try {
var key = keyFromBytes(bytesFromKeyStrings(privatePem));
var ecdsaSign = Signature.getInstance("SHA384withECDSA", BOUNCY_CASTLE_PROVIDER);
ecdsaSign.initSign(key, new SecureRandom());
ecdsaSign.update(content);
var signature = ecdsaSign.sign();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Edit: Updated the example (previously I was using encrypted private key)