2

I have a public/private key set created with sjcl.js in node.js using the P-384 curve. It is constructed like this:

var keyPair = sjcl.ecc.elGamal.generateKeys(sjcl.ecc.curves.c384);

var pubGet = keyPair.pub.get();
var secGet = keyPair.sec.get();

var pub = sjcl.codec.base64.fromBits(pubGet.x.concat(pubGet.y));
var pubKey = "JjcqsHEy9dv8uHQzAoH7do6veTwtK3sxlwB1f/F4PvRXqi36/DCioEaEQu265xtBZ5MQ+SSVlBAaGfLi0NJHe41klrPivyyjATBmE2ZE7tb+Zb0SJhIvL4By89VCVfH/"

var sec = sjcl.codec.base64.fromBits(secGet);
var secKey = "8DDr+bZTVHyC5yLNEpVfSCekNJUTq3S8oibzdS5mp/55bDHgW7Dtkl3D4+naV8Ul"

In Javascript, this can be used to encrypt the string "hello world" like this:

var teststring = "hello world"
sjcl.encrypt(teststring, pub)

which results in the encryption objects with the relevant parameters:

"{\"iv\":\"TEcjccaUsKWzg+UwRxIAtg==\",\"v\":1,\"iter\":10000,\"ks\":128,\"ts\":64,\"mode\":\"ccm\",\"adata\":\"\",\"cipher\":\"aes\",\"salt\":\"jNHR3i4TJ44=\",\"ct\":\"9p5DCV6AdPFsUYuXqLvELg/pJd1ShesQLOgRkrzQmZUdo7Idfu8I9B74wl5A7CfVG08vbG6Etr6VetrPdQpO/GKWOvN9eisaHeKrOHgBAGLx0GP+Mh3OVX/JtDvr0/F5DK1FRyFLkZ6IPxRn9I+s7UjXQ3HPazPNEVdHuA2Jgi/49jPkFZ0dSQ==\"}"

Again, in Javascript, the keys can be deseralized like this: and in javascript, the key can be deserialised like this

var privateKeyObject = new sjcl.ecc.elGamal.secretKey(
    sjcl.ecc.curves.c384,
    sjcl.ecc.curves.c384.field.fromBits(sjcl.codec.base64.toBits(privateKey))
  );

and used like this to decrypt

var cleartext = sjcl.decrypt(privateKeyObject, message);

Up until here, all works great. However, I now need to be able to perform the same operation in Android using Kotlin.

var pubKey = "JjcqsHEy9dv8uHQzAoH7do6veTwtK3sxlwB1f/F4PvRXqi36/DCioEaEQu265xtBZ5MQ+SSVlBAaGfLi0NJHe41klrPivyyjATBmE2ZE7tb+Zb0SJhIvL4By89VCVfH/"
var secKey = "8DDr+bZTVHyC5yLNEpVfSCekNJUTq3S8oibzdS5mp/55bDHgW7Dtkl3D4+naV8Ul"
var teststring = "hello world"

 // deserialize base64 private key into object
 val input = secKey
 val encoded = Base64.getDecoder().decode(input);
 val aesPrivKey = SecretKeySpec(encoded, "P-384");

