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
Y: %d
D: %d
", 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
", encrypted.Header)
compact, err := encrypted.CompactSerialize()
if err != nil {
panic(err)
}
//compact = "eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTUyMSIsIngiOiJBRUFNS2ZGQ3p5NlY2WmdPdEFjSEh1c0VEM0syUC1aZmdrd2xLWmxtRFJaeGVLcTh4dUx0cXJDTzFycWx5Wkh5MXpfOEVmWXFNM0F6YlI3UGNhQVdCTURkIiwieSI6IkFMUWpEQjNLWWpLQ2twUUsxd0VUVmtvbXZ1ZDRkT05LeXhMeFJVcGpsQ0ZNSnl1bXFlUjJvc0d4N0w3UC1aU19vemJDTnhLaWU1RVQtdlNXUXczRmNLMDAifSwiZW5jIjoiQTI1NkdDTSIsImFsZyI6IkVDREgtRVMifQ..4pyFf4sd5muL9Ony.TOMCKHHWd20nPU8.NN6MFByRemeyNa50yJGVUQ"
fmt.Printf("Compact Encrypted: %v
", compact)
msg, _ := gojose.ParseEncrypted(compact)
fmt.Printf("Message from Encrypted: %v
", msg.Header)
decrypted, err := msg.Decrypt(privkey)
fmt.Printf("Decrtyped: %s
", 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:
" + exposedJWK.toECPrivateKey().getS());
System.out.println("X:
" + exposedJWK.toECPublicKey().getW().getAffineX());
System.out.println("Y:
" + 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