Java解密错误:数据未对齐块大小

发布于 2024-11-17 22:56:35 字数 2282 浏览 2 评论 0原文

我正在尝试加密我的 Android 应用程序和 PHP Web 服务之间的数据。

我在该网站中找到了下一段代码: http://schneimi.wordpress.com/2008/11/25/aes-128bit-encryption- Between-java-and-php/

但是当我尝试解密时我得到标题“数据未块大小对齐”的例外

这是我的MCrypt类中的方法

public String encrypt(String text) throws Exception
{
    if(text == null || text.length() == 0)
        throw new Exception("Empty string");

    Cipher cipher;
    byte[] encrypted = null;

    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

        encrypted = cipher.doFinal(padString(text).getBytes());
    } catch (Exception e)
    {           
        throw new Exception("[encrypt] " + e.getMessage());
    }

    return new String( encrypted );
}

public String decrypt(String code) throws Exception
{
    if(code == null || code.length() == 0)
        throw new Exception("Empty string");

    Cipher cipher;
    byte[] decrypted = null;

    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
        decrypted = cipher.doFinal(hexToBytes(code));
    } catch (Exception e)
    {
        throw new Exception("[decrypt] " + e.getMessage());
    }
    return new String( decrypted );
}


private static byte[] hexToBytes(String hex) {
  String HEXINDEX = "0123456789abcdef";
  int l = hex.length() / 2;
  byte data[] = new byte[l];
  int j = 0;

  for (int i = 0; i < l; i++) {
    char c = hex.charAt(j++);
    int n, b;

    n = HEXINDEX.indexOf(c);
    b = (n & 0xf) << 4;
    c = hex.charAt(j++);
    n = HEXINDEX.indexOf(c);
    b += (n & 0xf);
    data[i] = (byte) b;
  }

  return data;
}

private static String padString(String source)
{
  char paddingChar = ' ';
  int size = 16;
  int x = source.length() % size;
  int padLength = size - x;

  for (int i = 0; i < padLength; i++)
  {
      source += paddingChar;
  }

  return source;
}

这就是我在活动中使用它来测试的方式:

String encrypted = mcrypt.encrypt(jsonUser.toString());
String decrypted = mcrypt.decrypt(encrypted);

加密方法工作正常,但是第二个抛出异常。

I'm trying to encrypt data between my android application and a PHP webservice.

I found the next piece of code in this website: http://schneimi.wordpress.com/2008/11/25/aes-128bit-encryption-between-java-and-php/

But when I try to decrypt I get the Exception of the title "data not block size aligned"

This are the method in my MCrypt class

public String encrypt(String text) throws Exception
{
    if(text == null || text.length() == 0)
        throw new Exception("Empty string");

    Cipher cipher;
    byte[] encrypted = null;

    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

        encrypted = cipher.doFinal(padString(text).getBytes());
    } catch (Exception e)
    {           
        throw new Exception("[encrypt] " + e.getMessage());
    }

    return new String( encrypted );
}

public String decrypt(String code) throws Exception
{
    if(code == null || code.length() == 0)
        throw new Exception("Empty string");

    Cipher cipher;
    byte[] decrypted = null;

    try {
        cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
        decrypted = cipher.doFinal(hexToBytes(code));
    } catch (Exception e)
    {
        throw new Exception("[decrypt] " + e.getMessage());
    }
    return new String( decrypted );
}


private static byte[] hexToBytes(String hex) {
  String HEXINDEX = "0123456789abcdef";
  int l = hex.length() / 2;
  byte data[] = new byte[l];
  int j = 0;

  for (int i = 0; i < l; i++) {
    char c = hex.charAt(j++);
    int n, b;

    n = HEXINDEX.indexOf(c);
    b = (n & 0xf) << 4;
    c = hex.charAt(j++);
    n = HEXINDEX.indexOf(c);
    b += (n & 0xf);
    data[i] = (byte) b;
  }

  return data;
}

private static String padString(String source)
{
  char paddingChar = ' ';
  int size = 16;
  int x = source.length() % size;
  int padLength = size - x;

  for (int i = 0; i < padLength; i++)
  {
      source += paddingChar;
  }

  return source;
}

