1

I am not able to encrypt/decrypt within Java and Go using Curve P521, ECDH-ES, and A256GCM using the same values for curve X, Y, and D. When I try to take the encrypted value from Go and Decrypt in Java it fails or vice versa it fails with:

Go: square/go-jose: error in cryptographic primitive

Java: AES/GCM/NoPadding decryption failed: mac check in GCM failed

Any reason why this isn't working?

!!!Update!!! I have also added code to parse a JWK to create my KeyPair in Go. (I used the java program and printed the keypair to json).

What I notice is that sometimes the Java encrypted value can be parsed in Go and sometimes it can't. It is my thought that there is something in the GCM decryption that might not be working quite right in Go.

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "log"
    "math/big"
    "os"

    b64 "encoding/base64"

    //"github.com/contiamo/jwt"

    gojose "gopkg.in/square/go-jose.v2"
    //jwtgo "github.com/dgrijalva/jwt-go"
)

func main() {

    testgojmx()
}

func testgojmx() {
    plaintext := []byte("test string")

    xEnc := "AVuFsno89wJ5xT2z63iznxVO8H5gsfcHmS1XJ_JbfEzIsudqjrvKGrzxJT96-dmP_NY7KeMvyJEUInmqcqCWbzcQ"
    yEnc := "ANv5hayQ3_TwMcFqPrtw-a9wNkfQuynuWhhbWOXYvGArdvibDGYRIRx3O5gAjfyumpibZFQ0K0jrjb09YP3AVbtc"
    dEnc := "AGPoHUdXajyyRLV0bAokQTnDzlO7Kjs1zSucSu69CGfSwpg7oXSxlfptApD-5O47d1PX3y0ag5228XsPFXVzYnH0"

    x := new(big.Int)
    temp, _ := b64.URLEncoding.DecodeString(xEnc)
    x = x.SetBytes(temp)

    y := new(big.Int)
    temp, _ = b64.URLEncoding.DecodeString(yEnc)
    y = y.SetBytes(temp)

    d := new(big.Int)
    temp, _ = b64.URLEncoding.DecodeString(dEnc)
    d = d.SetBytes(temp)

    privkey := new(ecdsa.PrivateKey)

    privkey.PublicKey.Curve = elliptic.P521()
    privkey.D = d
    privkey.PublicKey.X = x
    privkey.PublicKey.Y = y

    //javaKey := `{"kty":"EC","d":"AGPoHUdXajyyRLV0bAokQTnDzlO7Kjs1zSucSu69CGfSwpg7oXSxlfptApD-5O47d1PX3y0ag5228XsPFXVzYnH0","crv":"P-521","x":"AVuFsno89wJ5xT2z63iznxVO8H5gsfcHmS1XJ_JbfEzIsudqjrvKGrzxJT96-dmP_NY7KeMvyJEUInmqcqCWbzcQ","y":"ANv5hayQ3_TwMcFqPrtw-a9wNkfQuynuWhhbWOXYvGArdvibDGYRIRx3O5gAjfyumpibZFQ0K0jrjb09YP3AVbtc"}`
    //jwkSet, err := jwk.ParseString(javaKey)

    //if err != nil {
    //  panic(err)
    //}

    //key, err := jwkSet.Keys[0].Materialize()

    //if err != nil {
    //  panic(err)
    //}

    //privkey := key.(*ecdsa.PrivateKey)

    fmt.Printf("X: %d\nY: %d\nD: %d\n", privkey.X, privkey.Y, privkey.D)

    encrypter, err := gojose.NewEncrypter(gojose.A256GCM, gojose.Recipient{Algorithm: gojose.ECDH_ES, Key: privkey.Public()}, nil)
    if err != nil {
        panic(err)
    }

    encrypted, err := encrypter.Encrypt(plaintext)
    if err != nil {
        panic(err)
    }

    fmt.Printf("encrypted = %v\n", encrypted.Header)

    compact, err := encrypted.CompactSerialize()
    if err != nil {
        panic(err)
    }

    //compact = "eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTUyMSIsIngiOiJBRUFNS2ZGQ3p5NlY2WmdPdEFjSEh1c0VEM0syUC1aZmdrd2xLWmxtRFJaeGVLcTh4dUx0cXJDTzFycWx5Wkh5MXpfOEVmWXFNM0F6YlI3UGNhQVdCTURkIiwieSI6IkFMUWpEQjNLWWpLQ2twUUsxd0VUVmtvbXZ1ZDRkT05LeXhMeFJVcGpsQ0ZNSnl1bXFlUjJvc0d4N0w3UC1aU19vemJDTnhLaWU1RVQtdlNXUXczRmNLMDAifSwiZW5jIjoiQTI1NkdDTSIsImFsZyI6IkVDREgtRVMifQ..4pyFf4sd5muL9Ony.TOMCKHHWd20nPU8.NN6MFByRemeyNa50yJGVUQ"

    fmt.Printf("Compact Encrypted: %v\n", compact)

    msg, _ := gojose.ParseEncrypted(compact)
    fmt.Printf("Message from Encrypted: %v\n", msg.Header)

    decrypted, err := msg.Decrypt(privkey)

    fmt.Printf("Decrtyped: %s\n", decrypted)
}
@SpringBootApplication
public class EcdhjweApplication implements CommandLineRunner {

