加密和解密具有特殊字符的字符串

时间:2013-01-07 11:46:57

标签: java encryption

我正在尝试加密客户端名称(字符串格式),将其存储在数据库中,然后检索并解密它。因为我需要避免使用任何第三方库,所以我使用了Java发行版中可用的类。

这个过程运行正常,直到我遇到一个带有特殊字符的名字(Ascii:48910)。这是一个问号(?)。加密和解密很顺利,但在解密后,特殊字符被替换为问号。

所以我将编码格式从'UTF-8'更改为'ISO-8859-1'。这解决了显示问题,但解密后仍然会替换特殊字符。

正在使用的代码和输出如下(我删除了不必要的代码):

package crypt;

import java.io.PrintStream;
import java.nio.charset.Charset;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.xml.bind.DatatypeConverter;

public class SecretKeyEncryptionExample {

    private static final String FORMAT = "ISO-8859-1";
    public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";

    private KeySpec ks;
    private SecretKeyFactory skf;
    private Cipher cipher;
    SecretKey key;

    public SecretKeyEncryptionExample() throws Exception {

        String myEncryptionKey = "4A144BEBF7E5E7B7DCF26491AE79C54C768C514CF1547D23";

        ks = new DESedeKeySpec(myEncryptionKey.getBytes(FORMAT));
        skf = SecretKeyFactory.getInstance(DESEDE_ENCRYPTION_SCHEME);
        cipher = Cipher.getInstance(DESEDE_ENCRYPTION_SCHEME);
        key = skf.generateSecret(ks);
    }

    public String encrypt(String unencryptedString) throws Exception {

        String encryptedString = null;
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] plainText = unencryptedString.getBytes(FORMAT);
        byte[] encryptedText = cipher.doFinal(plainText);
        encryptedString = DatatypeConverter.printBase64Binary(encryptedText);

        return encryptedString;
    }

    public String decrypt(String encryptedString)  throws Exception {

        String decryptedText = null;
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] encryptedText = DatatypeConverter.parseBase64Binary(encryptedString);
        byte[] plainText = cipher.doFinal(encryptedText);
        decryptedText = new String(plainText);

        return decryptedText;
    }

    public static void main(String args[]) throws Exception {

        SecretKeyEncryptionExample td = new SecretKeyEncryptionExample();

        String target = "Expendable" + getSpecialCharacter(49810) + "s Pte Ltd";

        String encrypted = td.encrypt(target);
        String decrypted = td.decrypt(encrypted);

        PrintStream out = new PrintStream(System.out, true, FORMAT);
        out.println("String To Encrypt: " + target);
        out.println("Encrypted String: " + encrypted);
        out.println("Decrypted String: " + decrypted);

    }

    public static String getSpecialCharacter(int code) {

        Charset charSet = Charset.forName(FORMAT);
        String specialCharacter = new String(new byte[] { (byte) code }, charSet);
        specialCharacter = String.format("%s", specialCharacter);

        return specialCharacter;
    }

}

输出:

String To Encrypt: Expendable’s Pte Ltd
Encrypted String: TAAJuF7KOmBZHBXFHsW0FB9YBwH7Tcif
Decrypted String: Expendable?s Pte Ltd

请告知如何解密,而无需更换特殊字符。

2 个答案:

答案 0 :(得分:2)

我认为你应该在每次从字符串到字节数组并返回时指定编码。特别是,这一行:

decryptedText = new String(plainText);

应为:

decryptedText = new String(plainText, FORMAT);

否则,您依赖于环境的编码,这种编码很可能与FORMAT不同,导致特殊字符被打印为“?”。

答案 1 :(得分:1)

可能有用的一些事情。

System.out.println((int) getSpecialCharacter(49810).charAt(0));

打印

146

这是你在这里实际创建的角色。

System.out.println("The Falcon" + (char) 146 + "s Hangar Pte Ltd");

打印

The Falcon’s Hangar Pte Ltd

我认为问题在于您使用带有

的ISO-8859-1字符集获取字节
byte[] plainText = unencryptedString.getBytes(FORMAT);

但是当你把它变回String时,你会使用系统默认值。

decryptedText = new String(plainText);

我怀疑这应该是

decryptedText = new String(plainText, FORMAT); // use the same Charset