转换为十六进制然后回到正常字符串时出错

时间:2011-10-27 09:48:57

标签: java hex 3des

我正在使用以下代码进行我正在使用的采样3DES加密:

package Algorithms;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class MyDES {
    public static String encrypt(String pass,String plainText) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
        byte[] key = pass.getBytes("UTF-8"); //get byte arrays of the given password
        MessageDigest sha = MessageDigest.getInstance("SHA-1"); //get SHA-1 hashing instance
        key=sha.digest(key); //has the given password
        key=Arrays.copyOf(key,24);//take the first 16 bytes as the key for DES encryption

        SecretKeySpec sks = new SecretKeySpec(key, "DESede");//key spec for 3-DES
        Cipher c = Cipher.getInstance("DESede");//get an instance of 3DES
        c.init(Cipher.ENCRYPT_MODE,sks); //initialize 3DES to encrypt mode with given parameters
        byte[] cipherTextBytes = c.doFinal(plainText.getBytes()); //encrypt

        System.out.println("key used: "+new String(key)+" cipher generated "+new String(cipherTextBytes));
        StringBuffer cipherText= new StringBuffer();
        for(int i=0;i<cipherTextBytes.length;i++)
        {
            cipherText.append(Integer.toHexString(cipherTextBytes[i]));
        }

        System.out.println("Final Cipher returned: "+cipherText.toString());
        return cipherText.toString();
    }

    public static String decrypt(String pass,String cipherText) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
        System.out.println("Initially in decryption-> pass:"+pass+" cipher: "+cipherText);
        byte[] byteArray = new byte[cipherText.length() / 2];
        int j=0;
        for(int k=0;k<cipherText.length()-1;k+=2)
        {
            String o= cipherText.substring(k,k+2);
            int dec = Integer.parseInt(o,16);
            byteArray[j++] = (byte)dec;
        }

        String plainText="";
        byte[] key = pass.getBytes("UTF-8");
        MessageDigest sha = MessageDigest.getInstance("SHA-1");
        key=sha.digest(key);
        key=Arrays.copyOf(key,24);
        System.out.println("\nkey obtained: "+new String(key)+"\n Later cipher text:-> "+new String(byteArray));

        SecretKeySpec sks = new SecretKeySpec(key, "DESede");
        Cipher c = Cipher.getInstance("DESede");
        c.init(Cipher.DECRYPT_MODE,sks);
        plainText = new String(c.doFinal(byteArray));
        return plainText;
    }

}

这里我试图获取密码,使用SHA-1哈希,然后使用生成的has作为我的3DES的关键。生成的密码正在转换为十六进制表示,因为我在保存和检索密码中的奇怪字符时遇到问题。

在解密中,我将密码从十六进制再次转换为普通字符串然后解密。但我得到javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher例外。知道我哪里错了吗?

2 个答案:

答案 0 :(得分:3)

问题隐藏在多个层次下。

    StringBuilder sb = new StringBuilder();
      ...
        sb.append((char)dec);

因此,您尝试通过调用StringBuilder.Append(char)来组装原始二进制数据。嗯,那是做什么的?

StringBuilder.Append(char)的文档说“整体效果就像是通过方法String.valueOf(char)将参数转换为字符串,然后将该字符串中的字符附加到此字符序列。 “

好的,那么String.valueOf(char)的文档是什么意思呢?我很高兴你问。它说它“返回:一个长度为1的字符串,其中包含参数c的单个字符。” (你可以看到,返回类型是'String'。)

啊,但是String值是如何工作的?好吧,我们来看看documentation on that。它表示,“字符串表示UTF-16格式的字符串,其中补充字符由代理项对表示(有关详细信息,请参阅字符类中的Unicode字符表示形式一节)。索引值是指char代码单元,因此a补充字符在字符串中使用两个位置。“

这就是问题所在。您正在将原始二进制密文转换为UTF-16字符和代理对,这肯定不是您想要的。

答案 1 :(得分:2)

啊我觉得我明白了。 尝试简化您的代码,难以阅读。 避免使用字符串并尽可能多地使用数组,当你必须使用二进制数据时最好使用字节数组。

隔离问题!将程序拆分为较小的函数,以完成更简单的任务。

您可以使用标准Base64编码器来执行此操作。见http://www.docjar.com/docs/api/sun/misc/BASE64Encoder.html Base64当然比十六进制编码短。

如果您想继续使用HEX转换,请尝试使用我在google上找到的这些功能:http://www.developerfeed.com/javacore/blog/how-convert-hex-string-bytes-and-viceversa-java

我试着稍微简化代码。 您可以使用要编写的新编码函数来代替新的sun.misc.BASE64Encoder()。encode()或decode()。

private static byte[] getPassKey(String pass)
{
    byte[] passKey = pass.getBytes("UTF-8"); //get byte arrays of the given password
    byte[] shaKey = MessageDigest.getInstance("SHA-1").digest(passKey); 
    return Arrays.copyOf(shaKey,24);
}

public static String encrypt(String pass, String plainText) throws NoSuchAlgorithmException, UnsupportedEncodingException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{

    Cipher desCipher = Cipher.getInstance("DESede");
    desCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(getPassKey(pass), "DESede")); 

    byte[] cipherTextBytes = desCipher.doFinal(plainText.getBytes());
    String encoded = new sun.misc.BASE64Encoder().encode(cipherTextBytes);

    return encoded;
}

public static String decrypt(String pass,String cipherText) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{

    byte[] decoded = new sun.misc.BASE64Encoder().decode(cipherText);

    Cipher desCipher = Cipher.getInstance("DESede");
    desCipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(getPassKey(pass), "DESede"));
    plainText = new String(desCipher.doFinal(decoded));
    return plainText;
}

未经测试,只是在记事本中用刮笔写的。