0

I want to send a request to google FCM to send a push request to the Browser to show notification.

The main goal is to sign the JWT payload with private key using the ECDH algorithm with SHA256 to get a JWT token.

I try to sign a token with ECDH algorithem but everytime I get a deffirent signature for the same payload.

This is a snap of my code

Thanks in advance

    JwtClaims claims = new JwtClaims();
    claims.setAudience("https://fcm.googleapis.com");
    claims.setExpirationTime(NumericDate.fromSeconds(1560388318));
    claims.setSubject("mailto:admin@example.com");

    JsonWebSignature jws = new JsonWebSignature();
    jws.setHeader("typ", "JWT");
    jws.setHeader("alg", "ES256");
    jws.setPayload(claims.toJson());
    try {
        Key key = loadPrivateKey("-kmhPYsH6JKiFjG8C1cS9vx4bCz594yofAwTLa_SOEE");
        jws.setKey(key);
    } catch (NoSuchProviderException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    }
    jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256);

    try {
        System.out.println(jws.getCompactSerialization());
    } catch (JoseException e) {
        e.printStackTrace();
    }

.

 public static PrivateKey loadPrivateKey(String encodedPrivateKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
    byte[] decodedPrivateKey = base64Decode(encodedPrivateKey);

    // prime256v1 is NIST P-256
    ECParameterSpec params = ECNamedCurveTable.getParameterSpec("prime256v1");
    ECPrivateKeySpec prvkey = new ECPrivateKeySpec(new BigInteger(decodedPrivateKey), params);
    KeyFactory kf = KeyFactory.getInstance("ECDH");

    return kf.generatePrivate(prvkey);
   }

.

public static byte[] base64Decode(String base64Encoded) {
    if (base64Encoded.contains("+") || base64Encoded.contains("/")) {
        return BaseEncoding.base64().decode(base64Encoded);
    } else {
        return BaseEncoding.base64Url().decode(base64Encoded);
    }
}

first try I got :

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTU2MDM4ODMxOCwic3ViIjoibWFpbHRvOmFkbWluQGV4YW1wbGUuY29tIn0.MpGc0pKvXtDb94Ruq5lkQjqCqxFMkVAwzVervnH90RLArvGHUAZ_kO4VcecLhGfIXTCitBKb5M-EKsYR35IT0A

The seconde time I got :

eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL2ZjbS5nb29nbGVhcGlzLmNvbSIsImV4cCI6MTU2MDM4ODMxOCwic3ViIjoibWFpbHRvOmFkbWluQGV4YW1wbGUuY29tIn0.qEW0ci1BnXXUUjkfsQkdReznAyIDEPtygxV3B58Sl8v_gTlh8O4HHGzRtxsqdvL5UIJV06e_UJHYPOUDK_MF9A

Ridae HAMDANI
  • 686
  • 2
  • 7
  • 17
  • You are not using ECDH at all, you are using ECDSA, which is signature and not encryption. These are both elliptic-curve algorithms, but they are entirely different algorithms. They do however use the same _keys_ (really keypairs), which is why your KeyFactory for ECDH worked. – dave_thompson_085 Jun 13 '19 at 14:34

2 Answers2

1

By design, a digital cryptographic signature should only fulfill the following condition

It should be verifiable by the corresponding public key.

If you look at the Signature Generation algorithm Step 3, it says Select a cryptographically secure random integer k from [1,n-1]. , where n is the order of the curve (you can ignore this fact for now).

Then calculate (x1, y1) = k * G, where G is the generator point of the Elliptic curve. Then, r = x1 mod n. This r is a part of the signature. So, by changing k, r will also change, hence the signature changes.

So, for every signature generation, the algorithm selects a different parameter and then uses is to calculate the signature. eg:

>>> from ecc import curves
>>> curve = curves.P256()
>>> pkey =  0x00c3f7c39a9be2418cd89a732e40d648b09fa0af9e909a4fb6864910144b5cbcdf
>>> s1 = c.sign(b'Hello', pkey)
(37527198291707833181859423619289327687028014812888685671525882103189540525356,7717531609084222009133798505588038563850333231389727023073200992747312618427)
>>> s2 = c.sign(b'Hello', pkey)
(55880701658034823360120047989457771316451459626784083177171213563603884569397,88917360761747520665103257272757357544674490240888454865713640275762122369837)
>>> s1 == s2
False

Each time the signature is different.

ref:

prateeknischal
  • 752
  • 4
  • 12
  • but will the public key verifie the cipherText without the random integer ? It is a stupid question but could we do it wihout the random integer ? Thanks in advance mate – Ridae HAMDANI Jun 13 '19 at 08:46
  • 1
    @Ridae: the random k for ECDSA (or DSA) is effectively encoded in the r portion of the signature, which the verifier uses. If you generate ECDSA (or DSA) signatures with a fixed or repeated k they will verify, but any adversary who sees two such signatures on different data can compute your privatekey and then forge 'your' signatures on any data they want, which makes your signatures completely worthless. This was a problem in Bitcoin where some bugs caused duplicate-k signatures and people lost lots of their money. For _multiple_ Qs on this see crypto.SX security.SX bitcoin.SX . – dave_thompson_085 Jun 13 '19 at 14:40
  • 1
    Yes, that's the whole idea.. you don't need to know to the random number.. you just need to verify if the sequence of calculations is valid. you can refer the "ECDSA and a small proof" article to see how the verification works.. it's a bit too much if you haven't had some experience with ecc internals.. but you can get a feel of it. And, like others said, you should not use a predictable random number since.. having same k can leak the private key.. that doc mentioned in the references also has a proof as to how it will leak the private key. – prateeknischal Jun 13 '19 at 18:07
0

ECDH encryption algorithm use a different Secure Random number to calculate the signature for every signature generation. You can specify this Secure Random to get the same signature every time . For your case :

ProviderContext context = new ProviderContext();
context.setSecureRandom(new SecureRandom(SecureRandom.getSeed(0)));
jws.setProviderContext(context);
FzYa
  • 24
  • 3