13

我需要从客户端(JavaScript)加密字符串并从服务器端(Java)解密,所以我找到了 CryptoJS,我用 mi Java 代码的相同参数/配置编写代码,但输出总是不同,做你有什么想法或发生了什么?

我将CBC 与 NoPadding 一起使用

CryptoJS

http://jsfiddle.net/Soldier/gCHAG/

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js">
</script>
<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/pad-nopadding-min.js"></script>
<script>

    function padString(source) {
        var paddingChar = ' ';
        var size = 16;
        var x = source.length % size;
        var padLength = size - x;

        for (var i = 0; i < padLength; i++) source += paddingChar;

        return source;
    }

    var key = CryptoJS.enc.Hex.parse('0123456789abcdef');
    var iv  = CryptoJS.enc.Hex.parse('fedcba9876543210');
    var message = "soldier";
    var padMsg = padString(message);

    var encrypted = CryptoJS.AES.encrypt(padMsg, key, { iv: iv, padding: CryptoJS.pad.NoPadding, mode: CryptoJS.mode.CBC});

    console.log("Encrypted: "+encrypted);
    console.log("Encrypted text: "+encrypted.ciphertext);

</script>

Java 代码

import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;

public class AesCipher {

    private static final String algorithm = "AES/CBC/NoPadding";

    private static final byte[] keyValue = new byte[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    private static final byte[] ivValue = new byte[] { 'f', 'e', 'd', 'c', 'b', 'a', '9', '8', '7', '6', '5', '4', '3', '2', '1', '0' };

    private static final IvParameterSpec ivspec = new IvParameterSpec(ivValue);
    private static final SecretKeySpec keyspec = new SecretKeySpec(keyValue, "AES");

    final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();

    public static String encrypt(String Data) throws Exception {
        Cipher c = Cipher.getInstance(algorithm);
        c.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
        byte[] encVal = c.doFinal(Data.getBytes());
        String encryptedValue = new BASE64Encoder().encode(encVal);
        return encryptedValue;
    }

    public static String decrypt(String encryptedData) throws Exception {
        Cipher c = Cipher.getInstance(algorithm);
        c.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
        byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
        byte[] decValue = c.doFinal(decordedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }

    public static String bytesToHex(byte[] bytes) {
        char[] hexChars = new char[bytes.length * 2];
        int v;
        for ( int j = 0; j < bytes.length; j++ ) {
            v = bytes[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);
    }

    private static String padString(String source) {
        char paddingChar = ' ';
        int size = 16;
        int x = source.length() % size;
        int padLength = size - x;

        for (int i = 0; i < padLength; i++)
        {
            source += paddingChar;
        }
        return source;
      }

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

        String password = "soldier";
        String passwordEnc = AesCipher.encrypt(padString(password));
        String passwordDec = AesCipher.decrypt(passwordEnc);

        System.out.println("Plain Text : " + password);
        System.out.println("Encrypted Text : " + passwordEnc);
        System.out.println("Decrypted Text : " + passwordDec);
    }

}

原始字符串:

soldier

CryptoJS 的输出:

Encrypted: VNzZNKJTqfRbM7zO/M4cDQ==
Encrypted Hex: 54dcd934a253a9f45b33bccefcce1c0d

Java 代码的输出:

Encrypted: j6dSmg2lfjY2RpN91GNgNw==
Encrypted Hex: 6a3664536d67326c666a593252704e3931474e674e773d3d

加密的 base64 字符串具有相同的长度,但不是十六进制。如果我将 CryptoJS 的输出结果放在 Java Code 中,则解密不正确。

问候,

4

3 回答 3

8

这里的问题是您的键输入不一致。

  • CryptoJS.enc.Hex.parse('0123456789abcdef')将输入读取为一系列字节,表示为两位十六进制值:012345等。

  • 您的 Java 数组使用字符的字符编码值指定字节值。因此,字节序列(十六进制)是:(30十进制 48,ASCII 码'0'),然后31(十进制 49,ASCII 码'1'),等等。

CryptoJS.enc.Latin1.parse您可以通过使用它将读取单个字符值并将它们用作字节值来使 JavaScript 符合 Java 实现:http: //jsfiddle.net/gCHAG/1/(这会产生相同的j6dSm...输出)

但是,您可能希望每个数字都是它自己的字节。为此,您需要更改这两个实现。

爪哇:

// use hex literals, not characters
byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
// array values: 0x00, 0x01, 0x02, etc

JavaScript:

// remember each bytes is two digits wide
CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f')
// array values: 0x00, 0x01, 0x02, etc
于 2013-10-18T20:23:29.883 回答
1

字符“0”与十六进制值 0 不同。 CryptoJS 键很可能与 Java 键不同,因为您将它们实例化为不同的对象类型。创建并比较后以两种语言打印出密钥/ IV。

编辑:也就是说,这可能会转移到 StackOverflow,因为关于特定加密库的问题在这里不是主题。

于 2013-10-17T21:48:41.563 回答
0

非常有用的例子 SoldierCorp,谢谢!

有几件事可以改进您的示例:

  • 方法 padString 不支持 UTF8,而不是修复此方法,让我们删除它并使用标准填充

在 javascript 中替换

padding: CryptoJS.pad.Pkcs7

在java中替换

algorithm = "AES/CBC/PKCS5Padding"
  • 从任何字符串短语生成密钥(IV 可以相同)

在 javascript 中替换

var key = CryptoJS.MD5("Secret Passphrase");

在java中替换

byte[] keyValue = org.apache.commons.codec.digest.DigestUtils.md5("Secret Passphrase");
于 2015-01-09T09:35:26.263 回答