【加密解密】高级加密标准AES加密(Javascript实现)

原创
2017/02/10 05:00
阅读数 6.9K

高级加密标准(Advanced Encryption Standard,AES),是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES。2006年,高级加密标准已然成为对称密钥加密中最流行的算法之一。Rijndael加密算法为比利时密码学家Joan Daemen和Vincent Rijmen所设计,结合两位作者的名字,以Rijndael之命名之,投稿高级加密标准的甄选流程。(Rijdael的发音近于 "Rhinedoll"。)

 

AES的区块长度固定为128比特,密钥长度则可以是128,192或256比特;而Rijndael使用的密钥和区块长度均可以是128,192或256比特。

AES加密有很多轮的重复和变换。大致步骤如下:

1、密钥扩展(KeyExpansion),

2、初始轮(Initial Round),

3、重复9轮(Rounds),每一轮又包括:SubBytes、ShiftRows、MixColumns、AddRoundKey,

4、最终轮(Final Round),最终轮没有MixColumns。

 

4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。

1 字节代替

  字节代替的主要功能是通过S盒完成一个字节到另外一个字节的映射。

2 行移位

  行移位的功能是实现一个4x4矩阵内部字节之间的置换。

3 列混淆

  列混淆:利用GF(2^8)域上算术特性的一个代替。

4 轮密码加

  这个操作相对简单,其依据的原理是“任何数和自身的异或结果为0”。加密过程中,每轮的输入与每轮密钥异或一次;因此,解密时再异或上该轮的密钥即可恢复输入。

5 密钥扩展

密钥扩展过程说明:

    1)  将初始密钥以列为主,转化为4个32 bits的字,分别记为w[0…3];

    2)  按照如下方式,依次求解w[j],其中j是整数并且属于[4,43];

    3)  若j%4=0,则w[j]=w[j-4]⊕g(w[j-1]),否则w[j]=w[j-4]⊕w[j-1];

  函数g的流程说明:

    4)  将w循环左移一个字节;

    5)  分别对每个字节按S盒进行映射;

    6)  与32 bits的常量(RC[j/4],0,0,0)进行异或,RC是一个一维数组,其值如下。(RC的值只需要有10个,而此处用了11个,实际上RC[0]在运算中没有用到,增加RC[0]是为了便于程序中用数组表示。由于j的最小取值是4,j/4的最小取值则是1,因此不会产生错误。)

      RC = {00, 01, 02, 04, 08, 10, 20, 40, 80, 1B, 36}

<script language="JavaScript">
// sample key to expand:
//      2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c
// sample data:
//      32 43 f6 a8 88 5a 30 8d 31 31 98 a2 e0 37 07 34
// output:
//      39 25 84 1d 02 dc 09 fb dc 11 85 97 19 6a 0b 32

// sample key/data:
// PLAINTEXT: 00112233445566778899aabbccddeeff
// KEY:       000102030405060708090a0b0c0d0e0f
// OUTPUT:    69c4e0d86a7b0430d8cdb78070b4c55a

// accumulate values to put into text area
var accumulated_output_info;

// add a labeled value to the text area
function accumulate_output( str ) {
   accumulated_output_info = accumulated_output_info + str + "\n";
}

// convert a 8-bit value to a string
function cvt_hex8( val )
{
   var vh = (val>>>4)&0x0f;
   return vh.toString(16) + (val&0x0f).toString(16);
}

// convert a 32-bit value to a 8-char hex string
function cvt_hex32( val )
{
   var str="";
   var i;
   var v;

   for( i=7; i>=0; i-- )
   {
      v = (val>>>(i*4))&0x0f;
      str += v.toString(16);
   }
   return str;
}

// convert a two-digit hex value to a number
function cvt_byte( str )
{
  // get the first hex digit
  var val1 = str.charCodeAt(0);

  // do some error checking
  if ( val1 >= 48 && val1 <= 57 )
      // have a valid digit 0-9
      val1 -= 48;
   else if ( val1 >= 65 && val1 <= 70 )
      // have a valid digit A-F
      val1 -= 55;
   else if ( val1 >= 97 && val1 <= 102 )
      // have a valid digit A-F
      val1 -= 87;
   else
   {
      // not 0-9 or A-F, complain
      window.alert( str.charAt(1)+" is not a valid hex digit" );
      return -1;
   }

  // get the second hex digit
  var val2 = str.charCodeAt(1);

  // do some error checking
  if ( val2 >= 48 && val2 <= 57 )
      // have a valid digit 0-9
      val2 -= 48;
   else if ( val2 >= 65 && val2 <= 70 )
      // have a valid digit A-F
      val2 -= 55;
   else if ( val2 >= 97 && val2 <= 102 )
      // have a valid digit A-F
      val2 -= 87;
   else
   {
      // not 0-9 or A-F, complain
      window.alert( str.charAt(2)+" is not a valid hex digit" );
      return -1;
   }

   // all is ok, return the value
   return val1*16 + val2;
}

// add a byte to the output
function accumulate_byte( label, val )
{
   accumulated_output_info += label + cvt_hex8(val) + "\n";
}

// add a word to the output
function accumulate_wordarray( label, ary )
{
   var i, j;
   accumulated_output_info += label + " ";

   // process the four elements in this word
   for( j=0; j<4; j++ )
      accumulated_output_info += " " + cvt_hex8( ary[j] );

   // mark the end of the word
   accumulated_output_info += "\n";
}

// add an array to the output
function accumulate_array( label, ary )
{
   var i, j;
   var spacer="";

   // build a set of spaces of equal length to the label
   while( spacer.length < label.length )
      spacer += " ";

   // build the table
   for( i=0; i<16; i+= 4 )
   {
      // add label/spaces
      if ( i== 0 )
         accumulated_output_info += label + " ";
      else
         accumulated_output_info += spacer + " ";

      // process the four elements in this "row"
      for( j=0; j<4; j++ )
         accumulated_output_info += " " + cvt_hex8( ary[i+j] );

      // mark the end of this row
      accumulated_output_info += "\n";
   }
}

// S-Box substitution table
var S_enc = new Array(
 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);

// inverse S-Box for decryptions
var S_dec = new Array(
 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d);

// convert two-dimensional indicies to one-dim array indices
var I00 = 0;
var I01 = 1;
var I02 = 2;
var I03 = 3;
var I10 = 4;
var I11 = 5;
var I12 = 6;
var I13 = 7;
var I20 = 8;
var I21 = 9;
var I22 = 10;
var I23 = 11;
var I30 = 12;
var I31 = 13;
var I32 = 14;
var I33 = 15;
// conversion function for non-constant subscripts
// assume subscript range 0..3
function I(x,y) { return (x*4) + y; }

// remove spaces from input
function remove_spaces( instr ) {
   var i;
   var outstr="";

   for( i=0; i<instr.length; i++ )
      if ( instr.charAt(i) != " " )
         // not a space, include it
         outstr += instr.charAt(i);

   return outstr;
}

// get the message to encrypt/decrypt or the key
// return as a 16-byte array
function get_value( lbl, str, isASCII )
{
   var dbyte = new Array(16);
   var i;
   var val;    // one hex digit

   if ( isASCII )
   {
      // check length of data
      if ( str.length > 16 )
      {
         window.alert( lbl + " is too long, using the first 16 ASCII characters" );
      }

      // have ASCII data
      // 16 characters?
      if ( str.length >= 16 )
      {
         // 16 or more characters
         for( i=0; i<16; i++ )
         {
            dbyte[i] = str.charCodeAt(i);
         }
      }
      else
      {
         // less than 16 characters - fill with NULLs
         for( i=0; i<str.length; i++ )
         {
            dbyte[i] = str.charCodeAt(i);
         }
         for( i=str.length; i<16; i++ )
         {
            dbyte[i] = 0;
         }
      }
         
   }
   else
   {
      // have hex data - remove any spaces they used, then convert
      str = remove_spaces(str);

      // check length of data
      if ( str.length != 32 )
      {
         window.alert( lbl + " length wrong: Is " + str.length +
        " hex digits, but must be 128 bits (32 hex digits)");
         dbyte[0] = -1;
         return dbyte;
      }

      for( i=0; i<16; i++ )
      {
         // isolate and convert this substring
         dbyte[i] = cvt_byte( str.substr(i*2,2) );
         if( dbyte[i] < 0 )
         {
            // have an error
            dbyte[0] = -1;
            return dbyte;
         }
      } // for i
   } // if isASCII

   // return successful conversion
   return dbyte;
} // get_value

