javascriptandroidrsasjcljsbn

Encryption compatibility between SJCL and java Bouncy Castle RSA


I am working on an android application using RSA encryption.

I can get from my server, my private key and my friends public key.

Private key are like this:

{"n":"...","d":"...","p":"...","q":"...","dmp1":"...","dmq1":"...","coeff":"..."} (jsbn json format)

modulus => n
public exponent => e
private exponent => d
prime1 => p
prime2 => q
exponent1 => dmp1
exponent2 => dmq1
coefficient => coeff

With this key, I need to decrypt messages received from my server and already crypted by a javascript library (SJCL). I also need to crypt messages in order to be able to be decrypt them with the javascrypt library.

For now I have done this:

 public static String decrypt (String data, String stringKey) throws Exception {
        JSONObject jsonKey = new JSONObject(stringKey);

        BigInteger n = new BigInteger(Base64.decode(jsonKey.getString("n"), Base64.DEFAULT));
        BigInteger e = new BigInteger("10001");
        BigInteger d = new BigInteger(Base64.decode(jsonKey.getString("d"), Base64.DEFAULT));
        BigInteger p = new BigInteger(Base64.decode(jsonKey.getString("p"), Base64.DEFAULT));
        BigInteger q = new BigInteger(Base64.decode(jsonKey.getString("q"), Base64.DEFAULT));
        BigInteger dmp1 = new BigInteger(Base64.decode(jsonKey.getString("dmp1"), Base64.DEFAULT));
        BigInteger dmq1 = new BigInteger(Base64.decode(jsonKey.getString("dmq1"), Base64.DEFAULT));
        BigInteger coeff = new BigInteger(Base64.decode(jsonKey.getString("coeff"), Base64.DEFAULT));


        KeySpec privateKeySpec = new RSAPrivateCrtKeySpec(n, e, d, p, q, dmp1, dmq1, coeff);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

        Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] dec = cipher.doFinal(data.getBytes());
        return new String(Base64.decode(dec, Base64.DEFAULT));
    }

Now I get this:

javax.crypto.IllegalBlockSizeException: input must be under 96 bytes


Solution

  • After huge amount of time spent on another project I came back on this application, and solved my issue.

    First of all, the private key json generated by the SJCL/JSBN library are hexa strings. So I just needed to convert my strings in byte arrays.

    BigInteger n = new BigInteger(Utils.hexStringToByteArray(nString));
    BigInteger e = new BigInteger("10001", 16); // Public exponent
    

    The cyphered data sent, are also an hexa string.

    byte[] dec = cipher.doFinal(Utils.hexStringToByteArray(data));
    return new String(dec, "UTF-8");
    

    The cipher return a byte array as a plaintext.

    Bellow the algorithm:

    Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding");
    

    and the hexStringToByteArray functions:

    public static byte[] hexStringToByteArray(String s) {
      int len = s.length();
      byte[] data = new byte[len/2];
    
      s = s.toUpperCase();
      for(int i = 0; i < len; i+=2){
        data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
      }
    
     return data;
    }
    
    public static String byteArrayToHexString(byte[] bytes) {
      char[] hexChars = new char[bytes.length*2];
      int v;
    
      for(int j=0; j < bytes.length; j++) {
        v = bytes[j] & 0xFF;
        hexChars[j*2] = hexArray[v>>>4];
        hexChars[j*2 + 1] = hexArray[v & 0x0F];
      }
    
      return new String(hexChars).toLowerCase();
    }
    

    At the time I was looking at the wrong place, decoding my BigInteger as base64 coded strings.