【问题标题】:AES Encryption in Node.js and Decryption in Java AndroidNode.js 中的 AES 加密和 Java Android 中的解密
【发布时间】:2018-09-12 23:33:20
【问题描述】:

我使用ricmoo/aes-js加密节点服务器响应,

Cypher.js

"use strict";

var aesjs = require("aes-js");
var sha256 = require("js-sha256");

const getKeyArray = function() {
  let buffer = sha256.arrayBuffer("mykey");
  let keyArray = new Uint8Array(buffer);
  const keySize = 16;
  let arr = new Array();
  for (var i = 0; i < keySize; i++) {
    arr.push(keyArray[i]);
  }
  return arr;
};

module.exports = {
  getKey: function() {
    return getKeyArray();
  },

  encrypt: function(text) {
    var textBytes = aesjs.utils.utf8.toBytes(text);

    // The counter is optional, and if omitted will begin at 1
    var aesCtr = new aesjs.ModeOfOperation.ctr(
      getKeyArray(),
      new aesjs.Counter(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER))
    );

    var counterArray = aesCtr._counter._counter.slice()

    var encryptedBytes = aesCtr.encrypt(textBytes);

    // To print or store the binary data, you may convert it to hex
    var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);

    var ivHex = aesjs.utils.hex.fromBytes(counterArray);

    return ivHex + ":" + encryptedHex;
  },
  decrypt: function(encryptedHex) {
    let split = encryptedHex.split(":");

    // When ready to decrypt the hex string, convert it back to bytes
    var encryptedBytes = aesjs.utils.hex.toBytes(split[1]);

    let ivHex = split[0];

    var ivBytes = aesjs.utils.hex.toBytes(ivHex);

    var counter = new aesjs.Counter(ivBytes);

    // The counter mode of operation maintains internal state, so to
    // decrypt a new instance must be instantiated.
    var aesCtr = new aesjs.ModeOfOperation.ctr(getKeyArray(), ivBytes);

    var decryptedBytes = aesCtr.decrypt(encryptedBytes);

    // Convert our bytes back into text
    var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes);

    return decryptedText;
  }
};

以及Java中的解密

Cypher.java

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

class Cypher {
    private final static char[] hexArray = "0123456789ABCDEF".toCharArray();

    private static String KEY = "mykey";

    /**
     * Decrypt a given hex string,
     * @param hexString
     * @return
     */
    static String decrypt(String hexString) throws Exception{
        Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

        String ivHex = hexString.split(":")[0];
        hexString = hexString.split(":")[1];

        IvParameterSpec ivSpec = new IvParameterSpec(hexStringToByteArray(ivHex));

        cipher.init(Cipher.DECRYPT_MODE, getEncryptionKey(KEY), ivSpec);

        byte[] decrypted = cipher.doFinal(hexStringToByteArray(hexString));

        return new String(decrypted);
    }

    private static SecretKeySpec getEncryptionKey(String key) throws Exception {

        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        digest.update(key.getBytes("UTF-8"));
        byte[] keyBytes = new byte[16];
        System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
        SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
        return secretKeySpec;

    }

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

    static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
}

我的问题是:

1- 上面的代码是否可以接受或有任何重大问题?

2- 我该如何改进它?

