3

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)

Anton Krug
  • 1,555
  • 2
  • 19
  • 32

3 Answers3

3

Why it didn't work

All key formats supported by OpenSSL are not just the raw key, but include some metadata; in particular all EC keys are meaningul and usable only with regard to a particular curve, which is included in the key file. The two formats produced by openssl pkcs8 -topk8 [-nocrypt] are the unencrypted and encrypted formats defined in PKCS8, more conveniently available as RFC5208, which you could easily find for example in wikipedia. The use of these formats in PEM types 'BEGIN/END PRIVATE KEY' and 'BEGIN/END ENCRYPTED PRIVATE KEY' are formalized in RFC7468 section 10 and section 11 although OpenSSL had made them defacto standards decades earlier.

Michael Fehr's first answer shows you the breakdown of the encrypted form, but even the unencrypted form still contains metadata identifying this as an EC algorithm using the secp384r1 aka P-384 curve:

$ cat 65740255.clr
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBBdsy5smSA2+DvnIdx
bqq6GwwHadEdtHqcKO9N8hB0/NKvFOCmHBSfBpYPgCWlybGhZANiAAQ0iyw2wPjs
zhale0mPkiCCTzcNzTW1g7zqUoN3XNuZbaK1FHhVVMsywUaS6MSUJI6r1QcUziMm
MWGYZUSSq4noniTDt8INj/ElndKhbh3oSCq3j1oKK9cYXi3uGuDFXks=
-----END PRIVATE KEY-----
$ openssl asn1parse <65740255.clr -i
    0:d=0  hl=3 l= 182 cons: SEQUENCE
    3:d=1  hl=2 l=   1 prim:  INTEGER           :00
    6:d=1  hl=2 l=  16 cons:  SEQUENCE
    8:d=2  hl=2 l=   7 prim:   OBJECT            :id-ecPublicKey
   17:d=2  hl=2 l=   5 prim:   OBJECT            :secp384r1
   24:d=1  hl=3 l= 158 prim:  OCTET STRING      [HEX DUMP]:30819B02010104304176CCB9B26480DBE0EF9C87716EAABA1B0C0769D11DB47A9C28EF4DF21074FCD2AF14E0A61C149F06960F8025A5C9B1A16403620004348B2C36C0F8ECCE16A57B498F9220824F370DCD35B583BCEA5283775CDB996DA2B514785554CB32C14692E8C494248EABD50714CE2326316198654492AB89E89E24C3B7C20D8FF1259DD2A16E1DE8482AB78F5A0A2BD7185E2DEE1AE0C55E4B

