我们的团队根据Javascript代码段加密数据,请点击此页http://www.movable-type.co.uk/scripts/aes.html和此处 http://anh.cs.luc.edu/331/code/aes.py
现在我必须使用移动设备上的java解密数据。
页面上写着:
此脚本中的密钥是通过应用密码例程来加密密码的前16/24/32个字符(用于128- / 192- / 256位密钥)来获得密钥。这是在完全自包含的脚本中获得安全密钥的便捷方式(在生产环境中,与本质上的教程代码相反,密钥可以生成为散列,例如简单地为key = Sha256(密码))。更详细地说,提供的密码将转换为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;
}
答案 0 :(得分:0)
我自己移植了。无论如何还是Tks。
在这里发帖给任何想要像我一样的人
/**
* 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);
}
}