//do the AES GF(2**8) multiplication
// do this by the shift-and-"add" approach
function aes_mul( a, b )
{
   var res = 0;

   while( a > 0 )
   {
      if ( (a&1) != 0 )
         res = res ^ b;        // "add" to the result
      a >>>= 1;            // shift a to get next higher-order bit
      b <<= 1;            // shift multiplier also
   }

   // now reduce it modulo x**8 + x**4 + x**3 + x + 1
   var hbit = 0x10000;        // bit to test if we need to take action
   var modulus = 0x11b00;    // modulus - XOR by this to change value
   while( hbit >= 0x100 )
   {
      if ( (res & hbit) != 0 )        // if the high-order bit is set
         res ^= modulus;    // XOR with the modulus

      // prepare for the next loop
      hbit >>= 1;
      modulus >>= 1;
   }

   return res;
}

// apply the S-box substitution to the key expansion
function SubWord( word_ary )
{
   var i;

   for( i=0; i<16; i++ )
      word_ary[i] = S_enc[ word_ary[i] ];

   return word_ary;
}

// rotate the bytes in a word
function RotWord( word_ary )
{
   return new Array( word_ary[1], word_ary[2], word_ary[3], word_ary[0] );
}

// calculate the first item Rcon[i] = { x^(i-1), 0, 0, 0 }
// note we only return the first item
function Rcon( exp )
{
   var val = 2;
   var result = 1;

   // remember to calculate x^(exp-1)
   exp--;

   // process the exponent using normal shift and multiply
   while ( exp > 0 )
   {
      if ( (exp & 1) != 0 )
         result = aes_mul( result, val );

      // square the value
      val = aes_mul( val, val );

      // move to the next bit
      exp >>= 1;
   }

   return result;
}

// round key generation
// return a byte array with the expanded key information
function key_expand( key )
{
   var temp = new Array(4);
   var i, j;
   var w = new Array( 4*11 );

   // copy initial key stuff
   for( i=0; i<16; i++ )
   {
      w[i] = key[i];
   }
   accumulate_wordarray( "w[0] = ", w.slice(0,4) );
   accumulate_wordarray( "w[1] = ", w.slice(4,8) );
   accumulate_wordarray( "w[2] = ", w.slice(8,12) );
   accumulate_wordarray( "w[3] = ", w.slice(12,16) );

   // generate rest of key schedule using 32-bit words
   i = 4;
   while ( i < 44 )        // blocksize * ( rounds + 1 )
   {
      // copy word W[i-1] to temp
      for( j=0; j<4; j++ )
         temp[j] = w[(i-1)*4+j];

      if ( i % 4 == 0)
      {
         // temp = SubWord(RotWord(temp)) ^ Rcon[i/4];
         temp = RotWord( temp );
         accumulate_wordarray( "RotWord()=", temp );
         temp = SubWord( temp );
         accumulate_wordarray( "SubWord()=", temp );
         temp[0] ^= Rcon( i>>>2 );
         accumulate_wordarray( " ^ Rcon()=", temp );
      }

      // word = word ^ temp
      for( j=0; j<4; j++ )
         w[i*4+j] = w[(i-4)*4+j] ^ temp[j];
      accumulate_wordarray( "w["+i+"] = ", w.slice( i*4, i*4+4 ) );

      i++;
   }

   return w;
}

// do S-Box substitution
function SubBytes(state, Sbox)
{
   var i;

   for( i=0; i<16; i++ )
      state[i] = Sbox[ state[i] ];

   return state;
}

// shift each row as appropriate
function ShiftRows(state)
{
   var t0, t1, t2, t3;

   // top row (row 0) isn't shifted

   // next row (row 1) rotated left 1 place
   t0 = state[I10];
   t1 = state[I11];
   t2 = state[I12];
   t3 = state[I13];
   state[I10] = t1;
   state[I11] = t2;
   state[I12] = t3;
   state[I13] = t0;

   // next row (row 2) rotated left 2 places
   t0 = state[I20];
   t1 = state[I21];
   t2 = state[I22];
   t3 = state[I23];
   state[I20] = t2;
   state[I21] = t3;
   state[I22] = t0;
   state[I23] = t1;

   // bottom row (row 3) rotated left 3 places
   t0 = state[I30];
   t1 = state[I31];
   t2 = state[I32];
   t3 = state[I33];
   state[I30] = t3;
   state[I31] = t0;
   state[I32] = t1;
   state[I33] = t2;

   return state;
}

