Java 为什么我的简化DES实现在Cp1252编码下工作良好,但在UTF-8下却不工作?

Java 为什么我的简化DES实现在Cp1252编码下工作良好,但在UTF-8下却不工作?,java,encryption,utf-8,cp1252,Java,Encryption,Utf 8,Cp1252,我昨天问了下面的问题,但由于我没有包括任何关于我实际问题的细节,所以没有引起太多的注意 我会尽可能多地分析我的问题,以便让你对正在发生的事情有一个清晰的了解 我有一个大学项目,我应该实现简化的DES算法用于教育目的。该算法是一种加密算法,使用10位密钥加密8位数据 在实现中,我想包括加密任何字符串 所以我写了8位加密的代码,它对所有类型的输入都非常有效。为了包含字符串加密支持,我使用了函数String.getBytes(),将字符串的所有字节保存在变量byte[]data 然后我遵循这个逻辑:

我昨天问了下面的问题,但由于我没有包括任何关于我实际问题的细节,所以没有引起太多的注意

我会尽可能多地分析我的问题,以便让你对正在发生的事情有一个清晰的了解

我有一个大学项目,我应该实现简化的DES算法用于教育目的。该算法是一种加密算法,使用10位密钥加密8位数据

在实现中,我想包括加密任何字符串

所以我写了8位加密的代码,它对所有类型的输入都非常有效。为了包含字符串加密支持,我使用了函数
String.getBytes()
,将字符串的所有字节保存在变量
byte[]data

然后我遵循这个逻辑:

int i;
for(i=0; i< data.length; i++)
    data[i] = encrypt(data[i]);
int i;
for(i=0; i< data.length; i++)
    data[i] = encrypt(data[i]);
通过使用默认编码Cp1252。我尝试加密字符串,结果如下:

Initial Text: INFO BOB 57674
Encrypted Text: ÅO [áa[aá»j×jt
Decrypted Text: INFO BOB 57674
Initial Text(binary): 01001001 01001110 01000110 01001111 00100000 01000010 01001111 01000010 00100000 00110101 00110111 00110110 00110111 00110100
Encrypted Text(binary): 11000101 01001111 00100000 01011011 11100001 01100001 01011011 01100001 11100001 10111011 01101010 11010111 01101010 01110100
Decrypted Text(binary): 01001001 01001110 01000110 01001111 00100000 01000010 01001111 01000010 00100000 00110101 00110111 00110110 00110111 00110100
Initial Text: INFO BOB 57674
Encrypted Text: �O [�a[a�j�jt
Decrypted Text: ���NFO���BOB���7���74
为了在每次加密和解密数据时看到实际的位表示,我创建了以下函数以显示每个字符串的所有数据:

public void show(byte[] data){
    //εμφάνιση των 
    //note how the Greek letters aren't displayed at all under Cp1252

    int i;
    for(i=0;i<data.length;i++){

        short mask = (short) (1<<7); //10000000
        while(mask>0){
            if((data[i]&mask) == 0)
                System.out.print("0");
            else
                System.out.print("1");

            mask = (short) (mask >> 1);
        }
        if(i < data.length - 1){

            System.out.print(" ");
        }
    }
    System.out.println();
}
似乎一切都在按预期进行。为了在代码编辑器中支持希腊字母,我不得不将编码改为UTF-8

再次运行所有操作后,我得到以下结果:

Initial Text: INFO BOB 57674
Encrypted Text: ÅO [áa[aá»j×jt
Decrypted Text: INFO BOB 57674
Initial Text(binary): 01001001 01001110 01000110 01001111 00100000 01000010 01001111 01000010 00100000 00110101 00110111 00110110 00110111 00110100
Encrypted Text(binary): 11000101 01001111 00100000 01011011 11100001 01100001 01011011 01100001 11100001 10111011 01101010 11010111 01101010 01110100
Decrypted Text(binary): 01001001 01001110 01000110 01001111 00100000 01000010 01001111 01000010 00100000 00110101 00110111 00110110 00110111 00110100
Initial Text: INFO BOB 57674
Encrypted Text: �O [�a[a�j�jt
Decrypted Text: ���NFO���BOB���7���74
注意解密文本中的一些单词是如何正确显示的,例如
NFO
BOB
。在我看来,位操作似乎存在某种问题,好像Eclipse无法识别遵循UTF-8规则的位序列

以下是二进制形式的结果:

Initial Text(binary): 01001001 01001110 01000110 01001111 00100000 01000010 01001111 01000010 00100000 00110101 00110111 00110110 00110111 00110100
Encrypted Text(binary): 11101111 10111111 10111101 01001111 00100000 01011011 11101111 10111111 10111101 01100001 01011011 01100001 11101111 10111111 10111101 01101010 11101111 10111111 10111101 01101010 01110100
Decrypted Text(binary): 11101111 10111111 10111101 11101111 10111111 10111101 11101111 10111111 10111101 01001110 01000110 01001111 11101111 10111111 10111101 11101111 10111111 10111101 11101111 10111111 10111101 01000010 01001111 01000010 11101111 10111111 10111101 11101111 10111111 10111101 11101111 10111111 10111101 00110111 11101111 10111111 10111101 11101111 10111111 10111101 11101111 10111111 10111101 00110111 00110100
现在我可以清楚地看到这个问题了。看起来UTF-8向字符串添加了更多字节。但是我不知道为什么。我的意思是,初始文本似乎有相同数量的字节,那么为什么在加密后添加这些字节,而在解密后添加更多的字节呢


如果您能提供任何帮助,我将不胜感激。提前谢谢你

每次执行
String.getBytes()
,都会隐式使用平台默认编码将字符转换为字节。如果字符串包含无法使用平台默认编码表示的字符,则会丢失信息。因此,使用支持地球上每个字符的显式编码,比如UTF8:
string.getBytes(“UTF8”)

类似地,在执行
新建字符串(字节)
时,使用平台的默认编码将字节转换为字符。如果字节实际上是使用另一种编码的文本编码,或者根本不是字符,而是纯粹的二进制信息,那么也会丢失信息

加密是一种二进制操作。它接受字节并返回其他字节。无论编码是什么,都不能盲目地将字节转换为字符,因为并非所有字节都表示有效字符。如果要将二进制信息(如加密文本)转换为字符串,请使用十六进制或Base64编码

因此,加密过程应该是:

String clearText = ...:
byte[] clearTextAsBytes = clearText.getBytes("UTF8");
byte[] encryptedBinary = encrypt(clearTextAsBytes);
String encryptedBinaryAsPrintableChars = toBase64(encryptedBinary);
解密过程应该是对称的:

String encryptedBinaryAsPrintableChars = ...;
byte[] encryptedBinary  = fromBase64(encryptedBinaryAsPrintableChars);
byte[] decryptedTextAsBytes = decrypt(encryptedBinary);
String decryptedText = new String(decryptedTextAsBytes, "UTF8");

每次执行
String.getBytes()
,都会隐式使用平台默认编码将字符转换为字节。如果字符串包含无法使用平台默认编码表示的字符,则会丢失信息。因此,使用支持地球上每个字符的显式编码,比如UTF8:
string.getBytes(“UTF8”)

类似地,在执行
新建字符串(字节)
时,使用平台的默认编码将字节转换为字符。如果字节实际上是使用另一种编码的文本编码,或者根本不是字符,而是纯粹的二进制信息,那么也会丢失信息

加密是一种二进制操作。它接受字节并返回其他字节。无论编码是什么,都不能盲目地将字节转换为字符,因为并非所有字节都表示有效字符。如果要将二进制信息(如加密文本)转换为字符串,请使用十六进制或Base64编码

因此,加密过程应该是:

String clearText = ...:
byte[] clearTextAsBytes = clearText.getBytes("UTF8");
byte[] encryptedBinary = encrypt(clearTextAsBytes);
String encryptedBinaryAsPrintableChars = toBase64(encryptedBinary);
解密过程应该是对称的:

String encryptedBinaryAsPrintableChars = ...;
byte[] encryptedBinary  = fromBase64(encryptedBinaryAsPrintableChars);
byte[] decryptedTextAsBytes = decrypt(encryptedBinary);
String decryptedText = new String(decryptedTextAsBytes, "UTF8");

没错,如果他使用Findbugs这样的静态代码分析工具,这个漏洞(使用
getBytes()
而不是
getBytes(String)
)会立即被报告。这并不是说有多大区别,但是类型现在提供了预定义的变量,用于类型安全方法/构造函数。非常感谢您的清晰解释,我的问题终于解决了!关于字符串处理,您也可以在这里查看我的答案:。是的,如果他使用静态代码分析工具(如Findbugs),则会立即报告此缺陷(使用
getBytes()
而不是
getBytes(string)
),但是类型现在提供了预定义的变量,用于类型安全方法/构造函数。非常感谢您的清晰解释,我的问题终于解决了!对于字符串处理,您也可以在此处查看我的答案:。