androidencryptionrc4-cipher

Android rc4 encryption


I think I am missing something, i believe the image(converted to bytes) is being encrypted but not decrypted when it arrives at the client side. The image seems to pass the RSA signature verification but somehow it can not be viewed.

Client Side Code:

    public void aliceEncrypt(byte[] plaintext, byte[] sharedSecret) {

    Cipher cipher;
    byte[] encrypted = null;
    try {
        cipher = Cipher.getInstance("RC4");
        Key sk = new SecretKeySpec(sharedSecret, "RC4");
        cipher.init(Cipher.ENCRYPT_MODE, sk);
        encrypted = cipher.doFinal(plaintext);
        CipherOutputStream cos = new CipherOutputStream(socket.getOutputStream(), cipher);
        ObjectOutputStream oos = new ObjectOutputStream(cos);
        oos.writeObject(encrypted);
        oos.flush();

    } catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException | InvalidKeyException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    }
}

Server Side Code:

public byte[] bobDecrypt( byte[] sharedSecret) {


    Cipher cipher = null;
    byte[] bytes = null;
    byte[] decrypted = null;
    try {
        cipher = Cipher.getInstance("RC4");
        Key sk = new SecretKeySpec(sharedSecret, "RC4");
        cipher.init(Cipher.DECRYPT_MODE, sk);
        CipherInputStream cis = new CipherInputStream(socket.getInputStream(), cipher);
        ObjectInputStream ois = new ObjectInputStream(cis);
        bytes =  (byte[])ois.readObject();
        decrypted = cipher.doFinal(bytes);

    } catch (NoSuchAlgorithmException | NoSuchPaddingException | IOException | InvalidKeyException | ClassNotFoundException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    }
    return decrypted;
}

Solution

  • CipherInputStream and CipherOutputStream are meant to do all the heavy lifting, so you just supply them with an initialized Cipher instance and then use the write and read stream methods. For the most part you can layer these as with out input and output streams, but there is one subtlety: when a block cipher is used, there is no good way to signal the CipherOutputStream that it needs to call Cipher.doFinal(). The only supported way is to call the close() method. These close() calls propagate to other wrapped streams, and in this case where a socket Outputstream is wrapped it ends up closing the socket as a side effect. This may be perfectly acceptable behavior, but you need to be aware of it. In this case, because you are using a byte-oriented stream cipher (RC4), there is no padding, so Cipher.doFinal()is basically a no-op anyway (well, it does reset the cipher state), so calling flush() is as good as calling close(). The code below is basically your code modified to correctly show how to layer and use the various streams.

    public void aliceEncrypt(byte[] plaintext, byte[] sharedSecret, Socket socket) {
    
        try {
            Cipher cipher = Cipher.getInstance("RC4/ECB/NoPadding");
            Key sk = new SecretKeySpec(sharedSecret, "RC4");
            cipher.init(Cipher.ENCRYPT_MODE, sk);
            CipherOutputStream cos = new CipherOutputStream(socket.getOutputStream(), cipher);
            ObjectOutputStream oos = new ObjectOutputStream(cos);
            oos.writeObject(plaintext);
            oos.close();
    
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    
    public byte[] bobDecrypt( byte[] sharedSecret, Socket socket) {
    
    
        try {
            Cipher cipher = Cipher.getInstance("RC4/ECB/NoPadding");
            Key sk = new SecretKeySpec(sharedSecret, "RC4");
            cipher.init(Cipher.DECRYPT_MODE, sk);
            CipherInputStream cis = new CipherInputStream(socket.getInputStream(), cipher);
            ObjectInputStream ois = new ObjectInputStream(cis);
            byte[] bytes = (byte[]) ois.readObject();
            return bytes;
    
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }