Ejemplo n.º 1
0
        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);
        }
Ejemplo n.º 2
0
        /**
         * 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();
        }