    private static Logger LOG = LoggerFactory
            .getLogger(EcdhjweApplication.class);

    static ECKey exposedJWK = generateECKeyJwk();

    public static void main(String[] args) {

        SpringApplication.run(EcdhjweApplication.class, args);
    }

    @Override
    public void run(String... args) {
        LOG.info("EXECUTING : command line runner");

        try {
            System.out.println("D: \n" + exposedJWK.toECPrivateKey().getS());
            System.out.println("X: \n" + exposedJWK.toECPublicKey().getW().getAffineX());
            System.out.println("Y: \n" + exposedJWK.toECPublicKey().getW().getAffineY());
        } catch (JOSEException e) {
            e.printStackTrace();
        }

        System.out.println("======================== Encrypting ================================");
        String encryptedRequest = null;
        try {
            encryptedRequest = encryptJWE("test string", exposedJWK.toECPublicKey());
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Encrypted Requested::: " + encryptedRequest);

        //encryptedRequest = "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTI1NkdDTSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtNTIxIiwieCI6IkFOSXluNGRoZTd1UkpLWDJzTkVGaVU1dDFEYmR4ZUE5UjRReGVRdk1IXy1GZ2VLWWhNSk9uR1k0LWRzMEtVbjQtRlJfZVhuNl9HLWpqWEdGaThYVGdwVjUiLCJ5IjoiQUxYVkpaMEZmcHhaQzd6andhbEdWWjdyNTJxZlg5VmNsRnY4eWlsQ1pqY3lJbnlYT1BEVlhoWDlPYVU4R1ppeVVmOU1mQndSaTAzOUNMajdiQ0duM1lPRCJ9fQ.._ASNMu9mjl02agPG.QPV7DKPV77hiLJ8.SBGhv8JRJTl_adfFNxgO0Q";

        System.out.println("======================== Decrypting Request ================================");
        String decryptedDetails = null;
        try {
            decryptedDetails = decryptJWE(encryptedRequest, exposedJWK.toECPrivateKey());
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("Decrypted Request:::" + decryptedDetails);
    }

    //private static String encryptJWE(JSONObject payload, ECPublicKey ecPublicKey) throws Exception {
    private static String encryptJWE(String payload, ECPublicKey ecPublicKey) throws Exception {
        // Build JWE header
        JWEHeader header = new JWEHeader.Builder(JWEAlgorithm.ECDH_ES, EncryptionMethod.A256GCM)
                .build();

        // Build JWE Object
        JWEObject jweObjectClient = new JWEObject(header, new Payload(payload));

        // Set Public Key, Encrypt
        ECDHEncrypter encrypter = new ECDHEncrypter(ecPublicKey);
        encrypter.getJCAContext().setContentEncryptionProvider(BouncyCastleProviderSingleton.getInstance());
        jweObjectClient.encrypt(encrypter);
        return jweObjectClient.serialize();
    }

    private static String decryptJWE(String vcnRequestJWE, ECPrivateKey ecPrivateKey) throws Exception {
        // Parse JWE & validate headers
        JWEObject jweObject = EncryptedJWT.parse(vcnRequestJWE);

        // Set PrivateKey and Decrypt
        ECDHDecrypter decrypter = new ECDHDecrypter(ecPrivateKey);
        decrypter.getJCAContext().setContentEncryptionProvider(BouncyCastleProviderSingleton.getInstance());
        jweObject.decrypt(decrypter);

        return jweObject.getPayload().toString();
    }

    public static ECKey generateECKeyJwk() {
        try {
            // Generate EC key pair with P-521 curve

            String x = "AVuFsno89wJ5xT2z63iznxVO8H5gsfcHmS1XJ_JbfEzIsudqjrvKGrzxJT96-dmP_NY7KeMvyJEUInmqcqCWbzcQ";
            String y = "ANv5hayQ3_TwMcFqPrtw-a9wNkfQuynuWhhbWOXYvGArdvibDGYRIRx3O5gAjfyumpibZFQ0K0jrjb09YP3AVbtc";
            String d = "AGPoHUdXajyyRLV0bAokQTnDzlO7Kjs1zSucSu69CGfSwpg7oXSxlfptApD-5O47d1PX3y0ag5228XsPFXVzYnH0";

            return new ECKey.Builder(Curve.P_521, new Base64URL(x), new Base64URL(y))
                    .d(new Base64URL(d))
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}

The code as shown shows demonstrates working within the language. The commented out strings are the result of a run from the other language

user1701907
  • 98
  • 11
  • I believe it is something in dealing with Curve P-521. I posted an issue on the githhub repo for go-jose. The response so far was it may have to do something with 8 byte boundaries and that curve doesn't play nice with that. https://github.com/square/go-jose/issues/228. I have change to using P-256 (I can't permanently because clients are going to use P-251) and everything works 100% of the time – user1701907 Apr 17 '19 at 12:14
  • Is it possible to get a sample to test the decryption using another library? Can be sent in private if needed. – Spomky-Labs Apr 18 '19 at 08:43
  • Here is the encryped string: "eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTUyMSIsIngiOiJBRUFNS2ZGQ3p5NlY2WmdPdEFjSEh1c0VEM0syUC1aZmdrd2xLWmxtRFJaeGVLcTh4dUx0cXJDTzFycWx5Wkh5MXpfOEVmWXFNM0F6YlI3UGNhQVdCTURkIiwieSI6IkFMUWpEQjNLWWpLQ2twUUsxd0VUVmtvbXZ1ZDRkT05LeXhMeFJVcGpsQ0ZNSnl1bXFlUjJvc0d4N0w3UC1aU19vemJDTnhLaWU1RVQtdlNXUXczRmNLMDAifSwiZW5jIjoiQTI1NkdDTSIsImFsZyI6IkVDREgtRVMifQ..4pyFf4sd5muL9Ony.TOMCKHHWd20nPU8.NN6MFByRemeyNa50yJGVUQ" – user1701907 Apr 18 '19 at 12:29
  • Here is the KeyPair Definition: {"kty":"EC","d":"AGPoHUdXajyyRLV0bAokQTnDzlO7Kjs1zSucSu69CGfSwpg7oXSxlfptApD-5O47d1PX3y0ag5228XsPFXVzYnH0","crv":"P-521","x":"AVuFsno89wJ5xT2z63iznxVO8H5gsfcHmS1XJ_JbfEzIsudqjrvKGrzxJT96-dmP_NY7KeMvyJEUInmqcqCWbzcQ","y":"ANv5hayQ3_TwMcFqPrtw-a9wNkfQuynuWhhbWOXYvGArdvibDGYRIRx3O5gAjfyumpibZFQ0K0jrjb09YP3AVbtc"} . That should be enough to test in any library. The x, y, and d values are Base64Encoded – user1701907 Apr 18 '19 at 12:31
  • I confirm that is not possible to decrypt the token with the key material and the JWE commented in the Go example. It is possible with the Java one. I concur with @user1701907 saying the ECDH-ES implementation of the go-jose is incorrect. The ephemeral keys are on the curve in both cases. My assumption is that the agreement key is not correctly computed by go-jose. It could be interresting to compare a key agreement computed by different libraries with known key. – Spomky-Labs Apr 18 '19 at 16:26

0 Answers0