基于Base64的Android和PHP的AES CBC


AES CBC in Android and PHP via Base64

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

如何工作: Android使用AES/CBC/pkcs5填充随机IV和我的秘密密钥加密消息,将加密消息转换为base64并使用POST方法将其发送到服务器。服务器将消息转换为二进制形式,解密并附加微笑到消息。下一步将消息发送回Android。如果消息为空,服务器发送给我一个" empty "文本。

工作原理:我总是从服务器接收数据,所以连接很好。不幸的是,我得到了三种类型的答案:

  1. 我微笑着留言-没关系
  2. "空…"文本-但解密工作,在PHP调试模式下没有问题
  3. 我的IV太短的错误-很少

一个线索:我看了看base64数据,注意到情况2出现在base64字符串是"+"字符,但我不知道它如何能帮助。

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应用程序:

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

您需要在标记行之前对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。这需要更多的工作来实现,但是你会得到更多的回报。