資料加密标準(DES,Data Encryption Standard,簡寫DES)是一種采用塊密碼加密的對稱密鑰算法,加密分組長度為64位,其中8、16、24、32、40、48、56、64 等8位是校驗位,其餘56位作為秘鑰。
DES加密解密需要一個秘鑰,使用這個秘鑰加密的資料隻能使用這個秘鑰解密,這樣,如果兩個用戶端在傳輸資料前先使用DES進行加密,那麼就算傳輸密文被竊取,不知道秘鑰就沒辦法解密,這就保證了資料的安全性。
DES是比較的老的加密算法,現已被證明可被破解,能不用就不用吧,可使用AES加密算法代替:AES對稱加密算法實作:Java,C#,Golang,Python。
下面使用介紹各語言中的DES加密解密實作:
聲明
1、加密解密過程分别使用比較多的ECB和CBC兩種方式來實作,ECB性能更快,但是安全性沒有CBC好,是以目前CBC用的比較多
2、加密解密填充方式采用PKCS7,或者PKCS5,這兩者在這裡的加密解密過程中的結果可以認為是一樣的
3、CBC模式需要秘鑰及初始向量,而ECB模式隻需要秘鑰即可,是以為了友善,下面封裝的方法或者函數将通過判斷是否存在初始向量來決定是使用CBC模式還是使用ECB模式
Java
Java中預設采用ECB模式,可以先做一個封裝:
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class DesUtil {
/**
* DES加密
*
* @param value
* 待機加密明文
* @param secretKey
* 秘鑰
* @param iv
* 初始向量,如果未空,則将采用ECB模式,否則采用CBC模式
*
* @return 密文
*/
public static String desEncrypt(String value, String secretKey, String iv)
throws Exception {
if (value == null || value.length() == 0)
return "";
Cipher cipher = initCipher(Cipher.ENCRYPT_MODE, secretKey, iv);
byte[] bytes = cipher.doFinal(value.getBytes("utf-8"));
// 使用hex格式資料輸出
StringBuffer result = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
result.append(String.format("%02x", bytes[i]));
}
return result.toString();
}
/**
* DES解密
*
* @param value
* 密文
* @param secretKey
* 秘鑰
* @param iv
* 初始向量,如果未空,則将采用ECB模式,否則采用CBC模式
*
* @return 解密後的明文
*/
public static String desDecrypt(String value, String secretKey, String iv)
throws Exception {
if (value == null || value.length() == 0)
return "";
// 轉換hex格式資料未byte數組
int length = value.length();
byte[] buffer = new byte[length / 2];
for (int i = 0; i < buffer.length; i++) {
buffer[i] = (byte) Integer.parseInt(
value.substring(i * 2, i * 2 + 2), 16);
}
Cipher cipher = initCipher(Cipher.DECRYPT_MODE, secretKey, iv);
buffer = cipher.doFinal(buffer);
return new String(buffer, "utf-8");
}
private static Cipher initCipher(int mode, String secretKey, String iv)
throws Exception {
try {
// 擷取秘鑰數組
byte[] secretBytes = secretKey.getBytes("utf-8");
byte[] keyBytes = new byte[8];
System.arraycopy(secretBytes, 0, keyBytes, 0,
Math.min(secretBytes.length, keyBytes.length));
DESKeySpec dks = new DESKeySpec(keyBytes);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
Key key = keyFactory.generateSecret(dks);
if (iv == null || iv.length() == 0) {
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");// 采用ECB方式
cipher.init(mode, key);
return cipher;
} else {
// 初始向量數組
byte[] ivBytes = iv.getBytes("utf-8");
byte[] ivKeyBytes = new byte[8];
System.arraycopy(ivBytes, 0, ivKeyBytes, 0,
Math.min(ivBytes.length, ivKeyBytes.length));
IvParameterSpec ivp = new IvParameterSpec(ivKeyBytes);
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");// 采用CBC方式
cipher.init(mode, key, ivp);
return cipher;
}
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
DesUtil
然後我們可以調用這個封裝好的類去進行加密解密:
public static void main(String[] args) {
String text = "上山打老虎";
String key = "123456";// 秘鑰
String iv = "abcdefg";// 初始向量
try {
String encryptText1 = DesUtil.desEncrypt(text, key, iv);
System.out.printf("【%s】經過【DES-CBC】加密後:%s\n", text, encryptText1);
String decryptText1 = DesUtil.desDecrypt(encryptText1, key, iv);
System.out.printf("【%s】經過【DES-CBC】解密後:%s\n", encryptText1,
decryptText1);
String encryptText2 = DesUtil.desEncrypt(text, key, null);
System.out.printf("【%s】經過【DES-ECB】加密後:%s\n", text, encryptText2);
String decryptText2 = DesUtil.desDecrypt(encryptText2, key, null);
System.out.printf("【%s】經過【DES-ECB】解密後:%s\n", encryptText2,
decryptText2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
執行結果:
C#
C#中預設采用CBC模式,同樣做一個封裝類:
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleApp1
{
public class DesHelper
{
#region DES
/// <summary>
/// 建立算法操作對象
/// </summary>
/// <param name="secretKey">秘鑰</param>
/// <param name="iv">初始向量為空則采用ECB,否則采用CBC</param>
/// <returns></returns>
private static DESCryptoServiceProvider CreateCryptoServiceProvider(string secretKey, string iv)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
//秘鑰數組
byte[] secretBytes = Encoding.UTF8.GetBytes(secretKey);
byte[] keyBytes = new byte[8];
Array.Copy(secretBytes, keyBytes, Math.Min(secretBytes.Length, keyBytes.Length));
des.Key = keyBytes;
if (!string.IsNullOrEmpty(iv))
{
//初始向量數組
byte[] array = Encoding.UTF8.GetBytes(iv);
byte[] ivBytes = new byte[keyBytes.Length];
Array.Copy(array, ivBytes, Math.Min(array.Length, ivBytes.Length));
des.IV = ivBytes;
des.Mode = CipherMode.CBC;//CBC模式
}
else
{
des.Mode = CipherMode.ECB;//ECB模式
}
des.Padding = PaddingMode.PKCS7;//填充方式
return des;
}
/// <summary>
/// Des加密
/// </summary>
/// <param name="value">待加密的明文</param>
/// <param name="secretKey">秘鑰</param>
/// <param name="iv">初始向量為空則采用ECB,否則采用CBC</param>
/// <returns>密文</returns>
public static string DesEncrypt(string value, string secretKey, string iv)
{
if (string.IsNullOrEmpty(value)) return string.Empty;
using (DESCryptoServiceProvider des = CreateCryptoServiceProvider(secretKey, iv))
{
byte[] buffer = Encoding.UTF8.GetBytes(value);
System.IO.MemoryStream ms = new System.IO.MemoryStream();
using (CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
cs.FlushFinalBlock();
buffer = ms.ToArray();
//使用hex格式資料輸出
StringBuilder result = new StringBuilder();
foreach (byte b in buffer)
{
result.AppendFormat("{0:x2}", b);
}
return result.ToString();
//或者使用下面的輸出
//return BitConverter.ToString(buffer).Replace("-", "").ToLower();
}
}
}
/// <summary>
/// Des解密
/// </summary>
/// <param name="value">密文</param>
/// <param name="secretKey">秘鑰</param>
/// <param name="iv">初始向量為空則采用ECB,否則采用CBC</param>
/// <returns>解密後的明文</returns>
public static string DesDecrypt(string value, string secretKey, string iv)
{
if (string.IsNullOrEmpty(value)) return string.Empty;
using (DESCryptoServiceProvider des = CreateCryptoServiceProvider(secretKey, iv))
{
//轉換hex格式資料為byte數組
byte[] buffer = new byte[value.Length / 2];
for (var i = 0; i < buffer.Length; i++)
{
buffer[i] = (byte)Convert.ToInt32(value.Substring(i * 2, 2), 16);
}
System.IO.MemoryStream ms = new System.IO.MemoryStream();
using (CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(buffer, 0, buffer.Length);
cs.FlushFinalBlock();
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
#endregion
}
}
DesHelper
然後測試加密解密:
static void Main(string[] args)
{
string text = "上山打老虎";
string key = "123456"; // 秘鑰
string iv = "abcdefg"; // 初始向量
string encryptText1 = DesHelper.DesEncrypt(text, key, iv);
Console.WriteLine($"【{text}】經過【DES-CBC】加密後:{encryptText1}");
string decryptText1 = DesHelper.DesDecrypt(encryptText1, key, iv);
Console.WriteLine($"【{encryptText1}】經過【DES-CBC】解密後:{decryptText1}");
string encryptText2 = DesHelper.DesEncrypt(text, key, null);
Console.WriteLine($"【{text}】經過【DES-ECB】加密後:{encryptText2}");
string decryptText2 = DesHelper.DesDecrypt(encryptText2, key, null);
Console.WriteLine($"【{encryptText2}】經過【DES-ECB】解密後:{decryptText2}");
}
Golang
可能因為ECB模式不太安全,所有go中并沒有封裝好ECB模式的直接加密解密過程,但是我們還是可以實作:
package main
import (
"bytes"
"crypto/cipher"
"crypto/des"
"fmt"
"strconv"
)
//DES加密
//iv為空則采用ECB模式,否則采用CBC模式
func DesEncrypt(value, secretKey, iv string) (string, error) {
if value == "" {
return "", nil
}
//根據秘鑰生成8位的秘鑰切片
keyBytes := make([]byte, des.BlockSize)
copy(keyBytes, []byte(secretKey))
//擷取block
block, err := des.NewCipher(keyBytes)
if err != nil {
return "", err
}
blocksize := block.BlockSize()
valueBytes := []byte(value)
//填充
fillsize := blocksize - len(valueBytes)%blocksize
repeat := bytes.Repeat([]byte{byte(fillsize)}, fillsize)
valueBytes = append(valueBytes, repeat...)
result := make([]byte, len(valueBytes))
//加密
if iv == "" {
temp := result
for len(valueBytes) > 0 {
block.Encrypt(temp, valueBytes[:blocksize])
valueBytes = valueBytes[blocksize:]
temp = temp[blocksize:]
}
} else {
//向量切片
ivBytes := make([]byte, des.BlockSize)
copy(ivBytes, []byte(iv))
encrypter := cipher.NewCBCEncrypter(block, ivBytes)
encrypter.CryptBlocks(result, valueBytes)
}
//以hex格式數值輸出
encryptText := fmt.Sprintf("%x", result)
return encryptText, nil
}
//DES解密
//iv為空則采用ECB模式,否則采用CBC模式
func DesDecrypt(value, secretKey, iv string) (string, error) {
if value == "" {
return "", nil
}
//根據秘鑰生成8位的秘鑰切片
keyBytes := make([]byte, des.BlockSize)
copy(keyBytes, []byte(secretKey))
//擷取block
block, err := des.NewCipher(keyBytes)
if err != nil {
return "", err
}
//将hex格式資料轉換為byte切片
valueBytes := []byte(value)
var encryptedData = make([]byte, len(valueBytes)/2)
for i := 0; i < len(encryptedData); i++ {
b, err := strconv.ParseInt(value[i*2:i*2+2], 16, 10)
if err != nil {
return "", err
}
encryptedData[i] = byte(b)
}
result := make([]byte, len(encryptedData))
if iv == "" {
blocksize := block.BlockSize()
temp := result
for len(encryptedData) > 0 {
block.Decrypt(temp, encryptedData[:blocksize])
encryptedData = encryptedData[blocksize:]
temp = temp[blocksize:]
}
} else {
//向量切片
ivBytes := make([]byte, des.BlockSize)
copy(ivBytes, []byte(iv))
//解密
blockMode := cipher.NewCBCDecrypter(block, ivBytes)
blockMode.CryptBlocks(result, encryptedData)
}
//取消填充
unpadding := int(result[len(result)-1])
result = result[:(len(result) - unpadding)]
return string(result), nil
}
desutil.go
調用測試:
package main
import (
"fmt"
"testing"
)
func TestDes(t *testing.T) {
text := "上山打老虎"
key := "123456" // 秘鑰
iv := "abcdefg" // 初始向量
encryptText1, _ := DesEncrypt(text, key, iv)
fmt.Printf("【%s】經過【DES-CBC】加密後:%s\n", text, encryptText1)
decryptText1, _ := DesDecrypt(encryptText1, key, iv)
fmt.Printf("【%s】經過【DES-CBC】解密後:%s\n", encryptText1, decryptText1)
encryptText2, _ := DesEncrypt(text, key, "")
fmt.Printf("【%s】經過【DES-ECB】加密後:%s\n", text, encryptText2)
decryptText2, _ := DesDecrypt(encryptText2, key, "")
fmt.Printf("【%s】經過【DES-ECB】解密後:%s\n", encryptText2, decryptText2)
}
Python
python一般需要安裝pyDes包來實作DES加密解密,可以使用pip來安裝:
pip install pyDes
預設也是采用ECB模式,是以要多注意,python相對簡單,封裝測試就放一起了:
# 需要安裝pyDes包:pip install pyDes
from pyDes import des, PAD_PKCS5, CBC, ECB
BLOCK_SIZE = 8
def init_cipher(secret_key, iv_key):
secret_bytes = secret_key.encode(encoding="utf-8")
key_bytes = []
if len(secret_bytes) >= BLOCK_SIZE:
key_bytes = secret_bytes[:BLOCK_SIZE]
else:
key_bytes.extend(secret_bytes)
key_bytes.extend([0 for x in range(0, BLOCK_SIZE - len(secret_bytes))])
if iv_key is None or len(iv_key) == 0:
cipher = des(key_bytes, ECB, None, padmode=PAD_PKCS5)
return cipher
else:
iv_bytes = iv_key.encode(encoding="utf-8")
iv_key_bytes = []
if len(iv_bytes) >= BLOCK_SIZE:
iv_key_bytes = iv_bytes[:BLOCK_SIZE]
else:
iv_key_bytes.extend(iv_bytes)
iv_key_bytes.extend([0 for x in range(0, BLOCK_SIZE - len(iv_bytes))])
cipher = des(key_bytes, CBC, iv_key_bytes, padmode=PAD_PKCS5)
return cipher
def des_encrypt(value, secret_key, iv_key):
cipher = init_cipher(secret_key, iv_key)
buffer = value.encode(encoding="utf-8")
buffer = cipher.encrypt(buffer)
return buffer.hex() # 使用hex格式輸出
def des_decrypt(value, secret_key, iv_key):
cipher = init_cipher(secret_key, iv_key)
buffer = bytes.fromhex(value) # 讀取hex格式資料
buffer = cipher.decrypt(buffer)
return buffer.decode("utf-8")
text = "上山打老虎"
key = "123456" # 秘鑰
iv = "abcdefg" # 初始向量
encryptText1 = des_encrypt(text, key, iv)
print("【", text, "】經過【DES-CBC】加密後:", encryptText1)
decryptText1 = des_decrypt(encryptText1, key, iv)
print("【", encryptText1, "】經過【DES-CBC】解密後:", decryptText1)
encryptText2 = des_encrypt(text, key, None)
print("【", text, "】經過【DES-ECB】加密後:", encryptText2)
decryptText2 = des_decrypt(encryptText2, key, None)
print("【", encryptText2, "】經過【DES-ECB】解密後:", decryptText2)
一個專注于.NetCore的技術小白