It is apparent to anyone familiar with ASN.1 that the last field in the PKCS8 unencrypted, which is the algorithm-dependent encoding, is itself an ASN.1 structure. RFC5915 specifies the ASN.1 structure of an X9-type EC privatekey (but not newer EC algorithms like Bernstein et al's EdDSA) based on the 'SEC1' document. Parsing that we see it actually contains both the actual privatekey (i.e. the number usually notated d but in Java S) and the publickey (a point usually notated Q but in Java W, in X9 uncompressed format) (which is technically redundant because it can be recomputed from the privatekey and the curve equations):

$ openssl asn1parse <65740255.clr -i -strparse 27 -dump
    0:d=0  hl=3 l= 155 cons: SEQUENCE
    3:d=1  hl=2 l=   1 prim:  INTEGER           :01
    6:d=1  hl=2 l=  48 prim:  OCTET STRING
      0000 - 41 76 cc b9 b2 64 80 db-e0 ef 9c 87 71 6e aa ba   Av...d......qn..
      0010 - 1b 0c 07 69 d1 1d b4 7a-9c 28 ef 4d f2 10 74 fc   ...i...z.(.M..t.
      0020 - d2 af 14 e0 a6 1c 14 9f-06 96 0f 80 25 a5 c9 b1   ............%...
   56:d=1  hl=2 l= 100 cons:  cont [ 1 ]
   58:d=2  hl=2 l=  98 prim:   BIT STRING
      0000 - 00 04 34 8b 2c 36 c0 f8-ec ce 16 a5 7b 49 8f 92   ..4.,6......{I..
      0010 - 20 82 4f 37 0d cd 35 b5-83 bc ea 52 83 77 5c db    .O7..5....R.w\.
      0020 - 99 6d a2 b5 14 78 55 54-cb 32 c1 46 92 e8 c4 94   .m...xUT.2.F....
      0030 - 24 8e ab d5 07 14 ce 23-26 31 61 98 65 44 92 ab   $......#&1a.eD..
      0040 - 89 e8 9e 24 c3 b7 c2 0d-8f f1 25 9d d2 a1 6e 1d   ...$......%...n.
      0050 - e8 48 2a b7 8f 5a 0a 2b-d7 18 5e 2d ee 1a e0 c5   .H*..Z.+..^-....
      0060 - 5e 4b                                             ^K

Another option

If you use the BouncyCastle bcpkix jar in addition to bcprov, it provides routines that directly read (and write) all OpenSSL PEM formats, including both forms of PKCS8 and OpenSSL's 'traditional' or 'legacy' algorithm-specific format BEGIN/END EC PRIVATE KEY which is just SEC1 in PEM wrapping. See e.g.:
Read RSA private key of format PKCS1 in JAVA
Get a PrivateKey from a RSA .pem file
Reading elliptic curve private key from file with BouncyCastle
How to Load RSA Private Key From File
(although some of those are for RSA, the issues are the same as for EC).

    String pk8e = "-----BEGIN ENCRYPTED PRIVATE KEY-----\r\n"
            + "MIIBHDBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIG5SFWXEmy8QCAggA\r\n"
            + "MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBBu6HN0VZ92vDYuKa5aLoD8BIHA\r\n"
            + "UK8AbLTo5C2ZkhVfnXSfNefUup2Il1xwF0FXrA2WkJ/d+fokLijgkTyal43t8iMJ\r\n"
            + "ne3gOjYP8Q558x+4k4TAMWug0nDh9RPINMqABnhPkf5wyCCYI2RN2b3C4SZCBMQw\r\n"
            + "r+cwVgwiShhPHcuUM9BmFlt9eCr3kuFnfMxlvvpR482kIT+Q6+hZsyUL/oJjOVC9\r\n"
            + "R2S7AwiKH4BPj5TBjMKF4ZI5cS0DMXn1q3h21AUdMPUchX6itcDTfWvEIfTwpUr0\r\n"
            + "-----END ENCRYPTED PRIVATE KEY-----\r\n";
    String pk8u = "-----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";
    String trad = "-----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";
    
    @SuppressWarnings("resource") // should use try-resources or similar
    Object o1 = new PEMParser (new StringReader (pk8e)).readObject();
    PrivateKey k1 = new JcaPEMKeyConverter().getPrivateKey(
            ((org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo)o1).decryptPrivateKeyInfo(
                    new JceOpenSSLPKCS8DecryptorProviderBuilder().build(/*password*/"".toCharArray()) ));
    System.out.println ( ((java.security.interfaces.ECPrivateKey)k1).getS() .toString(16));
    
    @SuppressWarnings("resource") // should use try-resources or similar
    Object o2 = new PEMParser (new StringReader (pk8u)).readObject();
    PrivateKey k2 = new JcaPEMKeyConverter().getPrivateKey((PrivateKeyInfo)o2);
    System.out.println ( ((java.security.interfaces.ECPrivateKey)k2).getS() .toString(16));
    
    @SuppressWarnings("resource") // should use try-resources or similar
    Object o3 = new PEMParser (new StringReader (trad)).readObject();
    PrivateKey k3 = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)o3).getPrivate();
    System.out.println ( ((java.security.interfaces.ECPrivateKey)k3).getS() .toString(16));

->
4176ccb9b26480dbe0ef9c87716eaaba1b0c0769d11db47a9c28ef4df21074fcd2af14e0a61c149f06960f8025a5c9b1
4176ccb9b26480dbe0ef9c87716eaaba1b0c0769d11db47a9c28ef4df21074fcd2af14e0a61c149f06960f8025a5c9b1
4176ccb9b26480dbe0ef9c87716eaaba1b0c0769d11db47a9c28ef4df21074fcd2af14e0a61c149f06960f8025a5c9b1

and that (hex) number is the 'raw' privatekey, which is the value you could put (as a positive BigInteger) in ECPrivateKeySpec for the method you posted. (I had to make some names package-qualified because in my dev environment I have conflicting imports; you probably won't need that.)

Community
  • 1
  • 1
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
  • "can be recomputed from the privatekey and the curve equations" doesn't it require to know the scalar multiplier as well? Or using the different random number as multiplier, then we would get different publickey, but we wouldn't 'recompute' the same one. – Anton Krug Jan 18 '21 at 15:19
  • Thank you very much, followed your advice and added bcpkix as well and let the library to parse the keys. Should I make it as a new question so I can accept your answer too? – Anton Krug Jan 18 '21 at 17:22
  • 1
    1: the publickey is _defined_ as the scalar multiplication of the privatekey (d) times the base point (G) (which is part of the curve definition -- I should have said definition rather than just equations) i.e. Q=dG. 2: don't bother; I think it makes more sense to have related though different answers to one question together rather than requiring readers to flip among multiple pages, and I don't need more points – dave_thompson_085 Jan 19 '21 at 07:50
2

According to https://lapo.it/asn1js/ your private key in PEM encoding is still an encrypted one - directly visible with "-----BEGIN ENCRYPTED PRIVATE KEY-----":

SEQUENCE (2 elem)
  SEQUENCE (2 elem)
    OBJECT IDENTIFIER 1.2.840.113549.1.5.13 pkcs5PBES2 (PKCS #5 v2.0)
    SEQUENCE (2 elem)
      SEQUENCE (2 elem)
        OBJECT IDENTIFIER 1.2.840.113549.1.5.12 pkcs5PBKDF2 (PKCS #5 v2.0)
        SEQUENCE (3 elem)
          OCTET STRING (8 byte) 1B9485597126CBC4
          INTEGER 2048
          SEQUENCE (2 elem)
            OBJECT IDENTIFIER 1.2.840.113549.2.9 hmacWithSHA256 (RSADSI digestAlgorithm)
            NULL
      SEQUENCE (2 elem)
        OBJECT IDENTIFIER 2.16.840.1.101.3.4.1.42 aes256-CBC (NIST Algorithm)
        OCTET STRING (16 byte) 6EE87374559F76BC362E29AE5A2E80FC
  OCTET STRING (192 byte) 50AF006CB4E8E42D9992155F9D749F35E7D4BA9D88975C70174157AC0D96909FDDF9F…

To get a working and unencrypted file use this OpenSSL command to convert the encrypted file to an unencrypted file:

openssl pkcs8 -topk8 -nocrypt -in ecprivateencrypted.pem -out ecprivateunencrypted.pem 

I tried to convert the file on my own but my OpenSSL denies this because the passphrase length (just hit enter) is too short.

Michael Fehr
  • 5,827
  • 2
  • 19
  • 40
  • Thank you very much.I had same error like you trying to convert it, but then I have still the first EC file so I have run openssl with it (just added -nocrypt). Got this key instead (which looks much better). -----BEGIN PRIVATE KEY----- MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBBdsy5smSA2+DvnIdx bqq6GwwHadEdtHqcKO9N8hB0/NKvFOCmHBSfBpYPgCWlybGhZANiAAQ0iyw2wPjs zhale0mPkiCCTzcNzTW1g7zqUoN3XNuZbaK1FHhVVMsywUaS6MSUJI6r1QcUziMm MWGYZUSSq4noniTDt8INj/ElndKhbh3oSCq3j1oKK9cYXi3uGuDFXks= -----END PRIVATE KEY----- But still getting the same exception – Anton Krug Jan 15 '21 at 19:56
  • Updated the question to not contain encrypted key anymore. Is there something else which looks suspicious? – Anton Krug Jan 15 '21 at 20:03
  • 2
    FYI `openssl pkey` will not accept a zero-length password, but the 'old-style' (pre-1.0) subcommands `pkcs8` (without `-topk8`) and `ec` will – dave_thompson_085 Jan 16 '21 at 00:26
2

I'm answering in a second answer as my first answer does not fit to the question any longer because you edited your question and changed the private key pem to unencrypted data.

As your key is the encoded form of a private key it has a PKCS#8 structure, so any reader is been able to identify the kind of key and the underlying curve so you don't need to build it with ECPrivateKeySpec. There is a more simple way using this code:

KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pkcs8key);
return keyFactory.generatePrivate(privateKeySpec);

Same is for the public key, just use the X509EncodedKeySpec (see full code example).

I appended your bytesFromKeyStrings-function to convert the key data for public keys as well into a byte array.

Below is a full sample code that read the private key pem, signs the message, read the public key pem (derived from the private key) and verify the signature with a "true" as expected result.

output:

signatureVerified: true

Security warning: the following code has no exception handling and is for educational purpose only:

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class MainSo {

    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";
/*
Key Details:
   Type: EC
   Size (bits): 384
   Curve Name: secp384r1
   Curve OID: 1.3.132.0.34
   Public key (x):
348b2c36c0f8ecce16a57b498f9220824f370dcd35b583bcea5283775cdb996d
a2b514785554cb32c14692e8c494248e
   Public key (y):
abd50714ce2326316198654492ab89e89e24c3b7c20d8ff1259dd2a16e1de848
2ab78f5a0a2bd7185e2dee1ae0c55e4b
   Private key (d):
4176ccb9b26480dbe0ef9c87716eaaba1b0c0769d11db47a9c28ef4df21074fc
d2af14e0a61c149f06960f8025a5c9b1

Public Key in PEM Format:
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAENIssNsD47M4WpXtJj5Iggk83Dc01tYO8
6lKDd1zbmW2itRR4VVTLMsFGkujElCSOq9UHFM4jJjFhmGVEkquJ6J4kw7fCDY/x
JZ3SoW4d6Egqt49aCivXGF4t7hrgxV5L
-----END PUBLIC KEY-----
 */

    private final static String publicPem = "-----BEGIN PUBLIC KEY-----\n" +
            "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAENIssNsD47M4WpXtJj5Iggk83Dc01tYO8\n" +
            "6lKDd1zbmW2itRR4VVTLMsFGkujElCSOq9UHFM4jJjFhmGVEkquJ6J4kw7fCDY/x\n" +
            "JZ3SoW4d6Egqt49aCivXGF4t7hrgxV5L\n" +
            "-----END PUBLIC KEY-----";

    private static final BouncyCastleProvider BOUNCY_CASTLE_PROVIDER = new BouncyCastleProvider();

    private static byte[] bytesFromKeyStrings(String string) {
        string = string.replaceAll("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
        string = string.replaceAll("-----BEGIN PRIVATE KEY-----", "");
        string = string.replaceAll("-----BEGIN PUBLIC KEY-----", "");
        string = string.replaceAll("-----BEGIN EC PRIVATE KEY-----", "");
        string = string.replaceAll("-----END EC PRIVATE KEY-----", "");
        string = string.replaceAll("-----END ENCRYPTED PRIVATE KEY-----", "");
        string = string.replaceAll("-----END PRIVATE KEY-----", "");
        string = string.replaceAll("-----END PUBLIC KEY-----", "");
        string = string.replaceAll("\r", "");
        string = string.replaceAll("\n", "");
        return Base64.getDecoder().decode(string);
    }

    private static PrivateKey privateKeyFromBytes(byte[] pkcs8key) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
        Security.addProvider(BOUNCY_CASTLE_PROVIDER);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pkcs8key);
        return keyFactory.generatePrivate(privateKeySpec);
    }

    private static PublicKey publicKeyFromBytes(byte[] x509key) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
        Security.addProvider(BOUNCY_CASTLE_PROVIDER);
        KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(x509key);
        return keyFactory.generatePublic(publicKeySpec);
    }

    public static void main(String[] args) {
        System.out.println("https://stackoverflow.com/questions/65740255/scalar-is-not-in-the-interval-1-n-1-exception-when-signing-ecdsa-with-sha/65742506?noredirect=1#comment116237430_65742506");
        var content = new byte[100];
        try {
            // signature with private key
            var privateKey = privateKeyFromBytes(bytesFromKeyStrings(privatePem));
            var ecdsaSign = Signature.getInstance("SHA384withECDSA", BOUNCY_CASTLE_PROVIDER);
            ecdsaSign.initSign(privateKey, new SecureRandom());
            ecdsaSign.update(content);
            var signature = ecdsaSign.sign();

            // verification with the public key
            var publicKey = publicKeyFromBytes(bytesFromKeyStrings(publicPem));
            var ecdsaVerify = Signature.getInstance("SHA384withECDSA", BOUNCY_CASTLE_PROVIDER);
            ecdsaVerify.initVerify(publicKey);
            ecdsaVerify.update(content);
            boolean signatureVerified = ecdsaVerify.verify(signature);
            System.out.println("signatureVerified: " + signatureVerified);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Michael Fehr
  • 5,827
  • 2
  • 19
  • 40