使用DES加密演算法在解密過程的相關問題?

問題:解密的時候是不是最好對加密過程中產生的二進位或者十六進位字元串進行解密操作,而不是ASCII字元串;工作場景:我選擇秘鑰A1,發現對部分密碼解密時候會出錯,其中已知的一個出錯密碼加密後的字元串是「稍N;」,如果我更換秘鑰,那麼這個出錯的密碼就沒有問題,但這樣會不會對別的密碼產生同樣的問題


看到 @玄星 同學來湊熱鬧了,那我也來湊湊熱鬧~

對於中文字元應用加密,確實容易出現解密錯誤的情況。不過出現這種解密錯誤可能並非是因為中文編碼的問題。有可能是以下幾種情況之一:

  • 中文編碼問題。ASCII是對英文中常見的各個字元進行編碼。對於中文來說,一般使用的編碼方式有兩種:UTF-8以及ISO-8859-1。如果選錯了編碼形式,可能會出現無法恢復明文的情況。不過這種情況一般是整個明文都恢復成了亂碼,不太可能出現解密結果有一部分不對的情況。
  • 對於單分組加密解密,明文字元長度超過了分組長度。這將導致尾部數據丟失,導致解密失敗。題主的問題有可能是這個。

  • 分組密碼實現中對於結尾處理不當。這個出現的情況一般來說使用分組加密原始演算法實現加密解密,而非使用程序語言中的流輸入輸出來進行。題主的問題也有可能是這個,因為所指出的一個解密錯誤是句尾。

我個人不太清楚題主用的什麼語言,使用什麼庫函數來實現的DES。我自己是用Java,在此給一個Java的AES實現代碼,以供參考。DES的實現需要把InitVector長度減小為64bit,同時把Key也減小為56bit(還有8bit的校驗)。AESEngine也需要換成DESEngine。我使用的庫是Java的Bouncy Castle庫(The Legion of the Bouncy Castle Java Cryptography APIs)。有關單鑰分組密碼加密解密可以參考我的博客:Java密碼學原型演算法實現——第二部分:單鑰加密演算法。注意,我是在Eclipse下面進行實現的,裡面的編碼格式我設置成了UTF-8。在Java中,我們恢復明文時可以指定編碼形式,即函數new String(byte[] message, String type),參見JDK。

