/// <summary> /// Decrypt in aes128-cts-hmac-sha1-96 or aes256-cts-hmac-sha1-96 /// </summary> /// <param name="key">key data</param> /// <param name="cipher">cipher data to be decrypted</param> /// <param name="usage">key usage number</param> /// <param name="aesKeyType">aes key type (128bit/256bit)</param> /// <returns>the decrypted data</returns> public static byte[] Decrypt( byte[] key, byte[] cipher, int usage, AesKeyType aesKeyType) { return Decrypt(key, cipher, usage, aesKeyType, null); }
/// <summary> /// Decrypt in aes128-cts-hmac-sha1-96 or aes256-cts-hmac-sha1-96 /// </summary> /// <param name="key">key data</param> /// <param name="cipher">cipher data to be decrypted</param> /// <param name="usage">key usage number</param> /// <param name="aesKeyType">aes key type (128bit/256bit)</param> /// <param name="getToBeSignedDateCallback"> /// A callback to get to-be-signed data. /// The method will use decrypted data directly if this parameter is null. /// </param> /// <returns>the decrypted data</returns> public static byte[] Decrypt( byte[] key, byte[] cipher, int usage, AesKeyType aesKeyType, GetToBeSignedDataFunc getToBeSignedDateCallback) { // check inputs if (null == key) { throw new ArgumentNullException("key"); } if (null == cipher) { throw new ArgumentNullException("cipher"); } // the cipher has two parts: encrypted(confounder + plain) + hmac byte[] encryptedData = ArrayUtility.SubArray<byte>( cipher, 0, cipher.Length - ConstValue.HMAC_HASH_OUTPUT_SIZE); byte[] hmacData = ArrayUtility.SubArray<byte>( cipher, cipher.Length - ConstValue.HMAC_HASH_OUTPUT_SIZE); // use ke key (the encryption key) to decrypt byte[] ke = AesKey.MakeDerivedKey(key, usage, DerivedKeyType.Ke, aesKeyType); byte[] initialVector = new byte[ConstValue.AES_BLOCK_SIZE]; CipherTextStealingMode aesCtsCrypto = CryptoUtility.CreateAesCtsCrypto(ke, initialVector); byte[] decryptedData = aesCtsCrypto.DecryptFinal(encryptedData, 0, encryptedData.Length); byte[] toBeSignedData; if (getToBeSignedDateCallback != null) { toBeSignedData = getToBeSignedDateCallback(decryptedData); } else { toBeSignedData = decryptedData; } // use ki key (the integrity key) to verify hmac data byte[] ki = AesKey.MakeDerivedKey(key, usage, DerivedKeyType.Ki, aesKeyType); byte[] expectedHmacData = CryptoUtility.ComputeHmacSha1(ki, toBeSignedData); expectedHmacData = ArrayUtility.SubArray<byte>(expectedHmacData, 0, ConstValue.HMAC_HASH_OUTPUT_SIZE); if (!ArrayUtility.CompareArrays<byte>(hmacData, expectedHmacData)) { throw new FormatException( "Decryption: failed integrity check in hmac checksum."); } // remove confounder data decryptedData = ArrayUtility.SubArray<byte>(decryptedData, ConstValue.AES_BLOCK_SIZE, decryptedData.Length - ConstValue.AES_BLOCK_SIZE); return decryptedData; }
/// <summary> /// Get Hmac-Sha1-Aes Checksum /// </summary> /// <param name="key">the key</param> /// <param name="input">input data</param> /// <param name="usage">usage number</param> /// <param name="aesKeyType">aes key type which decides key size</param> /// <returns>the calculated checksum</returns> public static byte[] GetMic( byte[] key, byte[] input, int usage, AesKeyType aesKeyType) { // make the kc key from aes key generation byte[] kcKey = AesKey.MakeDerivedKey( key, usage, DerivedKeyType.Kc, aesKeyType); // get mic from kc key and input byte[] hash = CryptoUtility.ComputeHmacSha1(kcKey, input); return ArrayUtility.SubArray(hash, 0, ConstValue.HMAC_HASH_OUTPUT_SIZE); }
/// <summary> /// DR is the random-octet generation function described in RFC 3961 /// [RFC 3961 section 5.1 A Key Derivation Function] /// </summary> /// <param name="baseKey">the base key which is to be derived from</param> /// <param name="wellKnownConstant">the "well-known constant"</param> /// <param name="aesKeyType">AES key type which decides key size</param> /// <returns>the pseudorandom octets</returns> private static byte[] DR( byte[] baseKey, byte[] wellKnownConstant, AesKeyType aesKeyType) { // to be encrypted data byte[] toBeEncrypted = new byte[wellKnownConstant.Length]; wellKnownConstant.CopyTo(toBeEncrypted, 0); // n-fold the "well-known constant" if needed if (wellKnownConstant.Length != ConstValue.AES_BLOCK_SIZE) { toBeEncrypted = NFold(wellKnownConstant, ConstValue.AES_BLOCK_SIZE * ConstValue.BYTE_SIZE); } // AES key size uint aesKeySize = (uint)aesKeyType / ConstValue.BYTE_SIZE; // initialize key array byte[] rawkey = new byte[aesKeySize]; // count means the total number of bytes has been copy to the rawkey. // length means how length of bytes should be copy to the rawkey array. uint count = 0; uint length = 0; // The initialCipherVector should be all zeros. byte[] initialCipherVector = new byte[ConstValue.AES_BLOCK_SIZE]; // AES-CTS encryptor CipherTextStealingMode aesCtsCrypto = CryptoUtility.CreateAesCtsCrypto(baseKey, initialCipherVector); while (count < aesKeySize) { byte[] cipherBlock = aesCtsCrypto.EncryptFinal(toBeEncrypted, 0, toBeEncrypted.Length); length = (aesKeySize - count <= cipherBlock.Length ? (aesKeySize - count) : Convert.ToUInt32(cipherBlock.Length)); Array.Copy(cipherBlock, 0, rawkey, count, length); count += length; toBeEncrypted = cipherBlock; } return rawkey; }
/// <summary> /// Generate an encryption key from password and salt /// [RFC 3962 Section 4 Key Generation from Pass Phrases or Random Data] /// (The pseudorandom function used by PBKDF2 will be a SHA-1 HMAC of /// the passphrase and salt, as described in Appendix B.1 to PKCS#5) /// </summary> /// <param name="password">password</param> /// <param name="salt">salt</param> /// <param name="iterationCount">interation count</param> /// <param name="keyType">AES key type which decides key size</param> /// <returns>the encrypted key in bytes</returns> internal static byte[] MakeStringToKey( string password, string salt, uint iterationCount, AesKeyType keyType) { if (null == password) { throw new ArgumentNullException("password"); } if (null == salt) { throw new ArgumentNullException("salt"); } byte[] passwordBytes = Encoding.UTF8.GetBytes(password); byte[] saltBytes = Encoding.UTF8.GetBytes(salt); int keySize = (int)keyType / ConstValue.BYTE_SIZE; // generate the intermediate key Rfc2898DeriveBytes PBKDF2 = new Rfc2898DeriveBytes(passwordBytes, saltBytes, (int)iterationCount); byte[] intermediateKey = PBKDF2.GetBytes(keySize); intermediateKey = RandomToKey(intermediateKey); // generate the final key return DK(intermediateKey, KERBEROS_CONSTANT, keyType); }
/// <summary> /// Generate a derived key from base key /// [RFC 3961 Section 5.1 A Key Derivation Function] /// </summary> /// <param name="baseKey">the base key</param> /// <param name="usage">key usage</param> /// <param name="derivedKeyType">the derived key type</param> /// <param name="aesKeyType">AES key type which decides key size</param> /// <returns>the derived key in bytes</returns> internal static byte[] MakeDerivedKey( byte[] baseKey, int usage, DerivedKeyType derivedKeyType, AesKeyType aesKeyType) { if (null == baseKey) { throw new ArgumentNullException("baseKey"); } // generate the well-known constant byte[] wellKnownConstant = GetWellKnownConstant(usage, derivedKeyType); // generate the derived key return DK(baseKey, wellKnownConstant, aesKeyType); }
/// <summary> /// DK is the key-derivation function described in RFC 3961 /// [RFC 3961 section 5.1 A Key Derivation Function] /// </summary> /// <param name="baseKey">the base key</param> /// <param name="wellKnownConstant">the "well-known constant"</param> /// <param name="aesKeyType">AES key type which decides key size</param> /// <returns>the derived key in bytes</returns> public static byte[] DK( byte[] baseKey, byte[] wellKnownConstant, AesKeyType aesKeyType) { // caculate DR value byte[] drBytes = DR(baseKey, wellKnownConstant, aesKeyType); // caculate Random return RandomToKey(drBytes); }
/// <summary> /// Encrypt in aes128-cts-hmac-sha1-96 or aes256-cts-hmac-sha1-96 /// </summary> /// <param name="key">key data</param> /// <param name="plain">plain data to be encrypted</param> /// <param name="usage">key usage number</param> /// <param name="aesKeyType">aes key type (128bit/256bit)</param> /// <param name="getToBeSignedDateCallback"> /// A callback to get to-be-signed data. /// The method will use plain-text data directly if this parameter is null. /// </param> /// <returns>the encrypted data</returns> public static byte[] Encrypt( byte[] key, byte[] plain, int usage, AesKeyType aesKeyType, GetToBeSignedDataFunc getToBeSignedDateCallback) { // check inputs if (null == key) { throw new ArgumentNullException("key"); } if (null == plain) { throw new ArgumentNullException("plain"); } // add confounder data byte[] plainData = ArrayUtility.ConcatenateArrays( CryptoUtility.CreateConfounder(ConstValue.AES_BLOCK_SIZE), plain); // use ke key (the encryption key) to encrypt the plain data byte[] ke = AesKey.MakeDerivedKey(key, usage, DerivedKeyType.Ke, aesKeyType); byte[] initialVector = new byte[ConstValue.AES_BLOCK_SIZE]; CipherTextStealingMode aesCtsCrypto = CryptoUtility.CreateAesCtsCrypto(ke, initialVector); byte[] encryptedData = aesCtsCrypto.EncryptFinal(plainData, 0, plainData.Length); byte[] toBeSignedData; if (getToBeSignedDateCallback != null) { toBeSignedData = getToBeSignedDateCallback(plainData); } else { toBeSignedData = plainData; } // use ki key (the integrity key) to generate checksum byte[] ki = AesKey.MakeDerivedKey(key, usage, DerivedKeyType.Ki, aesKeyType); byte[] hmacData = CryptoUtility.ComputeHmacSha1(ki, toBeSignedData); hmacData = ArrayUtility.SubArray<byte>(hmacData, 0, ConstValue.HMAC_HASH_OUTPUT_SIZE); // result: encryptedData + hmacData return ArrayUtility.ConcatenateArrays(encryptedData, hmacData); }
/// <summary> /// Encrypt in aes128-cts-hmac-sha1-96 or aes256-cts-hmac-sha1-96 /// </summary> /// <param name="key">key data</param> /// <param name="plain">plain data to be encrypted</param> /// <param name="usage">key usage number</param> /// <param name="aesKeyType">aes key type (128bit/256bit)</param> /// <returns>the encrypted data</returns> public static byte[] Encrypt( byte[] key, byte[] plain, int usage, AesKeyType aesKeyType) { return Encrypt(key, plain, usage, aesKeyType, null); }