【问题标题】:Android AES in counter mode with 256 bits keyAndroid AES 在计数器模式下使用 256 位密钥
【发布时间】:2016-01-29 08:45:20
【问题描述】:

我们的团队基于 Javascript 代码加密数据 sn-p 关注此页面 http://www.movable-type.co.uk/scripts/aes.html 和此处 http://anh.cs.luc.edu/331/code/aes.py

现在我必须在我的移动设备上使用 java 解密其中的数据。

页面说:

此脚本中的密钥是通过应用 Cipher 例程加密密码的前 16/24/32 个字符(对于 128-/192-/256 位密钥)来生成密钥而获得的。这是在完全独立的脚本中获取安全密钥的便捷方式(在生产环境中,与本教程代码相反,密钥可能生成为哈希,例如简单地 key = Sha256(password))。更详细地说,提供的密码被转换为 UTF-8(为了字节安全),然后前 16/24/32 个字符被转换为字节。生成的 pwBytes 用作 Aes.keyExpansion() 的种子,然后用作使用 Aes.cipher() 加密 pwBytes 的密钥。以这种方式从(不切实际的)简单的 `

生成的密钥示例

所以我想我在使用密码生成密钥时遇到了困难

这是我的测试用例:

  • 原文:Today
  • 密码:aa2145f9e2a5daaa9c6a8ddc5f5c1a39
  • 实际结果j��

他们没有使用两次密钥,每次都使用随机初始化向量(IV),因此结果不同。

Java 代码:

private static String decrypt(SecretKey aesKey, String encodedCiphertext) {
    try {
        // that's no base 64, that's base 64 over the UTF-8 encoding of the code points
        byte[] ciphertext = jsBase64Decode(encodedCiphertext);
        Cipher aesCTR = Cipher.getInstance("AES/CTR/NOPADDING");
        int n = aesCTR.getBlockSize();
        byte[] counter = new byte[n];
        int nonceSize = n / 2;
        System.arraycopy(ciphertext, 0, counter, 0, nonceSize);
        IvParameterSpec iv = new IvParameterSpec(counter);
        aesCTR.init(Cipher.DECRYPT_MODE, aesKey, iv);
        byte[] plaintext = aesCTR.doFinal(ciphertext, nonceSize, ciphertext.length - nonceSize);
        return new String(plaintext, "UTF-8");
        // that's no base 64, that's base 64 over the UTF-8 encoding of the code points

    } catch (Exception e) {
        e.printStackTrace();
    }
    return "";
}

private static byte[] jsBase64Decode(String encodedCiphertext) {
    byte[] ciphertext = null;
    try {
        byte[] utf8CT = Base64.decode(encodedCiphertext);

        String cts = new String(utf8CT, "UTF-8");
        ciphertext = new byte[cts.length()];
        for (int i = 0; i < cts.length(); i++) {
            ciphertext[i] = (byte) (cts.charAt(i) & 0xFF);
        }

    }catch (Exception e) {
        e.printStackTrace();
    }
    //Arrays.copyOfRange(new byte[100], 0, 99);
    return ciphertext;
}

// that should not be a singleton lazybones, it may contain state
private static SecretKey deriveKey(String password, int nBits) throws CharacterCodingException {
    try {
        Charset charset = Charset.forName("UTF-8");
        CharsetEncoder encoder = charset.newEncoder();

        ByteBuffer buf = encoder.encode(CharBuffer.wrap(password));
        //byte[] buf1 = password.getBytes();
        int nBytes = nBits / Byte.SIZE; // bits / Byte.SIZE;
        Cipher aesECB = Cipher.getInstance("AES/ECB/NoPadding");
        int n = aesECB.getBlockSize();
        byte[] pwBytes = new byte[nBytes];
        // so we only use those characters that fit in nBytes! oops!
        buf.get(pwBytes, 0, buf.remaining());
        //pwBytes = password.getBytes("UTF-8");
        SecretKey derivationKey = new SecretKeySpec(pwBytes, "AES");
        aesECB.init(Cipher.ENCRYPT_MODE, derivationKey);
        // and although the derivationKey is nBytes in size, we only encrypt 16 (the block size)
        byte[] partialKey = aesECB.doFinal(pwBytes, 0, n);
        byte[] key = new byte[nBytes];
        System.arraycopy(partialKey, 0, key, 0, n);
        // but now we have too few so we *copy* key bytes
        // so only the increased number of rounds is configured using nBits
        System.arraycopy(partialKey, 0, key, n, nBytes - n);
        SecretKey derivatedKey = new SecretKeySpec(key, "AES");
        return derivatedKey;
    } catch (Exception e) {
        throw new IllegalStateException("Key derivation should always finish", e);
    }
}


public static String main(){

    SecretKey key = null;
    try {
        key = deriveKey("aa2145f9e2a5daaa9c6a8ddc5f5c1a39", 256);


    } catch (Exception e) {
        e.printStackTrace();
    }
    // ciphertext may vary in length depending on UTF-8 encoding
    String pt = decrypt(key, "eQDH+srPqlbh7Ml42g==");
    return pt;
}

【问题讨论】:

    标签: android encryption aes


    【解决方案1】:

    我自己移植的。无论如何,谢谢。

    在这里为任何想像我一样做的人发帖

        /**
     * Created by luu on 1/28/2016.
     */
    public class AES {
    
    
    // sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [§5.1.1]
    private static final int[] sBox =  new int[]{
                0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
                0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
                0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
                0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
                0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
                0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
                0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
                0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
                0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
                0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
                0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
                0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
                0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
                0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
                0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
                0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
            };
    
    // rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2]
    private static final int[][] rCon = new int[][]{
            new int[]{0x00, 0x00, 0x00, 0x00},//0
            new int[]{0x01, 0x00, 0x00, 0x00},//1
            new int[]{0x02, 0x00, 0x00, 0x00},//2
            new int[]{0x04, 0x00, 0x00, 0x00},//3
            new int[]{0x08, 0x00, 0x00, 0x00},//4
            new int[]{0x10, 0x00, 0x00, 0x00},//5
            new int[]{0x20, 0x00, 0x00, 0x00},//6
            new int[]{0x40, 0x00, 0x00, 0x00},//7
            new int[]{0x80, 0x00, 0x00, 0x00},//8
            new int[]{0x1b, 0x00, 0x00, 0x00},//9
            new int[]{0x36, 0x00, 0x00, 0x00}//10
    };
    
    /**
     * AES Cipher function: encrypt 'input' state with Rijndael algorithm [§5.1];
     *   applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage.
     *
     * @param   {number[]}   input - 16-byte (128-bit) input state array.
     * @param   {number[][]} w - Key schedule as 2D byte-array (Nr+1 x Nb bytes).
     * @returns {number[]}   Encrypted output state array.
     */
    private static int[] cipher (int[] input, int[][] w) {
        int Nb = 4;               // block size (in words): no of columns in state (fixed at 4 for AES)
        int Nr = w.length / Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
    
        int[][] state = new int[4][];//  [[],[],[],[]];  // initialise 4xNb byte-array 'state' with input [§3.4]
        for (int i = 0; i < 4 * Nb; i++)
        {
            if (state[i % 4] == null)
                state[i % 4] = new int[4];
            state[i % 4][(int)Math.floor((double) i / 4)] = input[i];
        }
    
        state = addRoundKey(state, w, 0, Nb);
    
        for (int round = 1; round < Nr; round++)
        {
            state = subBytes(state, Nb);
            state = shiftRows(state, Nb);
            state = mixColumns(state, Nb);
            state = addRoundKey(state, w, round, Nb);
        }
    
        state = subBytes(state, Nb);
        state = shiftRows(state, Nb);
        state = addRoundKey(state, w, Nr, Nb);
    
        int[] output = new int[4 * Nb];  // convert state to 1-d array before returning [§3.4]
        for (int i = 0; i < 4 * Nb; i++)
            output[i] = state[i % 4][(int)Math.floor((double) i / 4)];
    
        return output;
    };
    
    
    /**
     * Perform key expansion to generate a key schedule from a cipher key [§5.2].
     *
     * @param   {number[]}   key - Cipher key as 16/24/32-byte array.
     * @returns {number[][]} Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes).
     */
    private static int[][] keyExpansion (int[] key) {
        int Nb = 4;            // block size (in words): no of columns in state (fixed at 4 for AES)
        int Nk = key.length / 4; // key length (in words): 4/6/8 for 128/192/256-bit keys
        int Nr = Nk + 6;       // no of rounds: 10/12/14 for 128/192/256-bit keys
    
        int[][] w = new int[Nb * (Nr + 1)][Nb];
        int[] temp = new int[4];
    
        // initialise first Nk words of expanded key with cipher key
        for (int i = 0; i < Nk; i++) {
            int[] r = new int[] { key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3] };
            w[i] = r;
        }
    
        // expand the key into the remainder of the schedule
        for (int i = Nk; i < (Nb * (Nr + 1)); i++) {
            w[i] = new int[4];
            for (int t = 0; t < 4; t++) temp[t] = w[i - 1][t];
            // each Nk'th word has extra transformation
            if (i % Nk == 0) {
                temp = subWord(rotWord(temp));
                for (int t = 0; t < 4; t++) temp[t] ^= rCon[i / Nk][t];
            }
            // 256-bit key has subWord applied every 4th word
            else if (Nk > 6 && i % Nk == 4) {
                temp = subWord(temp);
            }
            // xor w[i] with w[i-1] and w[i-Nk]
            for (int t = 0; t < 4; t++)
                w[i][t] = (w[i - Nk][t] ^ temp[t]);
        }
    
        return w;
    };
    
    /**
     * Apply SBox to state S [§5.1.1]
     * @private
     */
    private static int[][] subBytes (int[][] s, int Nb) {
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < Nb; c++) s[r][c] = sBox[s[r][c]];
        }
        return s;
    }
    
    
    /**
     * Shift row r of state S left by r bytes [§5.1.2]
     * @private
     */
    private static int[][] shiftRows (int[][] s, int Nb) {
        int[] t = new int[4];
        for (int r = 1; r < 4; r++) {
            for (int c = 0; c < 4; c++) t[c] = s[r][(c + r) % Nb];  // shift into temp copy
            for (int c = 0; c < 4; c++) s[r][c] = t[c];         // and copy back
        }          // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
        return s;  // see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
    }
    
    
    /**
     * Combine bytes of each col of state S [§5.1.3]
     * @private
     */
    private static int[][] mixColumns (int[][] s, int Nb) {
        for (int c = 0; c < 4; c++) {
            int[] a = new int[4];  // 'a' is a copy of the current column from 's'
            int[] b = new int[4];  // 'b' is a•{02} in GF(2^8)
            for (int i = 0; i < 4; i++)
            {
                a[i] = s[i][c];
                b[i] = (s[i][c] & 0x80) > 0 ? (s[i][c] << 1 ^ 0x011b) : (s[i][c] << 1);
            }
            // a[n] ^ b[n] is a•{03} in GF(2^8)
            s[0][c] = (b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]); // {02}•a0 + {03}•a1 + a2 + a3
            s[1][c] = (a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]); // a0 • {02}•a1 + {03}•a2 + a3
            s[2][c] = (a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]); // a0 + a1 + {02}•a2 + {03}•a3
            s[3][c] = (a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]); // {03}•a0 + a1 + a2 + {02}•a3
        }
        return s;
    }
    
    
    /**
     * Xor Round Key into state S [§5.1.4]
     * @private
     */
    private static int[][] addRoundKey (int[][] state, int[][] w, int rnd, int Nb) {
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < Nb; c++) state[r][c] ^= w[rnd * 4 + c][r];
        }
        return state;
    }
    
    
    /**
     * Apply SBox to 4-byte word w
     * @private
     */
    private static int[] subWord (int[] w) {
        for (int i = 0; i < 4; i++) w[i] = sBox[w[i]];
        return w;
    }
    
    
    /**
     * Rotate 4-byte word w left by one byte
     * @private
     */
    private static int[] rotWord (int[] w) {
        int tmp = w[0];
        for (int i = 0; i < 3; i++) w[i] = w[i + 1];
        w[3] = tmp;
        return w;
    }
    
    /**
     * Decrypt a text encrypted by AES in counter mode of operation
     *
     * @param   {string} ciphertext - Source text to be encrypted.
     * @param   {string} password - Password to use to generate a key.
     * @param   {number} nBits - Number of bits to be used in the key; 128 / 192 / 256.
     * @returns {string} Decrypted text
     *
     * @example
     *   var decr = Aes.Ctr.encrypt('lwGl66VVwVObKIr6of8HVqJr', 'pāşšŵōřđ', 256); // decr: 'big secret'
     */
    public static String decrypt(String ciphertext, String password, int nBits) throws Exception{
        String plaintext = "";
        //try {
            int blockSize = 16;  // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
            if (!(nBits == 128 || nBits == 192 || nBits == 256))
                return ""; // standard allows 128/192/256 bit keys
    
            ciphertext = base64Decoder(ciphertext);
            password = UTF8Encode(password);
    
            // use AES to encrypt password (mirroring encrypt routine)
            int nBytes = nBits / 8;  // no bytes in key
            int[] pwBytes = new int[nBytes];
            for (int i = 0; i < nBytes; i++) {
                pwBytes[i] = Float.isNaN(password.charAt(i)) ? 0 : password.charAt(i);
            }
    
            int[] key = cipher(pwBytes, keyExpansion(pwBytes));
            // expand key to 16/24/32 bytes long
            int bytesExpand = nBytes - 16;
            if(bytesExpand > 0){
    
                int keyOriginalLength = key.length;
                int[] expandKey = new int[bytesExpand];
                int[] endKey = new int[keyOriginalLength + bytesExpand];
                System.arraycopy(key, 0, expandKey, 0, bytesExpand);// initial expandKey
                System.arraycopy(key, 0, endKey, 0, key.length);// copy all from key to endKey
                System.arraycopy(expandKey, 0, endKey, key.length, expandKey.length);
                key = endKey;
            }
    
            // recover nonce from 1st 8 bytes of ciphertext
            int[] counterBlock = new int[16];
            String ctrTxt = ciphertext.substring(0, 8);
            for (int i = 0; i < 8; i++) counterBlock[i] = ctrTxt.charAt(i);
    
            // generate key schedule
            int[][] keySchedule = keyExpansion(key);
    
            // separate ciphertext into blocks (skipping past initial 8 bytes)
            int nBlocks = (int) Math.ceil((ciphertext.length() - 8) / (float)blockSize);
            String[] cipherArr = new String[nBlocks];
            for (int b = 0; b < nBlocks; b++) {
                int start = 8 + b * blockSize;
                int end =   8 + b * blockSize + blockSize;
                if (end >= ciphertext.length())
                    cipherArr[b] = UTF8Encode(ciphertext.substring(start));
                else
                    cipherArr[b] = UTF8Encode(ciphertext.substring(start, end));
            }
            // ciphertext is now array of block-length strings,  ³F.àiþ±wãì¿,ß°d
            // plaintext will get generated block-by-block into "³F.àiþ±wãì¿,ß°" array of block-length strings
            String[] plaintxt = new String[cipherArr.length];
            // Expand CounterBlock
            for (int b = 0; b < nBlocks; b++) {
                // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
                for (int c = 0; c < 4; c++)
                    counterBlock[15 - c] = (b >> c * 8) & 0xff;
                for (int c = 0; c < 4; c++)
                    // counterBlock[15 - c - 4] = (b / 0x100000000 >>> c * 8);
                    counterBlock[15 - c - 4] = 0;
    
                int[] cipherCntr = cipher(counterBlock, keySchedule);  // encrypt counter block
                char[] plaintxtByte = new char[cipherArr[b].length()];
                for (int i = 0; i < cipherArr[b].length(); i++) {
    
                        plaintxtByte[i] = (char) (cipherCntr[i] ^ cipherArr[b].charAt(i));
    
    
                }
                plaintxt[b] = String.copyValueOf(plaintxtByte);
            }
    
            // join array of blocks into single plaintext string
            plaintext = joinArray(plaintxt);//   plaintxt.Join('');
    
            // join array of blocks into single plaintext string
            plaintext = UTF8Decode(plaintext);// decode from UTF8 back to Unicode multi-byte chars
    
        return plaintext;
    };
    
    private static String joinArray(Object[] source){
        String dest = "";
        for(int i = 0; i< source.length; i++){
            dest += (String)source[i];
        }
        return dest;
    }
    
    private static String UTF8Decode(String s) throws Exception {
    
        byte[] utf8Bytes = new byte[s.length()];
        for (int i = 0; i < s.length(); ++i)
        {
            //Debug.Assert( 0 <= utf8String[i] && utf8String[i] <= 255, "the char must be in byte's range");
            utf8Bytes[i] = (byte)s.charAt(i);
        }
    
        return new String(utf8Bytes, UTF8);
    }
    
    public static String base64Decoder(String data) throws Exception {
    
        byte[] b = Base64.decode(data.getBytes("ISO-8859-1"), Base64.NO_WRAP);
        return new String(b, "ISO-8859-1");
    }
    
    private static final String UTF8 = "UTF-8";
    
    private static String UTF8Encode(String s) throws UnsupportedEncodingException {
    
        return new String(s.getBytes(UTF8), UTF8);
    }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-16
      • 1970-01-01
      • 2020-07-26
      • 1970-01-01
      • 1970-01-01
      • 2012-09-27
      • 2018-04-15
      • 1970-01-01
      相关资源
      最近更新 更多