欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android加密,php解密 , AES,CBC, PKCS7Padding

程序员文章站 2024-03-20 10:54:16
...

数据加密的技术分为两类,即对称加密和非对称加密。对称加密以数据加密标准(DES,Data Encryption Standard)算法为典型代表,非对称加密通常以RSA(RivestShamir Adleman)算法为代表。对称加密的加***和解***相同,而非对称加密的加***和解***不同,加***可以公开而解***需要保密。

DES比较老的算法,一共有三个参数入口(原文,**,加密模式)。而3DES只是DES的一种模式,是以DES为基础更安全的变形,对数据进行了三次加密,也是被指定为AES的过渡算法。AES是高级加密标准,新一代标准,加密速度更快,安全性更高。AES**长度可以选择128位(16字节),192位(24字节)和256位(32字节)**。AES算法的所有参数都是字节码的(包括**),因此字符串字符需要转换成字节码后进行加密。

小提示:我们日常使用的MD5,SHA1属于摘要算法,并非上述加密范畴之内。

废话不说了,在安卓端,我做了一个AESCryptUtil类,来实现加密算法,我的加密方式为 : AES/CBC/PKCS7Padding

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.io.UnsupportedEncodingException;

public class AESCryptUtil {

    /** 算法/模式/填充 **/
    private static final String CipherMode = "AES/CBC/PKCS7Padding";

    // 创建**, 长度为128位(16bytes), 且转成字节格式
    private static SecretKeySpec createKey(String key) {

        byte[] data = null;

        if (key == null) { key = ""; }
        StringBuffer sb = new StringBuffer(16);
        sb.append(key);
        while (sb.length() < 16) { sb.append("0"); }
        if (sb.length() > 16) { sb.setLength(16); }

        try { data = sb.toString().getBytes("UTF-8");}
        catch (UnsupportedEncodingException e) { e.printStackTrace(); }

        return new SecretKeySpec(data, "AES");
    }

    // 创建初始化向量, 长度为16bytes, 向量的作用其实就是salt
    private static IvParameterSpec createIV(String iv) {

        byte[] data = null;

        if (iv == null) { iv = ""; }
        StringBuffer sb = new StringBuffer(16);
        sb.append(iv);
        while (sb.length() < 16) { sb.append("0"); }
        if (sb.length() > 16) { sb.setLength(16); }

        try { data = sb.toString().getBytes("UTF-8"); }
        catch (UnsupportedEncodingException e) { e.printStackTrace(); }

        return new IvParameterSpec(data);
    }

    /****************************************************************************/

    // 加密字节数据, 被加密的数据需要提前转化成字节格式
    private static byte[] encrypt(byte[] content, String key, String iv) {

        try {
            SecretKeySpec secretKeySpec = createKey(key);
            IvParameterSpec ivParameterSpec = createIV(iv);
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] result = cipher.doFinal(content); // 加密
            return result;
        } catch (Exception e) { e.printStackTrace(); }
        return null;
    }

    // 加密字符串数据, 返回的字节数据还需转化成16进制字符串
    public static String encrypt(String content, String key, String iv) {

        byte[] data = null;
        try { data = content.getBytes("UTF-8"); }
        catch (Exception e) { e.printStackTrace(); }

        data = encrypt(data, key, iv);
        return byte2hex(data);
    }

    /****************************************************************************/

    // 解密字节数组
    private static byte[] decrypt(byte[] content, String key, String iv) {

        try {
            SecretKeySpec secretKeySpec = createKey(key);
            IvParameterSpec ivParameterSpec = createIV(iv);
            Cipher cipher = Cipher.getInstance(CipherMode);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) { e.printStackTrace(); }
        return null;
    }

    // 解密(输出结果为字符串), 密文为16进制的字符串
    public static String decrypt(String content, String password, String iv) {

        byte[] data = null;
        try { data = hex2byte(content); }
        catch (Exception e) { e.printStackTrace(); }

        data = decrypt(data, password, iv);
        if (data == null) return null;

        String result = null;
        try { result = new String(data, "UTF-8");
        } catch (UnsupportedEncodingException e) { e.printStackTrace(); }

        return result;
    }

    /****************************************************************************/

    // 字节数组转成16进制大写字符串
    private static String byte2hex(byte[] b) {

        String tmp = "";
        StringBuffer sb = new StringBuffer(b.length * 2);
        for (int n = 0; n < b.length; n++) {
            tmp = (java.lang.Integer.toHexString(b[n] & 0XFF));
            if (tmp.length() == 1) { sb.append("0"); }
            sb.append(tmp);
        }
        return sb.toString().toUpperCase();
    }

    // 将16进制字符串转换成字节数组
    private static byte[] hex2byte(String inputString) {

        if (inputString == null || inputString.length() < 2) { return new byte[0]; }
        inputString = inputString.toLowerCase();
        int l = inputString.length() / 2;
        byte[] result = new byte[l];
        for (int i = 0; i < l; ++i) {
            String tmp = inputString.substring(2 * i, 2 * i + 2);
            result[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
        }
        return result;
    }

}