// inverset shift each row as appropriate
function InvShiftRows(state)
{
   var t0, t1, t2, t3;

   // top row (row 0) isn't shifted

   // next row (row 1) rotated left 1 place
   t0 = state[I10];
   t1 = state[I11];
   t2 = state[I12];
   t3 = state[I13];
   state[I10] = t3;
   state[I11] = t0;
   state[I12] = t1;
   state[I13] = t2;

   // next row (row 2) rotated left 2 places
   t0 = state[I20];
   t1 = state[I21];
   t2 = state[I22];
   t3 = state[I23];
   state[I20] = t2;
   state[I21] = t3;
   state[I22] = t0;
   state[I23] = t1;

   // bottom row (row 3) rotated left 3 places
   t0 = state[I30];
   t1 = state[I31];
   t2 = state[I32];
   t3 = state[I33];
   state[I30] = t1;
   state[I31] = t2;
   state[I32] = t3;
   state[I33] = t0;

   return state;
}

// process column info
function MixColumns(state)
{
   var col;
   var c0, c1, c2, c3;

   for( col=0; col<4; col++ )
   {
      c0 = state[I(0,col)];
      c1 = state[I(1,col)];
      c2 = state[I(2,col)];
      c3 = state[I(3,col)];

      // do mixing, and put back into array
      state[I(0,col)] = aes_mul(2,c0) ^ aes_mul(3,c1) ^ c2 ^ c3;
      state[I(1,col)] = c0 ^ aes_mul(2,c1) ^ aes_mul(3,c2) ^ c3;
      state[I(2,col)] = c0 ^ c1 ^ aes_mul(2,c2) ^ aes_mul(3,c3);
      state[I(3,col)] = aes_mul(3,c0) ^ c1 ^ c2 ^ aes_mul(2,c3);
   }

   return state;
}

// inverse process column info
function InvMixColumns(state)
{
   var col;
   var c0, c1, c2, c3;

   for( col=0; col<4; col++ )
   {
      c0 = state[I(0,col)];
      c1 = state[I(1,col)];
      c2 = state[I(2,col)];
      c3 = state[I(3,col)];

      // do inverse mixing, and put back into array
      state[I(0,col)] = aes_mul(0x0e,c0) ^ aes_mul(0x0b,c1)
            ^ aes_mul(0x0d,c2) ^ aes_mul(0x09,c3);
      state[I(1,col)] = aes_mul(0x09,c0) ^ aes_mul(0x0e,c1)
            ^ aes_mul(0x0b,c2) ^ aes_mul(0x0d,c3);
      state[I(2,col)] = aes_mul(0x0d,c0) ^ aes_mul(0x09,c1)
            ^ aes_mul(0x0e,c2) ^ aes_mul(0x0b,c3);
      state[I(3,col)] = aes_mul(0x0b,c0) ^ aes_mul(0x0d,c1)
            ^ aes_mul(0x09,c2) ^ aes_mul(0x0e,c3);
   }

   return state;
}

// insert subkey information
function AddRoundKey( state, w, base )
{
   var col;

   for( col=0; col<4; col++ )
   {
      state[I(0,col)] ^= w[base+col*4];
      state[I(1,col)] ^= w[base+col*4+1];
      state[I(2,col)] ^= w[base+col*4+2];
      state[I(3,col)] ^= w[base+col*4+3];
   }

   return state;
}

// return a transposed array
function transpose( msg )
{
   var row, col;
   var state = new Array( 16 );

   for( row=0; row<4; row++ )
      for( col=0; col<4; col++ )
         state[I(row,col)] = msg[I(col,row)];

   return state;
}

// final AES state
var AES_output = new Array(16);

// format AES output
// -- uses the global array DES_output
function format_AES_output()
{
   var i;
   var bits;
   var str="";

   // what type of data do we have to work with?
   if ( document.stuff.outtype[0].checked )
   {
      // convert each set of bits back to ASCII
      for( i=0; i<16; i++ )
         str += String.fromCharCode( AES_output[i] );
   }
   else 
   {
      // output hexdecimal data (insert spaces)
      str = cvt_hex8( AES_output[0] );
      for( i=1; i<16; i++ )
      {
         str += " " + cvt_hex8( AES_output[i] );
      }
   }

   // copy to textbox
   document.stuff.outdata.value = str;
}

