2016年10月26日 星期三

PHP、Java 的 Des 加密(ECB mode)

PHP 和 Java 間用 Des 加密(ECB mode) 來傳輸資料,要確保兩邊用同樣的key加解密的結果一樣。

PHP code:
/**
 * DES 加解密(ECB mode)
 */
class DesCrypt {

    private $key;

    /**
     * DES 加解密(ECB mode)
     * @param string $key 加密的key
     */
    public function __construct($key) {
        $this->key = $key;
    }

    /**
     * 加密(ECB mode)
     * @param string $str 要加密的資料
     * @return string
     * @throws \Exception
     */
    public function encrypt($str) {
        //獲得加密算法的分組大小
        $size = mcrypt_get_block_size('des', 'ecb');
        //填充不足字節數,傳入數據長度必須是 n * 分組大小
        $str = $this->pkcs5Pad($str, $size);
        $key = $this->key;
        //打開算法和模組對應的模塊
        $td = mcrypt_module_open('des', '', 'ecb', '');
        $key_max_len = mcrypt_enc_get_key_size($td);
        if (strlen($key) > $key_max_len) {
            throw new \Exception("key超過最大長度:{$key_max_len}");
        }
        //建立初始向量
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        //初始化加密所需的緩衝區
        //在 ECB 模式下,初始向量會被忽略, 在 CFB,CBC,STREAM,nOFB 和 OFB 模式下,必須提供初始向量
        mcrypt_generic_init($td, $key, $iv);
        //加密數據
        $data = mcrypt_generic($td, $str);
        //清理指定加密模組緩衝區
        mcrypt_generic_deinit($td);
        //關閉加密模組
        mcrypt_module_close($td);
        //將加密結果用base64編碼
        $data = base64_encode($data);
        return $data;
    }

    /**
     * 解密(ECB mode)
     * @param string $str 要解密的密文
     * @return string
     * @throws \Exception
     */
    public function decrypt($str) {
        //將base64編碼結果解開
        $str = base64_decode($str);
        $key = $this->key;
        //獲得加密算法的分組大小
        $td = mcrypt_module_open('des', '', 'ecb', '');
        $key_max_len = mcrypt_enc_get_key_size($td);
        if (strlen($key) > $key_max_len) {
            throw new \Exception("key超過最大長度:{$key_max_len}");
        }
        //建立初始向量
        $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        //$ks = mcrypt_enc_get_key_size($td);
        //初始化加密所需的緩衝區
        mcrypt_generic_init($td, $key, $iv);
        //解密數據
        $decrypted = mdecrypt_generic($td, $str);
        //清理指定加密模組緩衝區
        mcrypt_generic_deinit($td);
        //關閉加密模組
        mcrypt_module_close($td);

        //去除 pkcs5Pad 的填充值
        $result = $this->pkcs5Unpad($decrypted);
        return $result;
    }

    /**
     * 填充不足字節數
     * 1.將填充量取chr當填充值
     * 2.剛好滿也再填充一組
     * @param string $text
     * @param int $blocksize
     * @return string
     */
    private function pkcs5Pad($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text . str_repeat(chr($pad), $pad);
    }

    /**
     * 去除 pkcs5Pad 的填充值
     * @param string $text
     * @return boolean
     * @return string
     */
    private function pkcs5Unpad($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);
    }

}
$Des = new DesCrypt("12345678");
$encrypt_res = $Des->encrypt("test data"); //l9TN9Ln/71IvFRjlhD8PaQ==
$Des->decrypt($encrypt_res); //test data
註:PHP要確定有裝 php-mcrypt
$ yum install php-mcrypt



Java Code:
import javax.crypto.*;
import java.security.*;
import javax.crypto.spec.*;

/**
 * DES算法
 *
 * @author user
 */
public class DES {

    /**
     * 加密函數
     *
     * @param data 加密數據
     * @param key 密鑰
     * @return 返回加密後的數據
     */
    public static byte[] encrypt(byte[] data, byte[] key) {

        try {

            // DES算法要求有一個可信任的隨機數源
            SecureRandom sr = new SecureRandom();

            // 從原始密鑰數據創建DESKeySpec對象
            DESKeySpec dks = new DESKeySpec(key);

            // 創建一個密匙工廠,然後用它把DESKeySpec轉換成
            // 一個SecretKey對象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(dks);

            // using DES in ECB mode
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");

            // 用密匙初始化Cipher對象
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, sr);

            // 執行加密操作
            byte encryptedData[] = cipher.doFinal(data);

            return encryptedData;
        } catch (Exception e) {
            System.err.println("DES算法,加密數據出錯!");
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 解密函數
     *
     * @param data 解密數據
     * @param key 密鑰
     * @return 返回解密後的數據
     */
    public static byte[] decrypt(byte[] data, byte[] key) {
        try {
            // DES算法要求有一個可信任的隨機數源
            SecureRandom sr = new SecureRandom();

            // byte rawKeyData[] = /* 用某種方法獲取原始密匙數據 */;
            // 從原始密匙數據創建一個DESKeySpec對象
            DESKeySpec dks = new DESKeySpec(key);

            // 創建一個密匙工廠,然後用它把DESKeySpec對象轉換成
            // 一個SecretKey對象
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey secretKey = keyFactory.generateSecret(dks);

            // using DES in ECB mode
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");

            // 用密匙初始化Cipher對象
            cipher.init(Cipher.DECRYPT_MODE, secretKey, sr);

            // 正式執行解密操作
            byte decryptedData[] = cipher.doFinal(data);

            return decryptedData;
        } catch (Exception e) {
            System.err.println("DES算法,解密出錯。");
            e.printStackTrace();
        }

        return null;
    }
}
import javax.xml.bind.*;

public class desTest {

public static void main(String[] args) {
    String key = "12345678"; //設定加密 key
    String plain = "test data";//加密內容明文

    byte[] key_byte = key.getBytes();
    byte[] plain_byte = plain.getBytes();
    //ECB mode 加密
    byte[] data_ecb = DES.encrypt(plain_byte, key_byte);
    //加密結果經BASE64編碼
    String base64String = DatatypeConverter.printBase64Binary(data_ecb);
    System.out.println("密文經過BASE64編碼:" + base64String);

    try {
        byte[] b = DatatypeConverter.parseBase64Binary(base64String);
        System.out.println("解密後明文:" + new String(DES.decrypt(b, key_byte)));
    } catch (Exception ex) {
        System.out.println(ex.getMessage());
        System.out.println(ex.toString());
    }
}

}
//輸出
密文經過BASE64編碼:l9TN9Ln/71IvFRjlhD8PaQ==
解密後明文:test data



參考、節錄:
在JAVA中使用DES算法 - 张兴业 - 博客园
java - how to avoid warning for the Base 64? - Stack Overflow
[ Java 文章收集 ] Java如何進行Base64的編碼(Encode)與解碼(Decode)?
DES加密模式详解(zz) - Wayne - BlogJava
java php DES 加密解密 - bask之家 -- 思则远 - ITeye技术网站
php des 加密解密实例 - clh604的专栏 - 博客频道 - CSDN.NET (AES加密範例)
使用php mcrypt加密解密
Encryption operating modes: ECB vs CBC
PHP通用DES加密算法
javaDESEncrypt.rar DESEncrypt.java


沒有留言:

張貼留言