// this is the same object as created above in the browser with Javascript
val encodedJSONNew = "{\n" +
            "  \"iv\":\"TEcjccaUsKWzg+UwRxIAtg==\",\n" +
            "  \"v\":1,\n" +
            "  \"iter\":10000,\n" +
            "  \"ks\":128,\n" +
            "  \"ts\":64,\n" +
            "  \"mode\":\"ccm\",\n" +
            "  \"adata\":\"\",\n" +
            "  \"cipher\":\"aes\",\n" +
            "  \"salt\":\"jNHR3i4TJ44=\",\n" +
            "  \"ct\":\"9p5DCV6AdPFsUYuXqLvELg/pJd1ShesQLOgRkrzQmZUdo7Idfu8I9B74wl5A7CfVG08vbG6Etr6VetrPdQpO/GKWOvN9eisaHeKrOHgBAGLx0GP+Mh3OVX/JtDvr0/F5DK1FRyFLkZ6IPxRn9I+s7UjXQ3HPazPNEVdHuA2Jgi/49jPkFZ0dSQ==\"\n" +
            "}"

    //sjcl.encrypt(teststring, pub)

    // Decode the encoded JSON and create a JSON Object from it
    val jsonn = JSONObject((encodedJSONNew))

    // We need the salt, the IV and the cipher text;
    // all of them need to be Base64 decoded
    val saltNew = d.decode(jsonn.getString("salt"))
    var ivNew = d.decode(jsonn.getString("iv"))
    val cipherTextNew = d.decode(jsonn.getString("ct"))

    // Also, we need the keySize and the iteration count
    val keySizeNew = jsonn.getInt("ks")
    val iterationsNew = jsonn.getInt("iter")

    // Now, SJCL doesn't use the whole IV in CCM mode;
    lolNew = 2

    // Cut the IV to the appropriate length, which is 15 - L
    ivNew = Arrays.copyOf(ivNew, 15-lolNew)
    println("iv: " + Base64.getEncoder().encodeToString(ivNew))

    // Crypto stuff.
    // First, we need the secret AES key,
    // which is generated from password and salt
    val factoryNew = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")

    val specNew = PBEKeySpec(secKey.toCharArray(), saltNew, iterationsNew, keySizeNew)

    val tmpNew = factoryNew.generateSecret(specNew)
    val secretNew = SecretKeySpec(tmpNew.getEncoded(), "AES")

    // Now it's time to decrypt.
    val cipherNew = Cipher.getInstance("AES/CCM/NoPadding",
        BouncyCastleProvider()
    )

    // change here to use serialized priv key and iv
    cipherNew.init(Cipher.DECRYPT_MODE, aesPrivKey, IvParameterSpec(ivNew));

    val finalStringNew = String(cipherNew.doFinal(cipherTextNew));
    println("decryption successful: " + finalStringNew)

There is the following error:

Caused by: java.lang.IllegalArgumentException: Key length not 128/192/256 bits.

How can I deserialise this key correctly in Android and make this work?

codebird456
  • 505
  • 8
  • 19
  • I keep looking at this and thinking that what your calling a secret key is really a private key, and what your calling a private key is actually a secret key. It also looks like the sjcl package is very lightly documented, to say the least, and it also seems to be calling a private key a secret key, but the documentation is not clear enough to be sure. – President James K. Polk Feb 18 '20 at 03:24
  • Thanks for your answer. You are right. The sjcl is not documented enough to replicate it on other platforms. – codebird456 Feb 18 '20 at 09:00

1 Answers1

1

Because elaborating this answer for you is very time consuming for doing it for free I can tell you what would I do if I were you.

Look at this source code and try to figure what does this library do http://bitwiseshiftleft.github.io/sjcl/doc/sjcl.js.html and do it in bouncycastle.

Sorry mate but this is your only bet, if you don't like it try to use other library.

janavarro
  • 869
  • 5
  • 24
  • I had a look at the code before even posting but it is very complex for non cryptographic experts to decompose it and roll from scratch which is advised against. It should be an easy thing like with webcrypto api which uses the following params to be cross platform compatible. val encrypter = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); – codebird456 Feb 25 '20 at 19:07
  • 1
    I feel you, I had to do this once and it took me one week, because the webservice used a js library none uses. – janavarro Feb 26 '20 at 07:48
  • 1
    Thx! I ditched this lib and instead went with JSEncrypt (encrypt) / openssl (key gen) and RSA/None/PKCS1Padding instead. This works well in Android and strings can be crypted back and forth – codebird456 Feb 27 '20 at 17:09
  • Thx! I ditched this lib and instead went with JSEncrypt (encrypt) / openssl (key gen) and RSA/None/PKCS1Padding instead. This works well in Android and strings can be encrypted back and forth – codebird456 Feb 27 '20 at 17:11