// do encrytion
function aes_encrypt() {
   var w     = new Array( 44 );            // subkey information
   var state = new Array( 16 );            // working state
   var round;

   accumulated_output_info = "";

   // get the message from the user
   // also check if it is ASCII or hex
   var msg = get_value( "Message", document.stuff.indata.value, document.stuff.intype[0].checked );

   // problems??
   if ( msg[0] < 0 ) {
      document.stuff.details.value = accumulated_output_info;
      return;
   }
   accumulate_array( "Input bits", msg );

   // get the key from the user
   var key = get_value( "Key", document.stuff.key.value, false );
   // problems??
   if ( key[0] < 0 ) {
      document.stuff.details.value = accumulated_output_info;
      return;
   }
   accumulate_array( "Key bits", key );

    // expand the key
    accumulate_output("密钥扩展");
    w = key_expand( key );

    // initial state = message in columns (transposed from what we input)
    state = transpose( msg );

    accumulate_array( "Initial state", state );
    // display the round key - Transpose due to the way it is stored/used
    accumulate_output("轮密码加");
    accumulate_array( "Round Key", transpose(w.slice( 0, 16 )) ); 
    state = AddRoundKey(state, w, 0);

    for( round = 1; round < 10; round ++ ) {
        accumulate_array( "Round " + round, state );
        accumulate_output("字节代替");
        state = SubBytes(state, S_enc);
        accumulate_array( "After SubBytes", state );
        accumulate_output("行移位");
        state = ShiftRows(state);
        accumulate_array( "After ShiftRows", state );
        accumulate_output("列混淆");
        state = MixColumns(state);
        accumulate_array( "After MixColumns", state );
        // display the round key - Transpose due to the way it is stored/used
        accumulate_array( "Round Key", transpose(w.slice( round*4*4, round*16+16 )) ); 
        accumulate_output("轮密码加");
        // note here the spec uses 32-bit words, we are using bytes, so an extra *4
        state = AddRoundKey(state, w, round*4*4);
   }

    accumulate_output("字节代替");
    SubBytes(state, S_enc);
    accumulate_array( "After SubBytes", state );
    accumulate_output("行移位");
    ShiftRows(state);
    accumulate_array( "After ShiftRows", state );
    accumulate_output("轮密码加");
    AddRoundKey(state, w, 10*4*4);
    accumulate_array( "Output", state );

    // process output
    AES_output = transpose( state );
    format_AES_output( );
    document.stuff.details.value = accumulated_output_info;
}

// do decryption
function aes_decrypt() {
   var w = new Array( 44 );            // subkey information
   var state = new Array( 16 );            // working state
   var round;

   accumulated_output_info = "";

   // get the message from the user
   // also check if it is ASCII or hex
   var msg = get_value( "Message", document.stuff.indata.value,
        document.stuff.intype[0].checked );

   // problems??
   if ( msg[0] < 0 )
   {
      document.stuff.details.value = accumulated_output_info;
      return;
   }
   accumulate_array( "Input bits", msg );

   // get the key from the user
   var key = get_value( "Key", document.stuff.key.value, false );
   // problems??
   if ( key[0] < 0 )
   {
      document.stuff.details.value = accumulated_output_info;
      return;
   }
   accumulate_array( "Key bits", key );

   // expand the key
   w = key_expand( key );

   // initial state = message
   state = transpose( msg );

   accumulate_array( "Initial state", state );
   // display the round key - Transpose due to the way it is stored/used
   accumulate_array( "Round Key", transpose(w.slice( 10*4*4, 10*4*4+16 )) ); 
   state = AddRoundKey(state, w, 10*4*4);

   for( round=9; round>=1; round-- )
   {
      accumulate_array( "Round " + round, state );
      state = InvShiftRows(state);
      accumulate_array( "After InvShiftRows", state );
      state = SubBytes(state, S_dec);
      accumulate_array( "After SubBytes", state );
      // display the round key - Transpose due to the way it is stored/used
      accumulate_array( "Round Key", transpose(w.slice( round*4*4, round*16+16 )) ); 
      // note here the spec uses 32-bit words, we are using bytes, so an extra *4
      state = AddRoundKey(state, w, round*4*4);
      accumulate_array( "After AddRoundKey", state );
      state = InvMixColumns(state);
   }

   InvShiftRows(state);
   accumulate_array( "After InvShiftRows", state );
   SubBytes(state, S_dec);
   accumulate_array( "After SubBytes", state );
   AddRoundKey(state, w, 0);
   accumulate_array( "Output", state );

   // process output
   AES_output = transpose( state );
   format_AES_output( );
   document.stuff.details.value = accumulated_output_info;
}
</SCRIPT>
</HEAD>
<BODY>
2001年Rijndael算法成为高级加密标准(Advanced Encryption Standard,AES)的获胜者。
这个标准用来替代原先的DES,AES的区块长度固定为128比特,密钥长度则可以是128,192或256比特;而Rijndael使用的密钥和区块长度均可以是128,192或256比特。
<P>
<form name="stuff">
<br>
输入一个16个ASCII字符以内的字符串。
<p>
<table>
<tr>
    <td>输入信息:</td>
        <td><input type="text" name="indata" size="50" value="abcdefghijklmnop" /></td>
        <td><input type="radio" name="intype" checked>ASCII
            <input type="radio" name="intype">十六进制
        </td>
