通过Base64在Android和PHP中使用AES CBC

时间:2015-10-14 16:39:47

标签: php android encryption base64 aes

所以我的代码中有一个特定的问题。它有时是有效的,有时不是,我不明白为什么。

如何运作:Android使用带有随机IV和我的密钥的AES / CBC / PKCS5Padding对邮件进行加密,将加密邮件转换为base64并使用POST方法将其发送到服务器。 服务器将消息转换为二进制形式,对其进行解密并将消息附加到消息中。接下来将消息发送回Android。如果消息为空,服务器会向我发送一个"空"文本。

工作原理:我总是从服务器接收数据,因此连接正常。不幸的是,我得到了3种答案:

  1. 我微笑的消息 - 没关系
  2. "空..."文本 - 但解密工作不知何故,PHP调试模式没问题
  3. 我的IV太短的错误 - 很少
  4. 线索:我查看了base64数据并发现当base64字符串为" +"时出现情况2 char,但我不知道它会如何帮助。

    Android部分发送数据做服务器:

    HttpURLConnection urlConnection;
    String message = null;
    String answer = null;
    String data = "a piece of data";
    
    try {
        byte[] wynikByte = encrypt(data.getBytes("UTF-8"));
        message = Base64.encodeToString(wynikByte, Base64.DEFAULT);
    } catch (UnsupportedEncodingException ex){
        Log.e("CRYPT", "Not working");
    }
    
    try {
        // Connect to server
        urlConnection = (HttpURLConnection) ((new URL(url).openConnection()));
        urlConnection.setDoOutput(true);
        urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        urlConnection.setRequestMethod("POST");
        urlConnection.connect();
    
        // Send to server
        OutputStream outputStream = urlConnection.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
        writer.write("dane=" + message);
        writer.close();
        outputStream.close();
    
        // Read answer
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), "UTF-8"));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while ((line = bufferedReader.readLine()) != null) {
            sb.append(line);
        }
        bufferedReader.close();
        answer = sb.toString();
    
    } catch (UnsupportedEncodingException | IOException ex) {
            e.printStackTrace();
        }   
    return message + "\n" + answer;
    

    Android加密方法:

    public static byte[] encrypt(byte[] plaintext) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKey key = new SecretKeySpec(hexStringToByteArray(klucz2), "AES");
    
            SecureRandom random = new SecureRandom();
            byte iv[] = new byte[16];//generate random 16 byte IV AES is always 16bytes
            random.nextBytes(iv);
            IvParameterSpec ivspec = new IvParameterSpec(iv);
    
            cipher.init(Cipher.ENCRYPT_MODE, key, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);
            byte[] ciphertext = new byte[iv.length + encrypted.length];
            System.arraycopy(iv, 0, ciphertext, 0, iv.length);
            System.arraycopy(encrypted, 0, ciphertext, iv.length, encrypted.length);
            return ciphertext;
    
        } catch (InvalidKeyException | NoSuchAlgorithmException
                | NoSuchPaddingException
                | IllegalBlockSizeException | InvalidAlgorithmParameterException
                | BadPaddingException e) {
            throw new IllegalStateException(
                    "CBC encryption with standard algorithm should never fail",
                    e);
        }
    } 
    

    带有我的密钥的PHP文件也在android app中使用:

    <?php
    if (isset($_POST['dane']))
    {
        $dane = $_POST['dane'];
        $key = pack('H*', "73f826a001837efe6278b82789267aca");
    
        $blocksize = mcrypt_get_block_size('rijndael_128', 'cbc');
        $ciphertext = base64_decode($dane, $powodzenie);
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
        $iv_old = substr($ciphertext, 0, $iv_size);
        $ciphertext = substr($ciphertext, $iv_size);
        $plaintext = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext, MCRYPT_MODE_CBC, $iv_old);
        $plaintext  = pkcs5_unpad($plaintext);
        if($plaintext == "")
        {
            echo "Empty...";
            return;
        }
        $plaintext = $plaintext . " :)";
        echo $plaintext;
    } else {
        echo "Dane is empty";
    }
    
    // PHP don't have pkcs5 methods to pad
    function pkcs5_pad ($text, $blocksize) 
    { 
        $pad = $blocksize - (strlen($text) % $blocksize); 
        return $text . str_repeat(chr($pad), $pad); 
    } 
    
    // PHP don't have pkcs5 methods to unpad
    function pkcs5_unpad($text) 
    { 
        $pad = ord($text{strlen($text)-1}); 
        if ($pad > strlen($text)) return false; 
        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false; 
        return substr($text, 0, -1 * $pad); 
    } 
    ?>
    

1 个答案:

答案 0 :(得分:1)

您需要在标记的行之前正确编码message字符串。是的,base64是7位安全的,但它也包含在表单编码数据中很重要的字符。 [+=具体而言]

// Send to server
OutputStream outputStream = urlConnection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));
writer.write("dane=" + message); // here
writer.close();
outputStream.close();

解决方案1将分别用+=替换%2B%3D

解决方案2将切换到多部分编码。

我的偏好是解决方案2.实施起来需要做更多的工作,但是你可以获得更多的收益。