2
votes

I am making a chat application using Firebase realtime database. I know that Firebase is very secure(provided your rules are correct), but I myself can read all the chats of the people using my app.

I want to stop this, and for this I'd need a decryption and encryption method. I tried using Caesar's decryption but failed in the process.

String encrypt(String talk, int key){

  for(int i=0;i<talk.length;i++)
  //can't think of how to proceed from here

I want to know if there's a way to implement Caesar's encryption in here, or if not, which other encryption should I use?

3

3 Answers

1
votes

The Caesar Cipher is not actually a "cipher" nor encryption in any use of the word. It is actually a set of 25 different encodings. An encoding, by definition, is not encryption and is not secure. If you're searching for a solution to use in production, the Caesar Cipher is definitely not it. It is trivial to break and provides no security at all.

What you should do is clearly define the threat model and attack vectors that you are protecting yourself from. From here, you should consult with someone who has actual cryptography experience to design a solution to the problems you've found.

You probably won't do that though, no one ever does, they think they know better. In the case that you decide to not do that, at the very least, take the time to learn some basic cryptography concepts.

Here is some Java code, from my own repository here, that demonstrates a secure method of encrypting and decrypting strings with a given password:

public class SecureCompatibleEncryptionExamples {

private final static String ALGORITHM_NAME = "AES/GCM/NoPadding";
private final static int ALGORITHM_NONCE_SIZE = 12;
private final static int ALGORITHM_TAG_SIZE = 128;
private final static int ALGORITHM_KEY_SIZE = 128;
private final static String PBKDF2_NAME = "PBKDF2WithHmacSHA256";
private final static int PBKDF2_SALT_SIZE = 16;
private final static int PBKDF2_ITERATIONS = 32767;

public static String encryptString(String plaintext, String password) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
    // Generate a 128-bit salt using a CSPRNG.
    SecureRandom rand = new SecureRandom();
    byte[] salt = new byte[PBKDF2_SALT_SIZE];
    rand.nextBytes(salt);

    // Create an instance of PBKDF2 and derive a key.
    PBEKeySpec pwSpec = new PBEKeySpec(password.toCharArray(), salt, PBKDF2_ITERATIONS, ALGORITHM_KEY_SIZE);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBKDF2_NAME);
    byte[] key = keyFactory.generateSecret(pwSpec).getEncoded();

    // Encrypt and prepend salt.
    byte[] ciphertextAndNonce = encrypt(plaintext.getBytes(StandardCharsets.UTF_8), key);
    byte[] ciphertextAndNonceAndSalt = new byte[salt.length + ciphertextAndNonce.length];
    System.arraycopy(salt, 0, ciphertextAndNonceAndSalt, 0, salt.length);
    System.arraycopy(ciphertextAndNonce, 0, ciphertextAndNonceAndSalt, salt.length, ciphertextAndNonce.length);

    // Return as base64 string.
    return Base64.getEncoder().encodeToString(ciphertextAndNonceAndSalt);
}

public static String decryptString(String base64CiphertextAndNonceAndSalt, String password) throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchPaddingException {
    // Decode the base64.
    byte[] ciphertextAndNonceAndSalt = Base64.getDecoder().decode(base64CiphertextAndNonceAndSalt);

    // Retrieve the salt and ciphertextAndNonce.
    byte[] salt = new byte[PBKDF2_SALT_SIZE];
    byte[] ciphertextAndNonce = new byte[ciphertextAndNonceAndSalt.length - PBKDF2_SALT_SIZE];
    System.arraycopy(ciphertextAndNonceAndSalt, 0, salt, 0, salt.length);
    System.arraycopy(ciphertextAndNonceAndSalt, salt.length, ciphertextAndNonce, 0, ciphertextAndNonce.length);

    // Create an instance of PBKDF2 and derive the key.
    PBEKeySpec pwSpec = new PBEKeySpec(password.toCharArray(), salt, PBKDF2_ITERATIONS, ALGORITHM_KEY_SIZE);
    SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBKDF2_NAME);
    byte[] key = keyFactory.generateSecret(pwSpec).getEncoded();

    // Decrypt and return result.
    return new String(decrypt(ciphertextAndNonce, key), StandardCharsets.UTF_8);
}

public static byte[] encrypt(byte[] plaintext, byte[] key) throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
    // Generate a 96-bit nonce using a CSPRNG.
    SecureRandom rand = new SecureRandom();
    byte[] nonce = new byte[ALGORITHM_NONCE_SIZE];
    rand.nextBytes(nonce);

    // Create the cipher instance and initialize.
    Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(ALGORITHM_TAG_SIZE, nonce));

    // Encrypt and prepend nonce.
    byte[] ciphertext = cipher.doFinal(plaintext);
    byte[] ciphertextAndNonce = new byte[nonce.length + ciphertext.length];
    System.arraycopy(nonce, 0, ciphertextAndNonce, 0, nonce.length);
    System.arraycopy(ciphertext, 0, ciphertextAndNonce, nonce.length, ciphertext.length);

    return ciphertextAndNonce;
}

public static byte[] decrypt(byte[] ciphertextAndNonce, byte[] key) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
    // Retrieve the nonce and ciphertext.
    byte[] nonce = new byte[ALGORITHM_NONCE_SIZE];
    byte[] ciphertext = new byte[ciphertextAndNonce.length - ALGORITHM_NONCE_SIZE];
    System.arraycopy(ciphertextAndNonce, 0, nonce, 0, nonce.length);
    System.arraycopy(ciphertextAndNonce, nonce.length, ciphertext, 0, ciphertext.length);

    // Create the cipher instance and initialize.
    Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(ALGORITHM_TAG_SIZE, nonce));

    // Decrypt and return result.
    return cipher.doFinal(ciphertext);
}

}

0
votes

You can generate some encryption key on the client, for example based on user's credentials, and store it there securely (i.e. in the KeyStore or using other approaches depending on what is your min SDK version). Then using the encryption key and AES (or any other standard) encrypt/decrypt messages on send/receive.

-1
votes

You can use Caesar's encryption but to decrypt the message on the receiver's side you've to also store the key in Firebase.

But you can make it not understandable to yourself, using rand() for getting key and before storing it, do any mathematical operation on it, that's also random.

This does look hard, but it is not. The code of Caesar's encryption and decryption looks something like this:

private String encryptMessage(String talk, int k){
        // make the string encrypted before sending to the database

        k = k % 26 + 26;
        StringBuilder encoded = new StringBuilder();
        for (char i : talk.toCharArray()) {
            if (Character.isLetter(i)) {
                if (Character.isUpperCase(i)) {
                    encoded.append((char) ('A' + (i - 'A' + k) % 26 ));
                }
                else {
                    encoded.append((char) ('a' + (i - 'a' + k) % 26 ));
                }
            }
            else {
                encoded.append(i);
            }
        }
        return encoded.toString();
    }

    private String decryptMessage(String m, int key){
       // make string readable on the receiver's device

       return encryptMessage(m,26-key);
    }