</tr>
<tr>
    <td>密钥</td>
        <td><select name="key">
         <OPTION value="0f1571c947d9e8590cb7add6af7f6798">0f 15 71 c9 47 d9 e8 59 0c b7 ad d6 af 7f 67 98</OPTION>
         <OPTION value="f6cc34cdc555c5418254260203ad3ecd">f6 cc 34 cd c5 55 c5 41 82 54 26 02 03 ad 3e cd</OPTION>
         <OPTION value="3070971ab7ce45063fd2573f49f5420d">30 70 97 1a b7 ce 45 06 3f d2 57 3f 49 f5 42 0d</OPTION>
         <OPTION value="a9aa1f9fd153bcb6c7834212c51f5c41">a9 aa 1f 9f d1 53 bc b6 c7 83 42 12 c5 1f 5c 41</OPTION>
         <OPTION value="d2f60c436e7ccebb8eaceaf3f86c8bad">d2 f6 0c 43 6e 7c ce bb 8e ac ea f3 f8 6c 8b ad</OPTION>
         <OPTION value="2b7e151628aed2a6abf7158809cf4f3c">2b 7e 15 16 28 ae d2 a6 ab f7 15 88 09 cf 4f 3c</OPTION>
       </SELECT></TD>
    <TD></TD>
</TR>
<TR>
    <TD colspan=3 align=center>
        <INPUT type="button" value="加密" onClick="aes_encrypt();" />
        <INPUT type="button" value="解密" onClick="aes_decrypt();" />
        </TD>
</TR>
<TR>
    <TD>输出信息:</TD>
    <TD><INPUT type="text" name="outdata" size="50" /></TD>
    <TD><INPUT type="radio" name="outtype" onClick="format_AES_output();">ASCII
        <INPUT type="radio" name="outtype" checked onClick="format_AES_output();">十六进制
    </TD>
</TR>
</TABLE>
<hr>
细节:<BR>
<TEXTAREA name="details" id="details" rows="25" cols="90"></TEXTAREA>
</FORM>

