1

我正在使用以下代码进行我正在使用的采样 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例外。知道我哪里错了吗?

4

2 回答 2

3

问题隐藏在几层之下。

    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 格式的字符串,其中补充字符由代理对表示(有关更多信息,请参阅字符类中的 Unicode 字符表示部分)。索引值指的是字符代码单元,因此补充字符在字符串中使用两个位置。”

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

于 2011-10-27T09:55:00.387 回答
2

Ah i think i got it. Try to simplify your code a bit, is hard to read. Avoid using strings and work as much as you can with arrays, when you have to work with binary data is better to use byte arrays.

Isolate the problems! Split your program in smaller functions that does simpler task.

You can use a standard Base64 encoder to do that. See http://www.docjar.com/docs/api/sun/misc/BASE64Encoder.html Base64 is of course shorter than hex encoding.

If you want to go on with the HEX conversion try to use these functions i found on google: http://www.developerfeed.com/javacore/blog/how-convert-hex-string-bytes-and-viceversa-java

I tried to simplify the code a little. Instead of new sun.misc.BASE64Encoder().encode() or decode() you can use the new encoding functions you are going to write.

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;
}

Not tested, just written by scratch in notepad.

于 2011-10-27T09:53:07.853 回答