And this is how I'm using it in my activity to test:

String encrypted = mcrypt.encrypt(jsonUser.toString());
String decrypted = mcrypt.decrypt(encrypted);

the encrypt method works fine, but the second throws an exception.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不爱素颜 2024-11-24 22:56:35

终于!我成功了!感谢您的所有建议。我想分享代码,以防万一有人像我一样陷入困境:

JAVA

import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class MCrypt {

    private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
    private IvParameterSpec ivspec;
    private SecretKeySpec keyspec;
    private Cipher cipher;

    private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)

    public MCrypt()
    {
        ivspec = new IvParameterSpec(iv.getBytes());

        keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");

        try {
            cipher = Cipher.getInstance("AES/CBC/NoPadding");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public byte[] encrypt(String text) throws Exception
    {
        if(text == null || text.length() == 0)
            throw new Exception("Empty string");

        byte[] encrypted = null;

        try {
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

            encrypted = cipher.doFinal(padString(text).getBytes());
        } catch (Exception e)
        {           
            throw new Exception("[encrypt] " + e.getMessage());
        }

        return encrypted;
    }

    public byte[] decrypt(String code) throws Exception
    {
        if(code == null || code.length() == 0)
            throw new Exception("Empty string");

        byte[] decrypted = null;

        try {
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            decrypted = cipher.doFinal(hexToBytes(code));
        } catch (Exception e)
        {
            throw new Exception("[decrypt] " + e.getMessage());
        }
        return decrypted;
    }



    public static String bytesToHex(byte[] data)
    {
        if (data==null)
        {
            return null;
        }

        int len = data.length;
        String str = "";
        for (int i=0; i<len; i++) {
            if ((data[i]&0xFF)<16)
                str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
            else
                str = str + java.lang.Integer.toHexString(data[i]&0xFF);
        }
        return str;
    }


    public static byte[] hexToBytes(String str) {
        if (str==null) {
            return null;
        } else if (str.length() < 2) {
            return null;
        } else {
            int len = str.length() / 2;
            byte[] buffer = new byte[len];
            for (int i=0; i<len; i++) {
                buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
            }
            return buffer;
        }
    }



    private static String padString(String source)
    {
      char paddingChar = ' ';
      int size = 16;
      int x = source.length() % size;
      int padLength = size - x;

      for (int i = 0; i < padLength; i++)
      {
          source += paddingChar;
      }

      return source;
    }
}

如何使用它 (JAVA)

mcrypt = new MCrypt();
/* Encrypt */
String encrypted = MCrypt.bytesToHex( mcrypt.encrypt("Text to Encrypt") );
/* Decrypt */
String decrypted = new String( mcrypt.decrypt( encrypted ) );

============ ==========================================

PHP

<?php 

class MCrypt
{
    private $iv = 'fedcba9876543210'; #Same as in JAVA
    private $key = '0123456789abcdef'; #Same as in JAVA


    function __construct()
    {
    }

    function encrypt($str) {

      //$key = $this->hex2bin($key);    
      $iv = $this->iv;

      $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);

      mcrypt_generic_init($td, $this->key, $iv);
      $encrypted = mcrypt_generic($td, $str);

      mcrypt_generic_deinit($td);
      mcrypt_module_close($td);

      return bin2hex($encrypted);
    }

    function decrypt($code) {
      //$key = $this->hex2bin($key);
      $code = $this->hex2bin($code);
      $iv = $this->iv;

      $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);

      mcrypt_generic_init($td, $this->key, $iv);
      $decrypted = mdecrypt_generic($td, $code);

      mcrypt_generic_deinit($td);
      mcrypt_module_close($td);

      return utf8_encode(trim($decrypted));
    }

    protected function hex2bin($hexdata) {
      $bindata = '';

      for ($i = 0; $i < strlen($hexdata); $i += 2) {
        $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
      }

      return $bindata;
    }

}

如何使用它(PHP)

<?php 

$mcrypt = new MCrypt();
#Encrypt
$encrypted = $mcrypt->encrypt("Text to encrypt");
#Decrypt
$decrypted = $mcrypt->decrypt($encrypted);

