public static string encrypt(string plaintext, string password, int nBits) { var 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 } // note PHP (5) gives us plaintext and password in UTF8 encoding! // use AES itself to encrypt password to get cipher key (using plain password as source for // key expansion) - gives us well encrypted key var nBytes = nBits / 8; // no bytes in key var pwBytes = new byte[nBytes]; //byte[] password_bs = System.Text.Encoding.UTF8.GetBytes(password); for (int i = 0; i < nBytes; i++) { byte cc = 0; if (password.Length > i) { cc = (byte)password[i]; } pwBytes[i] = (byte)(cc & 0xff); } var bs = AES.keyExpansion(pwBytes); var key = AES.cipher(pwBytes, bs, false); //key = array_merge($key, array_slice($key, 0, $nBytes-16)); // expand key to 16/24/32 bytes long var _tmp = new byte[key.Length + nBytes - 16]; System.Array.Copy(key, _tmp, key.Length); System.Array.Copy(key, 0, _tmp, key.Length, nBytes - 16); key = _tmp; // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in // 1st 8 bytes, block counter in 2nd 8 bytes var counterBlock = new byte[16]; long nonce = UnixTool.timestamp(); // timestamp: milliseconds since 1-Jan-1970 var nonceSec = (long)Math.Floor((decimal)nonce / 1000); var nonceMs = nonce % 1000; //AESLogger.needLog(true); AESLogger.add(nonceMs.ToString()); // encode nonce with seconds in 1st 4 bytes, and (repeated) ms part filling 2nd 4 bytes for (int i = 0; i < 4; i++) { counterBlock[i] = ((byte)(AesCtr.urs(nonceSec, (byte)(i * 8)) & 0xff)); } for (int i = 0; i < 4; i++) { counterBlock[i + 4] = (byte)(nonceMs & 0xff); } // and convert it to a string to go on the front of the ciphertext //var ctrTxt = ""; //for (int i=0; i<8; i++) ctrTxt += (char)(counterBlock[i]); //var allsb = new StringBuilder(); MemoryStream ms = new MemoryStream(); //for (int i = 0; i < 8; i++) allsb.Append((char)(counterBlock[i])); for (int i = 0; i < 8; i++) { ms.WriteByte((counterBlock[i])); } AESLogger.traceSingle(counterBlock); // generate key schedule - an expansion of the key into distinct Key Rounds for each round var keySchedule = AES.keyExpansion(key); //print_r($keySchedule); var blockCount = (int)Math.Ceiling((decimal)plaintext.Length / blockSize); var ciphertxt = new string[blockCount]; // ciphertext as array of strings for (var b = 0; b < blockCount; b++) { // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) // done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB) for (var c = 0; c < 4; c++) { counterBlock[15 - c] = (byte)(AesCtr.urs(b, (byte)(c * 8)) & 0xff); } for (var c = 0; c < 4; c++) { counterBlock[15 - c - 4] = (byte)(AesCtr.urs((int)(b / 0x100000000), (byte)(c * 8))); } var cipherCntr = AES.cipher(counterBlock, keySchedule, false); // -- encrypt counter block -- // block size is reduced on final block var blockLength = b < blockCount - 1 ? blockSize : (plaintext.Length - 1) % blockSize + 1; var cipherByte = new byte[blockLength]; //var sb = new StringBuilder(); for (var i = 0; i < blockLength; i++) { // -- xor plaintext with ciphered counter byte-by-byte -- cipherByte[i] = (byte)(cipherCntr[i] ^ (byte)(plaintext[b * blockSize + i])); //转成字符串 //cipherByte[i] = (char)(cipherByte[i]); //sb.Append((char)(cipherByte[i])); ms.WriteByte(cipherByte[i]); } //$ciphertxt[$b] = implode('', $cipherByte); // escape troublesome characters in ciphertext //ciphertxt[b] = sb.ToString(); //allsb.Append(sb.ToString()); } // implode is more efficient than repeated string concatenation //var ciphertext = ctrTxt . implode('', $ciphertxt); //$ciphertext = base64_encode($ciphertext); //byte[] byteArray = System.Text.Encoding.Default.GetBytes(allsb.ToString()); var byteArray = ms.ToArray(); var ciphertext = Convert.ToBase64String(byteArray); return(ciphertext); }
/** * Decrypt a text encrypted by AES in counter mode of operation * * @param ciphertext source text to be decrypted * @param password the password to use to generate a key * @param nBits number of bits to be used in the key (128, 192, or 256) * @return decrypted text */ public static string decrypt(string ciphertext, string password, int nBits) { 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 } byte[] bs = Convert.FromBase64String(ciphertext); //$ciphertext = base64_decode($ciphertext); // use AES to encrypt password (mirroring encrypt routine) int nBytes = nBits / 8; // no bytes in key var pwBytes = new byte[nBytes]; //for (int i=0; i<nBytes; i++) pwBytes[i] = ord(substr($password,$i,1)) & 0xff; for (int i = 0; i < nBytes; i++) { byte cc = 0; if (password.Length > i) { cc = (byte)password[i]; } pwBytes[i] = (byte)(cc & 0xff); } var key = AES.cipher(pwBytes, AES.keyExpansion(pwBytes), false); //$key = array_merge($key, array_slice($key, 0, $nBytes-16)); // expand key to 16/24/32 bytes long var _tmp = new byte[key.Length + nBytes - 16]; System.Array.Copy(key, _tmp, key.Length); System.Array.Copy(key, 0, _tmp, key.Length, nBytes - 16); key = _tmp; // recover nonce from 1st element of ciphertext var counterBlock = new byte[16]; //$ctrTxt = substr($ciphertext, 0, 8); var ctrTxt = new byte[8]; Array.Copy(bs, ctrTxt, 8); for (int i = 0; i < 8; i++) { counterBlock[i] = ctrTxt[i]; // ord(substr($ctrTxt,$i,1)); } // generate key schedule var keySchedule = AES.keyExpansion(key); // separate ciphertext into blocks (skipping past initial 8 bytes) var nBlocks = (int )Math.Ceiling((decimal )(((double)bs.Length - 8) / blockSize)); var ct = new List <byte[]>(); //for (var b=0; b<nBlocks; b++) ct[b] = substr($ciphertext, 8+$b*$blockSize, 16); for (var b = 0; b < nBlocks; b++) { var startIndex = 8 + b * blockSize; var size = 16; //超标 if (startIndex + 16 > bs.Length) { size = bs.Length - startIndex; } var tmpbs = new byte[size]; //= substr($ciphertext, 8+$b*$blockSize, 16); Array.Copy(bs, startIndex, tmpbs, 0, size); ct.Add(tmpbs); } //$ciphertext = $ct; // ciphertext is now array of block-length strings // plaintext will get generated block-by-block into array of block-length strings //$plaintxt = array(); //StringBuilder sb = new StringBuilder(); byte[] all_bs = new byte[bs.Length - 8]; int counter = 0; for (int b = 0; b < nBlocks; b++) { // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) for (byte c = 0; c < 4; c++) { counterBlock[15 - c] = (byte)(AesCtr.urs(b, (byte)(c * 8)) & 0xff); } for (int c = 0; c < 4; c++) { counterBlock[15 - c - 4] = //AesCtr::urs(($b+1)/0x100000000-1, $c*8) & 0xff; (byte)(AesCtr.urs((int)((b + 1) / 0x100000000), (byte)(c * 8)) & 0xff); } // (byte)(AesCtr.urs((int)((b + 1) / 0x100000000 - 1), (byte)(c * 8)) & 0xff); var cipherCntr = AES.cipher(counterBlock, keySchedule, false); // encrypt counter block //AESLogger.needLog(true); AESLogger.traceSingle(counterBlock); AESLogger.add("-----------------" + b.ToString()); //plaintxtByte = array(); for (int i = 0; i < ct[b].Length; i++) { // -- xor plaintext with ciphered counter byte-by-byte -- //$plaintxtByte[$i] = $cipherCntr[$i] ^ ord(substr($ciphertext[$b],$i,1)); byte bone = (byte)(cipherCntr[i] ^ ct[b][i]); //$plaintxtByte[$i] = chr($plaintxtByte[$i]); //sb.Append((char)bone); all_bs[counter] = bone; counter++; } } // join array of blocks into single plaintext string //$plaintext = implode('',$plaintxt); //System.Text.UnicodeEncoding converter = new System.Text.UnicodeEncoding(); return(System.Text.Encoding.UTF8.GetString(all_bs)); //return sb.ToString(); }