有了这样一个Util类,使用加密就非常方便了,使用方式如下:


        String key = MainApp.getAESkey(); // "781E5E245D69B566"
        String iv = MainApp.getAESiv(); // "9F0885C2D686C418"

        String name = "richie";
        String result = AESCryptUtil.encrypt(name, key, iv);
        Log.e("MainActivity", "result:" + result); // E80A6DD061E8CB53ABED62C7DCCEEEFA

        String name2 = AESCryptUtil.decrypt(result, key, iv);
        Log.e("MainActivity", "name:" + name2); // richie

运行结果为:E80A6DD061E8CB53ABED62C7DCCEEEFA


android加密,php解密 , AES,CBC, PKCS7Padding

最后是我们的PHP端,代码如下:


// "AES/CBC/PKCS7Padding"
define('SECRETKEY', '781E5E245D69B566');
define('SECRETIV', '9F0885C2D686C418');

// 加密方法
function encrypt($str) {
    $str = addPKCS7Padding($str);
    $encrypt_str = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, SECRETKEY, $str, MCRYPT_MODE_CBC, SECRETIV);
    return strToHex($encrypt_str);
}

// 解密方法
function decrypt($str) {
    $str = hexToStr($str);
    $encrypt_str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, SECRETKEY, $str, MCRYPT_MODE_CBC, SECRETIV);
    $encrypt_str = removePKSC7Padding($encrypt_str);
    return $encrypt_str;
}

// 填充算法
function addPKCS7Padding($source) {
    $source = trim($source);
    $block = mcrypt_get_block_size('rijndael-128', 'cbc');
    $pad = $block - (strlen($source) % $block);
    if ($pad <= $block) {
        $char = chr($pad);
        $source .= str_repeat($char, $pad);
    }
    return $source;
}

// 移去填充算法
function removePKSC7Padding($source) {
    $char = substr($source, -1);
    $num = ord($char);
    $source = substr($source, 0, -$num);
    return $source;
}

// 十六进制转字符串
function hexToStr($hex) {
	$string=""; 
	for($i=0;$i<strlen($hex)-1;$i+=2)
	$string.=chr(hexdec($hex[$i].$hex[$i+1]));
	return  $string;
}

// 字符串转十六进制
function strToHex($string) { 
	$hex="";
	$tmp="";
	for($i=0;$i<strlen($string);$i++){
		$tmp = dechex(ord($string[$i]));
		$hex.= strlen($tmp) == 1 ? "0".$tmp : $tmp;
	}
	$hex=strtoupper($hex);
	return $hex;
}

// 加密
$string = encrypt('richie'); // E80A6DD061E8CB53ABED62C7DCCEEEFA
print_r($string);
echo '<hr>';

// 解密
$string = decrypt($string); // richie 
print_r($string);

输出结果为同安卓端是一样的!


android加密,php解密 , AES,CBC, PKCS7Padding

特别需要指出的就是,php和android两者的加密模式必须一致,即:AES/CBC/PKCS7Padding,当然了秘钥和盐值也必须一致。

最后需要指出的是密文的呈现方式,我这里使用16进制,也有人使用base64的,这个原理都一样。