At last! I made it work! Thanks for all your suggestion. I would like to share the code just in case somebody get stuck like me:

JAVA

import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class MCrypt {

    private String iv = "fedcba9876543210";//Dummy iv (CHANGE IT!)
    private IvParameterSpec ivspec;
    private SecretKeySpec keyspec;
    private Cipher cipher;

    private String SecretKey = "0123456789abcdef";//Dummy secretKey (CHANGE IT!)

    public MCrypt()
    {
        ivspec = new IvParameterSpec(iv.getBytes());

        keyspec = new SecretKeySpec(SecretKey.getBytes(), "AES");

        try {
            cipher = Cipher.getInstance("AES/CBC/NoPadding");
        } catch (NoSuchAlgorithmException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public byte[] encrypt(String text) throws Exception
    {
        if(text == null || text.length() == 0)
            throw new Exception("Empty string");

        byte[] encrypted = null;

        try {
            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);

            encrypted = cipher.doFinal(padString(text).getBytes());
        } catch (Exception e)
        {           
            throw new Exception("[encrypt] " + e.getMessage());
        }

        return encrypted;
    }

    public byte[] decrypt(String code) throws Exception
    {
        if(code == null || code.length() == 0)
            throw new Exception("Empty string");

        byte[] decrypted = null;

        try {
            cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

            decrypted = cipher.doFinal(hexToBytes(code));
        } catch (Exception e)
        {
            throw new Exception("[decrypt] " + e.getMessage());
        }
        return decrypted;
    }



    public static String bytesToHex(byte[] data)
    {
        if (data==null)
        {
            return null;
        }

        int len = data.length;
        String str = "";
        for (int i=0; i<len; i++) {
            if ((data[i]&0xFF)<16)
                str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
            else
                str = str + java.lang.Integer.toHexString(data[i]&0xFF);
        }
        return str;
    }


    public static byte[] hexToBytes(String str) {
        if (str==null) {
            return null;
        } else if (str.length() < 2) {
            return null;
        } else {
            int len = str.length() / 2;
            byte[] buffer = new byte[len];
            for (int i=0; i<len; i++) {
                buffer[i] = (byte) Integer.parseInt(str.substring(i*2,i*2+2),16);
            }
            return buffer;
        }
    }



    private static String padString(String source)
    {
      char paddingChar = ' ';
      int size = 16;
      int x = source.length() % size;
      int padLength = size - x;

      for (int i = 0; i < padLength; i++)
      {
          source += paddingChar;
      }

      return source;
    }
}

HOW TO USE IT (JAVA)

mcrypt = new MCrypt();
/* Encrypt */
String encrypted = MCrypt.bytesToHex( mcrypt.encrypt("Text to Encrypt") );
/* Decrypt */
String decrypted = new String( mcrypt.decrypt( encrypted ) );

====================================================

PHP

<?php 

class MCrypt
{
    private $iv = 'fedcba9876543210'; #Same as in JAVA
    private $key = '0123456789abcdef'; #Same as in JAVA


    function __construct()
    {
    }

    function encrypt($str) {

      //$key = $this->hex2bin($key);    
      $iv = $this->iv;

      $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);

      mcrypt_generic_init($td, $this->key, $iv);
      $encrypted = mcrypt_generic($td, $str);

      mcrypt_generic_deinit($td);
      mcrypt_module_close($td);

      return bin2hex($encrypted);
    }

    function decrypt($code) {
      //$key = $this->hex2bin($key);
      $code = $this->hex2bin($code);
      $iv = $this->iv;

      $td = mcrypt_module_open('rijndael-128', '', 'cbc', $iv);

      mcrypt_generic_init($td, $this->key, $iv);
      $decrypted = mdecrypt_generic($td, $code);

      mcrypt_generic_deinit($td);
      mcrypt_module_close($td);

      return utf8_encode(trim($decrypted));
    }

    protected function hex2bin($hexdata) {
      $bindata = '';

      for ($i = 0; $i < strlen($hexdata); $i += 2) {
        $bindata .= chr(hexdec(substr($hexdata, $i, 2)));
      }

      return $bindata;
    }

}

