2015-10-14 51 views
2

所以我有我的代码中的具体问题。它有时有效,有时不会,我不知道为什么。AES和CBC在Android和PHP通过Base64

它应该是如何工作的:的Android加密使用AES/CBC/PKCS5Padding随机IV和我的秘密密钥的消息,转换加密消息,Base64和使用POST方法将其发送到服务器。 服务器将消息转换为二进制形式,将其解密并将笑脸附加到消息。接下来将消息发送回Android。如果消息为空,则服务器向我发送“空白”文本。

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

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

回答

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,实施起来还有点多,但是你会得到更多的回报。

+1

好的解决方案1运行良好!只有一个例外 - '+'是'%2B' :) –

相关问题