public class SymmetricBlockEnc {
public static final byte[] InitVector = { 0x38, 0x37, 0x36, 0x35, 0x34,
0x33, 0x32, 0x31, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31 };

public enum Mode {
/**
* Electronic CodeBook Mode
*/
ECB,

/**
* Cipher-Block Chaining Mode
*/
CBC,

/**
* Cipher FeedBack Mode
*/

CFB,
/**
* Output FeedBack Mode
*/
OFB,
}

// The default block size in bits (note: a multiple of 8)
private static int DEFAULT_SIZE = 16;

public static byte[] enc_AES(Mode mode, byte[] key, byte[] iv,
byte[] plaintext) {
// Make sure the validity of key, and plaintext
assert (key != null plaintext != null);
// The valid key length is 16Bytes, 24Bytes or 32Bytes
assert (key.length == 16 || key.length == 24 || key.length == 32);
if (mode != Mode.ECB) {
// The valid init vector is a no-none 16Bytes array
assert (iv != null iv.length == 16);
}
try {
KeyParameter kp = new KeyParameter(key);
BufferedBlockCipher b = null;
switch (mode) {
case ECB:
b = new PaddedBufferedBlockCipher(new AESEngine());
b.init(true, kp);
break;
case CBC:
b = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
b.init(true, new ParametersWithIV(kp, iv));
break;
case CFB:
b = new PaddedBufferedBlockCipher(new CFBBlockCipher(
new AESEngine(), DEFAULT_SIZE));
b.init(true, new ParametersWithIV(kp, iv));
break;
case OFB:
b = new PaddedBufferedBlockCipher(new OFBBlockCipher(
new AESEngine(), DEFAULT_SIZE));
b.init(true, new ParametersWithIV(kp, iv));
break;
default:
// Default Mode is ECB Mode
b = new PaddedBufferedBlockCipher(new AESEngine());
b.init(true, kp);
break;
}
byte[] enc = new byte[b.getOutputSize(plaintext.length)];
int size1 = b.processBytes(plaintext, 0, plaintext.length, enc, 0);
int size2;
size2 = b.doFinal(enc, size1);
byte[] ciphertext = new byte[size1 + size2];
System.arraycopy(enc, 0, ciphertext, 0, ciphertext.length);
return ciphertext;
} catch (DataLengthException e) {
e.printStackTrace();
return null;
} catch (IllegalStateException e) {
e.printStackTrace();
return null;
} catch (InvalidCipherTextException e) {
e.printStackTrace();
return null;
}
}

public static byte[] dec_AES(Mode mode, byte[] key, byte[] iv,
byte[] ciphertext) {
// Make sure the validity of key, and plaintext
assert (key != null ciphertext != null);
// The valid key length is 16Bytes, 24Bytes or 32Bytes
assert (key.length == 16 || key.length == 24 || key.length == 32);
if (mode != Mode.ECB) {
// The valid init vector is a no-none 16Bytes array
assert (iv != null iv.length == 16);
}
try {
KeyParameter kp = new KeyParameter(key);
BufferedBlockCipher b = null;
switch (mode) {
case ECB:
b = new PaddedBufferedBlockCipher(new AESEngine());
b.init(false, kp);
break;
case CBC:
b = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
b.init(false, new ParametersWithIV(kp, iv));
break;
case CFB:
b = new PaddedBufferedBlockCipher(new CFBBlockCipher(
new AESEngine(), DEFAULT_SIZE));
b.init(false, new ParametersWithIV(kp, iv));
break;
case OFB:
b = new PaddedBufferedBlockCipher(new OFBBlockCipher(
new AESEngine(), DEFAULT_SIZE));
b.init(false, new ParametersWithIV(kp, iv));
break;
default:
// Default Mode is ECB Mode
b = new PaddedBufferedBlockCipher(new AESEngine());
b.init(false, kp);
break;
}
byte[] dec = new byte[b.getOutputSize(ciphertext.length)];
int size1 = b
.processBytes(ciphertext, 0, ciphertext.length, dec, 0);
int size2 = b.doFinal(dec, size1);
byte[] plaintext = new byte[size1 + size2];
System.arraycopy(dec, 0, plaintext, 0, plaintext.length);
return plaintext;
} catch (DataLengthException e) {
e.printStackTrace();
return null;
} catch (IllegalStateException e) {
e.printStackTrace();
return null;
} catch (InvalidCipherTextException e) {
e.printStackTrace();
return null;
}
}

public static void enc_AES(Mode mode, byte[] key, byte[] iv,
InputStream in, OutputStream out) {
// Make sure the validity of key, and plaintext
assert (key != null in != null out != null);
// The valid key length is 16Bytes, 24Bytes or 32Bytes
assert (key.length == 16 || key.length == 24 || key.length == 32);
if (mode != Mode.ECB) {
// The valid init vector is a no-none 16Bytes array
assert (iv != null iv.length == 16);
}
try {
KeyParameter kp = new KeyParameter(key);
BufferedBlockCipher b = null;
switch (mode) {
case ECB:
b = new PaddedBufferedBlockCipher(new AESEngine());
b.init(true, kp);
break;
case CBC:
b = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
b.init(true, new ParametersWithIV(kp, iv));
break;
case CFB:
b = new PaddedBufferedBlockCipher(new CFBBlockCipher(
new AESEngine(), DEFAULT_SIZE));
b.init(true, new ParametersWithIV(kp, iv));
break;
case OFB:
b = new PaddedBufferedBlockCipher(new OFBBlockCipher(
new AESEngine(), DEFAULT_SIZE));
b.init(true, new ParametersWithIV(kp, iv));
break;
default:
// Default Mode is ECB Mode
b = new PaddedBufferedBlockCipher(new AESEngine());
b.init(true, kp);
break;
}
int inBlockSize = b.getBlockSize() * 10;
int outBlockSize = b.getOutputSize(inBlockSize);
byte[] inblock = new byte[inBlockSize];
byte[] outblock = new byte[outBlockSize];

int inL;
int outL;
byte[] rv = null;

while ((inL = in.read(inblock, 0, inBlockSize)) &> 0) {
outL = b.processBytes(inblock, 0, inL, outblock, 0);

if (outL &> 0) {
rv = Hex.encode(outblock, 0, outL);

out.write(rv, 0, rv.length);
out.write("
");
}
}

outL = b.doFinal(outblock, 0);
if (outL &> 0) {
rv = Hex.encode(outblock, 0, outL);
out.write(rv, 0, rv.length);
out.write("
");
}
} catch (DataLengthException e) {
e.printStackTrace();
return;
} catch (IllegalStateException e) {
e.printStackTrace();
return;
} catch (InvalidCipherTextException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
}
}

public static void dec_AES(Mode mode, byte[] key, byte[] iv,
InputStream in, OutputStream out) {
// Make sure the validity of key, and plaintext
assert (key != null in != null out != null);
// The valid key length is 16Bytes, 24Bytes or 32Bytes
assert (key.length == 16 || key.length == 24 || key.length == 32);
if (mode != Mode.ECB) {
// The valid init vector is a no-none 16Bytes array
assert (iv != null iv.length == 16);
}
try {
KeyParameter kp = new KeyParameter(key);
BufferedBlockCipher b = null;
switch (mode) {
case ECB:
b = new PaddedBufferedBlockCipher(new AESEngine());
b.init(false, kp);
break;
case CBC:
b = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
b.init(false, new ParametersWithIV(kp, iv));
break;
case CFB:
b = new PaddedBufferedBlockCipher(new CFBBlockCipher(
new AESEngine(), DEFAULT_SIZE));
b.init(false, new ParametersWithIV(kp, iv));
break;
case OFB:
b = new PaddedBufferedBlockCipher(new OFBBlockCipher(
new AESEngine(), DEFAULT_SIZE));
b.init(false, new ParametersWithIV(kp, iv));
break;
default:
// Default Mode is ECB Mode
b = new PaddedBufferedBlockCipher(new AESEngine());
b.init(false, kp);
break;
}
BufferedReader br = new BufferedReader(new InputStreamReader(in));

byte[] inblock = null;
byte[] outblock = null;

int outL;
String rv = null;

while ((rv = br.readLine()) != null) {
inblock = Hex.decode(rv);
outblock = new byte[b.getOutputSize(inblock.length)];

outL = b.processBytes(inblock, 0, inblock.length, outblock, 0);
if (outL &> 0) {
out.write(outblock, 0, outL);
}
}
outL = b.doFinal(outblock, 0);
if (outL &> 0) {
out.write(outblock, 0, outL);
}
} catch (DataLengthException e) {
e.printStackTrace();
return;
} catch (IllegalStateException e) {
e.printStackTrace();
return;
} catch (InvalidCipherTextException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
}
}

private static void Test_AES_String() {
String key = "6206c34e2186e752c74e6df32ab8fa5b";
String iv = "00e5d201c2c2acbff8154861242ba0c4";
String iv_p = "00e5d201c2c2acbff8154861242ba0c5";
String message;
byte[] ciphertext, ciphertext_p;
String plaintext, plaintext_p;

// Test ECB Mode
StdOut.println("Test AES with ECB Mode.");
message = "明文消息";
StdOut.println("Message = " + message);
ciphertext = enc_AES(Mode.ECB, Hex.decode(key), null,
message.getBytes());
StdOut.println("Encrypted Ciphertext = " + Hex.toHexString(ciphertext));
plaintext = new String(dec_AES(Mode.ECB, Hex.decode(key), null,
ciphertext));
StdOut.println("Decrypted Plaintext = " + plaintext);
StdOut.println();

// Test CBC Mode
StdOut.println("Test AES with CBC Mode.");
message = "明文消息";
StdOut.println("Message = " + message);
// Test for Correctness
ciphertext = enc_AES(Mode.CBC, Hex.decode(key), Hex.decode(iv),
message.getBytes());
StdOut.println("Encrypted Ciphertext = " + Hex.toHexString(ciphertext));
plaintext = new String(dec_AES(Mode.CBC, Hex.decode(key),
Hex.decode(iv), ciphertext));
StdOut.println("Decrypted Plaintext = " + plaintext);
// Test for Encryption with distinct IV
ciphertext_p = enc_AES(Mode.CBC, Hex.decode(key), Hex.decode(iv_p),
message.getBytes());
StdOut.println("Encrypted Ciphertext = "
+ Hex.toHexString(ciphertext_p));
plaintext_p = new String(dec_AES(Mode.CBC, Hex.decode(key),
Hex.decode(iv_p), ciphertext_p));
StdOut.println("Decrypted Plaintext = " + plaintext_p);
StdOut.println();

// Test CFB Mode
StdOut.println("Test AES with CFB Mode.");
message = "明文消息";
StdOut.println("Message = " + message);
// Test for Correctness
ciphertext = enc_AES(Mode.CFB, Hex.decode(key), Hex.decode(iv),
message.getBytes());
StdOut.println("Encrypted Ciphertext = " + Hex.toHexString(ciphertext));
plaintext = new String(dec_AES(Mode.CFB, Hex.decode(key),
Hex.decode(iv), ciphertext));
StdOut.println("Decrypted Plaintext = " + plaintext);
// Test for Encryption with distinct IV
ciphertext_p = enc_AES(Mode.CFB, Hex.decode(key), Hex.decode(iv_p),
message.getBytes());
StdOut.println("Encrypted Ciphertext = "
+ Hex.toHexString(ciphertext_p));
plaintext_p = new String(dec_AES(Mode.CFB, Hex.decode(key),
Hex.decode(iv_p), ciphertext_p));
StdOut.println("Decrypted Plaintext = " + plaintext_p);
StdOut.println();

// Test OFB Mode
StdOut.println("Test AES with OFB Mode.");
message = "明文消息";
StdOut.println("Message = " + message);
// Test for Correctness
ciphertext = enc_AES(Mode.OFB, Hex.decode(key), Hex.decode(iv),
message.getBytes());
StdOut.println("Encrypted Ciphertext = " + Hex.toHexString(ciphertext));
plaintext = new String(dec_AES(Mode.OFB, Hex.decode(key),
Hex.decode(iv), ciphertext));
StdOut.println("Decrypted Plaintext = " + plaintext);
// Test for Encryption with distinct IV
ciphertext_p = enc_AES(Mode.OFB, Hex.decode(key), Hex.decode(iv_p),
message.getBytes());
StdOut.println("Encrypted Ciphertext = "
+ Hex.toHexString(ciphertext_p));
plaintext_p = new String(dec_AES(Mode.OFB, Hex.decode(key),
Hex.decode(iv_p), ciphertext_p));
StdOut.println("Decrypted Plaintext = " + plaintext_p);
StdOut.println();
}

public static void Test_AES_File() {
try {
String key = "6206c34e2186e752c74e6df32ab8fa5b";
String iv = "00e5d201c2c2acbff8154861242ba0c4";
File fileIn = new File("docs1.5on.zip");
File fileEnc = new File("docs1.5on.enc");
File fileDec = new File("docs1.5on.dec");
FileInputStream in = new FileInputStream(fileIn);
FileOutputStream out = new FileOutputStream(fileEnc);

enc_AES(Mode.CBC, Hex.decode(key), Hex.decode(iv), in, out);
in.close();
out.close();

in = new FileInputStream(fileEnc);
out = new FileOutputStream(fileDec);

dec_AES(Mode.CBC, Hex.decode(key), Hex.decode(iv), in, out);
in.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Test_AES_String();
// Test_AES_File();
}
}


最好是把字元串進行Base64編碼後,再轉為Byte Array類似的數據形式「喂「給DES。

解密的方式就是先用DES解密,然後把數據再進行Base64 Decoding。

因為DES是在bit上進行操作,所以密文不一定是能正確顯示的字元串。(一個字元就算只動1個bit,都可能無法顯示)


當然是0和1構成的十六進位或者二進位的字元串


是具體軟體的問題?


推薦閱讀:

如何創建個人密碼或密語?
能用cookie找回密碼嗎?
現代的信息加密方式是怎樣的?
用什麼方式記密碼最好?
如何判斷一段聲音是不是自然語言?

TAG:密碼加密 | 密碼學 | 密碼編碼學與網路安全 |