HOW TO USE IT (PHP)

<?php 

$mcrypt = new MCrypt();
#Encrypt
$encrypted = $mcrypt->encrypt("Text to encrypt");
#Decrypt
$decrypted = $mcrypt->decrypt($encrypted);
墨离汐 2024-11-24 22:56:35

我猜您的 keyspecivspec 对于解密无效。我通常将它们转换为 PublicKeyPrivateKey 实例,然后使用私钥进行解密。

I'm guessing your keyspec and ivspec are not valid for decryption. I've typically transformed them into PublicKey and PrivateKey instances and then use the private key to decrypt.

心是晴朗的。 2024-11-24 22:56:35

我看了另一个答案的评论。我在尝试使用 php 中的开放 SSL 加密一大块文本时遇到了类似的问题(双方)。我想同样的问题也会出现在 Java 中。

如果您有 1024 位 RSA 密钥,则必须将传入文本拆分为 117 字节块(一个字符是一个字节)并对每个块进行加密(您可以将它们连接在一起)。另一方面,您必须将加密数据分割成 128 字节的块并对每个块进行解密。这应该会为您提供原始消息。

另请注意,http 可能无法友好地处理非 ASCII 加密数据。我在传输之前和之后对其进行 Base64 编码/解码(另外,您还必须担心 Base64 更改的额外 urlencoding,但这很容易)。

我不确定你的 AES 密钥长度,但如果它是 1024 位,则块长度可能是相同的。如果不是,则必须将这些位除以 8 才能找到字节块的长度。不幸的是,我实际上不确定如何让它进来(也许乘以 117/128 ?)

这是一些 php 代码:

class Crypto {
   public function encrypt($key, $data) {
      $crypto = '';
      foreach (str_split($data, 117) as $chunk) {
         openssl_public_encrypt($chunk, $encrypted, $key);
         $crypto .= $encrypted;
      }
      return $crypto;
   }

   //Decrypt omitted.  Basically the same, change 117 to 128.

   /**#@+
    * Update data for HTTP transmission and retrieval
    * Must be used on encrypted data, but also useful for any binary data
    * (e.g. zip files).
    */
   public function base64_encode($value) {
      return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
   }
   //String length must be padded for decoding for some reason
   public function base64_decode($value) {
      return base64_decode(str_pad(strtr($value, '-_', '+/')
         , strlen($value) % 4, '=', STR_PAD_RIGHT));
   }
   /**#@-*/
}

I looked at the comments in the other answer. I ran into a similar problem trying to encrypt a large block of text using open SSL in php (on both sides). I imagine the same issue would come up in Java.

If you have a 1024 bit RSA key, you must split the incoming text into 117 byte chunks (a char is a byte) and encrypt each (you can concatenate them together). On the other end, you must split the encrypted data into 128 byte chunks and decrypt each. This should give you your original message.

Also note that http may not play friendly with the non-ASCII encrypted data. I base64 encode/decode it before and after transmission (plus you have to worry about additional urlencoding for the base64 change, but it is easy).

I'm not sure of your AES key length, but if it's 1024 bits the chunk length is probably the same. If it's not, you will have to divide the bits by 8 to find the byte chunk length coming out. I'm actually not sure how to get it coming in, unfortunately (maybe multiply by 117/128 ?)

Here's some php code:

class Crypto {
   public function encrypt($key, $data) {
      $crypto = '';
      foreach (str_split($data, 117) as $chunk) {
         openssl_public_encrypt($chunk, $encrypted, $key);
         $crypto .= $encrypted;
      }
      return $crypto;
   }

   //Decrypt omitted.  Basically the same, change 117 to 128.

   /**#@+
    * Update data for HTTP transmission and retrieval
    * Must be used on encrypted data, but also useful for any binary data
    * (e.g. zip files).
    */
   public function base64_encode($value) {
      return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
   }
   //String length must be padded for decoding for some reason
   public function base64_decode($value) {
      return base64_decode(str_pad(strtr($value, '-_', '+/')
         , strlen($value) % 4, '=', STR_PAD_RIGHT));
   }
   /**#@-*/
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文