PHP与JAVA使用AES128位加密通信

本来JAVA和JSP之间加密通信好好的,相同的函数,相同的处理,不会有其他大问题。不过有时候就是蛋疼啊,于是就有了PHP与JAVA间使用AES进行加密通信。

PHP的AES128位由mcrypt模块提供,称为MCRYPT_RIJNDAEL_128。

JAVA的AES默认就是128位的。

加密模式有好几种,不同的语言不同的库支持的情况不同。这里选择的是安全且通用的CBC模式。

至于padding,这是最头疼的问题,因为PHP的padding与Java的padding不一样。如果使用NoPadding,则默认又用不了CBC模式。所以,最好的解决方法是自己padding——在原文末尾加上若干个空格,使原文凑齐16的倍数的长度。当然,原文末尾也可能是空格结束啊,那怎么办?没办法,只有强制原文末尾加上一个换行。这样子,每次解密后,将最右边的换行以及其右边的空格裁剪掉,就得到原文了。

另外,为了兼容,在加密和解密时,需要将内容转换成16进制的字符数组。这样一来,即使加密/解密的内容不是普通文本,而是二进制数据,也可以轻松传送啦。

JAVA方面

初始化代码:

        try {
            cipherEnc = Cipher.getInstance("AES/CBC/NoPadding");
        } catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
        } catch (NoSuchPaddingException ex) {
            ex.printStackTrace();
        }
        try {
            cipherDec = Cipher.getInstance("AES/CBC/NoPadding");
        } catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
        } catch (NoSuchPaddingException ex) {
            ex.printStackTrace();
        }

        key = new SecretKeySpec(keyStr.getBytes(), "AES");
        iv = new IvParameterSpec(ivStr.getBytes());

加密解密及其核心函数:

    public static String padRight(String s, int n) {
        return String.format("%1$-" + n + "s", s);
    }

    public static String padLeft(String s, int n) {
        return String.format("%1$#" + n + "s", s);
    }
    
    public String encrypt(SecretKeySpec enc_key, IvParameterSpec enc_iv, String str){
        byte[] ret = null;
        
        try {
            cipherEnc.init(Cipher.ENCRYPT_MODE, enc_key, enc_iv);
            ret = cipherEnc.doFinal(padRight(str,
                    ((int)Math.ceil(str.length() / 16.0))*16).getBytes());
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
        
        return byteArray2HexString(ret);
    }
    
    /*
     * str is Hex String
     */
    public String decrypt(SecretKeySpec dec_key, IvParameterSpec dec_iv, String str){
        byte[] ret = null;
        
        try {
            cipherDec.init(Cipher.DECRYPT_MODE, dec_key, dec_iv);
            ret = cipherDec.doFinal(hexString2ByteArray(str));
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
        
        try {
			return new String(ret, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			return null;
		}
    }

    static final char[] HEX_CHAR_TABLE = {
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
    };

    public static String byteArray2HexString(byte[] b) {
        if (b == null) {
            return null;
        }
        final StringBuilder hex = new StringBuilder(2 * b.length);
        for (final byte by : b) {
            hex.append(HEX_CHAR_TABLE[(by & 0xF0) >> 4]).append(HEX_CHAR_TABLE[(by & 0x0F)]);
        }
        return hex.toString();
    }
    
    public static byte[] hexString2ByteArray(String s) {
        if (s == null) {
            return null;
        }
        byte high, low;
        int len = s.length() / 2;
        byte[] b = new byte[len];
        for(int i=0, k=0; i<len; i++, k+=2)
        {
            high = (byte) (Character.digit(s.charAt(k), 16) & 0x0F);
            low = (byte) (Character.digit(s.charAt(k+1), 16) & 0x0F);
            b[i] = (byte) ((high<<4) | low);
        }
        
        return b;
    }

PHP方面

加密解密部分及其核心函数

	static function encrypt($enc_key, $enc_iv, $data){
		$pad = str_pad($data, ceil(strlen($data)/16.0)*16, " ");
		
		$method = MCRYPT_RIJNDAEL_128;
		$mode = MCRYPT_MODE_CBC;
		$td = mcrypt_module_open($method, '', $mode, '');
		mcrypt_generic_init ( $td , $enc_key , $enc_iv);
		$encrypt = mcrypt_generic($td, $pad);
		mcrypt_generic_deinit($td);
		mcrypt_module_close($td);
		
		return bin2hex($encrypt);
	}
	
	static function decrypt($dec_key, $dec_iv, $data){
		$method = MCRYPT_RIJNDAEL_128;
		$mode = MCRYPT_MODE_CBC;
		$td = mcrypt_module_open($method, '', $mode, '');
		mcrypt_generic_init ( $td , $dec_key , $dec_iv);
		$decrypt = mdecrypt_generic($td, hex2bin($data));
		mcrypt_generic_deinit($td);
		mcrypt_module_close($td);
	
		return $decrypt;
	}

if(!function_exists('hex2bin'))
{
	/**
	 * Converts the hex representation of data to binary
	 *
	 * http://www.php.net/manual/en/function.hex2bin.php
	 *
	 * @param   string  $str        Hexadecimal representation of data
	 *
	 * @return  string              Returns the binary representation of the given data
	 */
	function hex2bin($data)
	{
		return pack("H*" , $data);
	}
}

就是这样啦。什么?不懂怎么用?那就看看AES的文献,看看Java和PHP的文档再说吧。

6 comments

  1. cq528 says:

    这个,真的看不懂。太深奥的代码了。

  2. 81难 says:

    暂时还用不着PHP与JAVA之间进行通讯

  3. 百度 says:

    java部分不行。。。出错,麻烦整理到一个文件啊。

  4. DW says:

    //PHP 完全可以使用和Java一样的PKCS5Padding
    function pkcs5_pad ($text, $blocksize)
    {
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
    }

    1. creke says:

      感谢提供PKCS5Padding函数,不知道java的blocksize是多少?

Leave a comment