【问题讨论】:

    标签: java node.js encryption cryptography aes


    【解决方案1】:

    编辑 2

    我已经更新了问题,现在有问题的代码在加密和解密过程中使用了随机 IV。

    编辑

    请不要使用下面的代码,因为它不会随机生成 IV,请参阅下面的 cmets。

    旧答案

    这根本不容易,但我终于让它工作了,这是我的完整工作代码(node.js 上的加密和 Android 上的解密):

    Cypher.js

    "use strict";
    
    var aesjs = require("aes-js");
    var sha256 = require("js-sha256");
    
    const getKeyArray = function() { // decryption on Android doesn't support 256 bit keys with AES/CTR, so I'm using only 128 bits
      let buffer = sha256.arrayBuffer("mystrongkey");
      let keyArray =  new Uint8Array(buffer);
      const keySize = 16;
      let arr = new Array;
      for (var i = 0; i < keySize; i++) {
        arr.push(keyArray[i]);
      }
      return arr;
    }
    
    module.exports = {
      getKey: function() {
        return getKeyArray();
      },
      getIV: function() {
        return new aesjs.Counter(5);
      },
      encrypt: function(text) {
        var textBytes = aesjs.utils.utf8.toBytes(text);
    
        // The counter is optional, and if omitted will begin at 1
        var aesCtr = new aesjs.ModeOfOperation.ctr(getKeyArray(), this.getIV());
        var encryptedBytes = aesCtr.encrypt(textBytes);
    
        // To print or store the binary data, you may convert it to hex
        var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
        return encryptedHex;
      },
      decrypt: function(encryptedHex) {
        // When ready to decrypt the hex string, convert it back to bytes
        var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex);
    
        // The counter mode of operation maintains internal state, so to
        // decrypt a new instance must be instantiated.
        var aesCtr = new aesjs.ModeOfOperation.ctr(getKeyArray(), this.getIV());
        var decryptedBytes = aesCtr.decrypt(encryptedBytes);
    
        // Convert our bytes back into text
        var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes);
        return decryptedText;
      }
    };
    

    Cypher.java

    package com.mypackage;
    
    import javax.crypto.Cipher;
    import javax.crypto.NoSuchPaddingException;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    
    class Cypher {
        private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
    
        private static String KEY = "mystrongkey";
    
        /**
         * Decrypt a given hex string,
         * 08768efebc = Hello
         * @param hexString
         * @return
         */
        static String decrypt(String hexString) throws Exception{
            Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
    
            IvParameterSpec ivSpec = new IvParameterSpec(new byte[] { // got this one by console.log(Cypher.getIv()) from Cypher.js
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    5
            });
    
            cipher.init(Cipher.DECRYPT_MODE, getEncryptionKey(KEY), ivSpec);
    
            byte[] decrypted = cipher.doFinal(hexStringToByteArray(hexString));
    
            return new String(decrypted);
        }
    
        static String encrypt(String string) throws Exception {
            Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
    
            IvParameterSpec ivSpec = new IvParameterSpec(new byte[] {
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    0,
                    5
            });
    
            cipher.init(Cipher.ENCRYPT_MODE, getEncryptionKey(KEY), ivSpec);
    
            byte[] encrypted = cipher.doFinal(string.getBytes());
    
            return bytesToHex(encrypted);
        }
    
        private static SecretKeySpec getEncryptionKey(String key) throws Exception {
    
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            digest.update(key.getBytes("UTF-8"));
            byte[] keyBytes = new byte[16];
            System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
            SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
            return secretKeySpec;
    
        }
    
        static String bytesToHex(byte[] bytes) {
            char[] hexChars = new char[bytes.length * 2];
            for ( int j = 0; j < bytes.length; j++ ) {
                int v = bytes[j] & 0xFF;
                hexChars[j * 2] = hexArray[v >>> 4];
                hexChars[j * 2 + 1] = hexArray[v & 0x0F];
            }
            return new String(hexChars);
        }
    
        static byte[] hexStringToByteArray(String s) {
            int len = s.length();
            byte[] data = new byte[len / 2];
            for (int i = 0; i < len; i += 2) {
                data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                        + Character.digit(s.charAt(i+1), 16));
            }
            return data;
        }
    }
    

    希望这将节省尝试从 node.js 解密的人的时间

    【讨论】:

    • 请注意,这种加密方法根本不使用身份验证。如果密文在传输过程中/静止时更改,则无法在您的代码中检测到。另请注意,您使用普通的 SHA-256 作为 KDF。这通常是一个糟糕的主意。您应该改用实际的 KDF,例如 PBKDF2。
    • 最重要的是,最令人担忧的是,您并不是随机生成 IV。如果您使用相同的密钥进行两次加密,则可以通过对它们进行异或运算,轻松地从两个密文中检索出两者的明文。您可能想了解更多关于 CTR 模式的含义。您的代码实际上存在严重缺陷,很容易被破解。
    • 感谢您指出这一点,我对这个东西真的很陌生,我只是想加密我在 node.js 上的响应并在我的移动客户端上解密它。
    • @LukeJoshuaPark 我已经更新了问题,你能添加你的答案吗?
    • 等等,您是否使用它来加密您的应用程序和服务器之间传输的数据?这是否意味着加密密钥存在于任何具有您的应用程序的设备上?为什么不使用 HTTPS/TLS?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-16
    • 2011-07-14
    • 2013-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多