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



我使用以下代码进行我正在使用的采样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对其进行散列,然后使用生成的密码作为我的3DES的密钥。生成的密码正在被转换为十六进制表示,因为我在保存和检索密码中的奇怪字符时遇到了麻烦。

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

问题隐藏在多层之下。

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

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

StringBuilder.Append(char)的文档说:"总体效果就好像参数通过string . valueof (char)方法转换为字符串,然后将该字符串中的字符添加到该字符序列中。"

string。valueof (char)的文档是怎么说的?我很高兴你这么问。它说它"返回:一个长度为1的字符串,其中包含一个参数c。"(你可以看到,返回类型是'String'。)

啊,但是字符串值是如何工作的呢?好吧,让我们查一下相关文件。它说:"字符串表示UTF-16格式的字符串,其中补充字符由代理字符对表示(有关更多信息,请参阅Character类中的Unicode字符表示部分)。索引值引用char代码单元,因此补充字符在String中使用两个位置。"

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

我想我明白了。尽量简化你的代码,这样很难读懂。避免使用字符串,尽量使用数组,当必须使用二进制数据时最好使用字节数组。

隔离问题!把你的程序拆分成更小的函数来完成更简单的任务。

你可以使用标准的Base64编码器来做到这一点。参见http://www.docjar.com/docs/api/sun/misc/BASE64Encoder.htmlBase64当然比十六进制编码短。

如果你想继续十六进制转换尝试使用这些函数我在谷歌上找到: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;
}

未测试,只是在记事本上胡乱写的

最新更新