【问题标题】:How to encrypt data before sending it to Firebase database?如何在将数据发送到 Firebase 数据库之前对其进行加密?
【发布时间】:2018-10-10 06:12:13
【问题描述】:

我正在使用 Firebase 实时数据库制作聊天应用程序。我知道 Firebase 非常安全(前提是您的规则是正确的),但我自己可以阅读使用我的应用程序的人的所有聊天记录。

我想阻止这种情况,为此我需要一种解密和加密方法。我尝试使用凯撒的解密,但在这个过程中失败了。

String encrypt(String talk, int key){

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

我想知道这里有没有实现凯撒加密的方法,如果没有,我应该使用哪种其他加密?

【问题讨论】:

    标签: java android firebase encryption firebase-realtime-database


    【解决方案1】:

    凯撒密码实际上不是“密码”,也不是任何使用该词的加密。它实际上是一组 25 种不同的编码。根据定义,编码不是加密,也不安全。如果您正在寻找在生产中使用的解决方案,Caesar Cipher 绝对不是。破解是微不足道的,根本不提供安全性。

    应该做的是明确定义您要保护自己免受的威胁模型和攻击向量。从这里开始,您应该咨询具有实际密码学经验的人,为您发现的问题设计解决方案。

    不过,您可能不会这样做,从来没有人这样做过,他们认为自己知道得更清楚。如果您决定不这样做,至少要花时间学习一些基本的密码学概念。

    这里有一些 Java 代码,来自我的 own repository here,它演示了使用给定密码加密和解密字符串的安全方法:

    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);
    }
    

    }

    【讨论】:

      【解决方案2】:

      您可以在客户端上生成一些加密密钥,例如基于用户的凭据,并将其安全地存储在那里(即在 KeyStore 中或使用其他方法,具体取决于您的最小 SDK 版本)。然后在发送/接收时使用加密密钥和 AES(或任何其他标准)加密/解密消息。

      【讨论】:

        【解决方案3】:

        您可以使用 Caesar 的加密,但要在接收方解密消息,您还必须将 key 存储在 Firebase 中。

        但是你可以让它自己无法理解,使用rand() 获取key 并在存储它之前对其进行任何数学运算,这也是随机的。

        这看起来确实很难,但事实并非如此。凯撒加解密的代码长这样:

        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);
            }
        

        【讨论】:

        • 对“如何在生产中使用凯撒密码?”的问题的唯一正确回答。是“不要”。
        • @LukeJoshuaPark Firebase 已经是安全的,OP 只想加密聊天,因为他可以在数据库中看到它们。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-12
        相关资源
        最近更新 更多