Java验证

    private byte[] keys = {
            0x0f,0x15,0x71,(byte)0xc9, 0x47,(byte)0xd9,(byte)0xe8,0x59,
            0x0c,(byte)0xb7,(byte)0xad,(byte)0xd6,(byte)0xaf,0x7f,0x67,(byte)0x98
        };
  //KeyGenerator 提供对称密钥生成器的功能,支持各种算法
  private KeyGenerator keygen;
  //SecretKey 负责保存对称密钥
  private SecretKey deskey;
  //Cipher负责完成加密或解密工作
  private Cipher c;
  //该字节数组负责保存加密的结果
  private byte[] cipherByte;

  public AesDemo() throws NoSuchAlgorithmException, NoSuchPaddingException{
    Security.addProvider(new com.sun.crypto.provider.SunJCE());
//    //实例化支持DES算法的密钥生成器(算法名称命名需按规定,否则抛出异常)
//    keygen = KeyGenerator.getInstance("AES");
//    //生成密钥
//    deskey = keygen.generateKey();
    deskey= new SecretKeySpec(keys, "AES");
    System.out.println("密钥:" + UBytes.toHexString(deskey.getEncoded()));
    //生成Cipher对象,指定其支持的DES算法
    c = Cipher.getInstance("AES/ECB/NOPADDING"); // PKCS5Padding
  }

  /**
   * 对字符串加密
   *
   * @param str
   * @return
   * @throws InvalidKeyException
   * @throws IllegalBlockSizeException
   * @throws BadPaddingException
   */
  public byte[] Encrytor(String str) throws InvalidKeyException,
      IllegalBlockSizeException, BadPaddingException {
    // 根据密钥,对Cipher对象进行初始化,ENCRYPT_MODE表示加密模式
    c.init(Cipher.ENCRYPT_MODE, deskey);
    byte[] src = str.getBytes();
    System.out.println("明文:" + UBytes.toHexString(src));
    // 加密,结果保存进cipherByte
    cipherByte = c.doFinal(src);
    System.out.println("密文:" + UBytes.toHexString(cipherByte));
    return cipherByte;
  }

  /**
   * 对字符串解密
   *
   * @param buff
   * @return
   * @throws InvalidKeyException
   * @throws IllegalBlockSizeException
   * @throws BadPaddingException
   */
  public byte[] Decryptor(byte[] buff) throws InvalidKeyException,
      IllegalBlockSizeException, BadPaddingException {
    // 根据密钥,对Cipher对象进行初始化,DECRYPT_MODE表示加密模式
    c.init(Cipher.DECRYPT_MODE, deskey);
    cipherByte = c.doFinal(buff);
    return cipherByte;
  }

  /**
   * @param args
   * @throws NoSuchPaddingException
   * @throws NoSuchAlgorithmException
   * @throws BadPaddingException
   * @throws IllegalBlockSizeException
   * @throws InvalidKeyException
   */
  public static void main(String[] args) throws Exception {
    AesDemo de1 = new AesDemo();
    String msg ="abcdefghijklmnop";
    byte[] encontent = de1.Encrytor(msg);
    byte[] decontent = de1.Decryptor(encontent);
    System.out.println("解密:" + UBytes.toHexString(decontent));
    System.out.println("明文是:" + msg);
    System.out.println("加密后:" + new String(encontent));
    System.out.println("解密后:" + new String(decontent));
  }

结果:

密钥:0F 15 71 C9 47 D9 E8 59 0C B7 AD D6 AF 7F 67 98 
明文:61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 
密文:11 0A AF F3 F2 D5 6C 9E 69 1A 95 A5 2E 19 28 EB 
解密:61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 
明文是:abcdefghijklmnop
解密后:abcdefghijklmnop

 

展开阅读全文
打赏
12
198 收藏
分享
加载中
qweqweqeqwe
2017/07/21 16:48
回复
举报

引用来自“冬天之雪”的评论

为什么要用js实现?拿到这种加密的东西不是应该放到服务器端吗?

引用来自“jumbycc”的评论

node.js怎么办?哈哈哈
nodejs不需要你自己实现, 直接用crypto就行了
2017/03/20 14:14
回复
举报
😂 一脸蒙逼, 不知道什么时候会看的懂这些
2017/03/17 22:33
回复
举报
这个大赞!
2017/02/10 18:31
回复
举报

引用来自“阿影”的评论

嗯,这个JS不能设置模式和填充算法?
单纯的AES早已不建议再使用了,尤其是ECB和无填充算法。
要在浏览器使用较为完整的实现,可以看 browserify-aes 或者 crypto-js
2017/02/10 13:53
回复
举报

引用来自“冬天之雪”的评论

为什么要用js实现?拿到这种加密的东西不是应该放到服务器端吗?

引用来自“jumbycc”的评论

node.js怎么办?哈哈哈

引用来自“冬天之雪”的评论

天?难道是专门给nodejs搞得
nodejs 就不用单独写了,用 crypto 就行了
2017/02/10 13:39
回复
举报

引用来自“冬天之雪”的评论

为什么要用js实现?拿到这种加密的东西不是应该放到服务器端吗?
用作客户端和服务器端的加密通信,防止明文在传输过程中被截获
2017/02/10 13:36
回复
举报
非常好,学习了!
2017/02/10 11:44
回复
举报
又在玩轮子
2017/02/10 10:34
回复
举报
嗯,这个JS不能设置模式和填充算法?
单纯的AES早已不建议再使用了,尤其是ECB和无填充算法。
2017/02/10 10:11
回复
举报
更多评论
打赏
17 评论
198 收藏
12
分享
返回顶部
顶部