public static byte[] Decrypt(byte[] bytes, Dictionary<ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role) { Transform_Header transformHeader = Smb2Utility.UnmarshalStructure<Transform_Header>(bytes); if (transformHeader.SessionId == 0 || !cryptoInfoTable.ContainsKey(transformHeader.SessionId)) throw new InvalidOperationException("Invalid SessionId in TRANSFORM_HEADER."); Smb2CryptoInfo cryptoInfo = cryptoInfoTable[transformHeader.SessionId]; using (var bcrypt = new BCryptAlgorithm("AES")) { int nonceLength = 0; BCryptCipherMode mode = BCryptCipherMode.NotAvailable; GetCryptoParams(cryptoInfo, CryptoOperationType.Decrypt, out mode, out nonceLength); bcrypt.Mode = mode; bcrypt.Key = role == Smb2Role.Server ? cryptoInfo.ServerInKey : cryptoInfo.ServerOutKey; return bcrypt.Decrypt(bytes.Skip(52).ToArray(), transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(), bytes.Skip(20).Take(32).ToArray(), transformHeader.Signature); } }
/// <summary> /// Decrypt a byte array. /// Many data in NRPC struct is required to be encrypted. /// </summary> /// <param name="isAesNegotiated"> /// Is AES negotiated during secure channel setup. /// If AES is not negotiated, use RC4 to decrypt. /// </param> /// <param name="sessionKey"> /// Session key. /// </param> /// <param name="buffer"> /// Buffer to decrypt. /// </param> /// <returns> /// Decrypted buffer. /// </returns> /// <exception cref="ArgumentNullException"> /// Thrown when sessionKey or buffer is null. /// </exception> /// <exception cref="ArgumentException"> /// Thrown when session key length is incorrect. /// </exception> public static byte[] DecryptBuffer( bool isAesNegotiated, byte[] sessionKey, byte[] buffer) { if (sessionKey == null) { throw new ArgumentNullException("sessionKey"); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (sessionKey.Length != NETLOGON_SESSION_KEY_LENGTH) { throw new ArgumentException("Session key length is incorrect.", "sessionKey"); } if (isAesNegotiated) { using (BCryptAlgorithm aes = new BCryptAlgorithm("AES")) { aes.Mode = BCryptCipherMode.CFB; aes.Key = sessionKey; aes.IV = new byte[aes.BlockSize]; return aes.Decrypt(buffer); } } else { using (RC4 rc4 = RC4.Create()) { rc4.Key = sessionKey; return rc4.CreateDecryptor().TransformFinalBlock(buffer, 0, buffer.Length); } } }
/// <summary> /// validate netlogon signature token when AES is negotiated /// </summary> /// <param name="sequenceNumber">sequence number</param> /// <param name="sessionKey">session key</param> /// <param name="requestConfidentiality">confidentiality is required or not</param> /// <param name="isClientSideOutbound">Is client side outbound message.</param> /// <param name="securityBuffers"> /// Security buffer, contains plain-text if requestConfidentiality is false; /// or cipher-text if requestConfidentiality is true; /// and signature. /// </param> /// <returns>true if validate success; otherwise, false.</returns> private static bool ValidateNetlogonSignatureTokenWhenAesIsNegotiated( ref ulong sequenceNumber, byte[] sessionKey, bool requestConfidentiality, bool isClientSideOutbound, SecurityBuffer[] securityBuffers) { byte[] plainText; byte[] cipherText; byte[] token; //If AES is negotiated, a server receives an NL_AUTH_SHA2_SIGNATURE structure token = SspiUtility.ConcatenateSecurityBuffers(securityBuffers, SecurityBufferType.Token); NL_AUTH_SHA2_SIGNATURE nlAuthSha2Sign = TypeMarshal.ToStruct<NL_AUTH_SHA2_SIGNATURE>(token); //The SignatureAlgorithm bytes MUST be verified to ensure: //If AES is negotiated, the first byte is set to 0x13. //The second byte is set to 0x00 if (nlAuthSha2Sign.SignatureAlgorithm != NL_AUTH_SHA2_SIGNATURE_SignatureAlgorithm_Values.HMACSHA256) { return false; } if (requestConfidentiality) { //If the Confidentiality option is requested from the application, //then the SealAlgorithm MUST be verified to ensure that //if AES is negotiated, the first byte is set to 0x1A; //The second byte is set to 0x00. if (nlAuthSha2Sign.SealAlgorithm != NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.AES128) { return false; } } else { //If the Confidentiality option is not requested, //then the SealAlgorithm MUST be verified to contain all 0xff bytes. if (nlAuthSha2Sign.SealAlgorithm != NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.NotEncrypted) { return false; } } //The Pad MUST be verified to contain all 0xff bytes. if (nlAuthSha2Sign.Pad != NL_AUTH_SHA2_SIGNATURE_Pad_Values.V1) { return false; } //The Flags data MAY be<86> disregarded. //The SequenceNumber MUST be decrypted. //If AES is negotiated, then the server MUST use the AES128 algorithm //and a key of SessionKey and an initialization vector constructed //by concatenating the checksum with itself (thus getting 16 bytes of data). using (BCryptAlgorithm aes = new BCryptAlgorithm("AES")) { aes.Mode = BCryptCipherMode.CFB; aes.Key = sessionKey; aes.IV = ArrayUtility.ConcatenateArrays( nlAuthSha2Sign.Checksum, nlAuthSha2Sign.Checksum); nlAuthSha2Sign.SequenceNumber = aes.Decrypt(nlAuthSha2Sign.SequenceNumber); } //A local copy of SequenceNumber MUST be computed using the following algorithm. byte[] copySequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound); //The SequenceNumber MUST be compared to CopySeqNumber. if (!ArrayUtility.CompareArrays( copySequenceNumber, nlAuthSha2Sign.SequenceNumber)) { return false; } //ServerSequenceNumber MUST be incremented. sequenceNumber += 1; if (requestConfidentiality) { //If the Confidentiality option is requested, //the Confounder and the data MUST be decrypted. byte[] aesEncryptionKey = ComputeEncryptionKeyWhenAesIsNegotiated(sessionKey); using (BCryptAlgorithm aes = new BCryptAlgorithm("AES")) { aes.Mode = BCryptCipherMode.CFB; aes.Key = aesEncryptionKey; aes.IV = ArrayUtility.ConcatenateArrays( nlAuthSha2Sign.SequenceNumber, nlAuthSha2Sign.SequenceNumber); nlAuthSha2Sign.Confounder = aes.Decrypt(nlAuthSha2Sign.Confounder); cipherText = SspiUtility.ConcatenateReadWriteSecurityBuffers( securityBuffers, SecurityBufferType.Data); plainText = aes.Decrypt(cipherText); } SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, plainText); } //Compute signature plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers); byte[] checksum = ComputeSignatureWhenAesIsNegotiated( nlAuthSha2Sign, sessionKey, requestConfidentiality, plainText); //The first 8 bytes of the computed signature MUST be compared to the checksum. if (checksum != null && checksum.Length >= NL_AUTH_SIGNATURE_CHECKSUM_LENGTH && nlAuthSha2Sign.Checksum != null && nlAuthSha2Sign.Checksum.Length >= NL_AUTH_SIGNATURE_CHECKSUM_LENGTH) { for (int i = 0; i < NL_AUTH_SIGNATURE_CHECKSUM_LENGTH; i++) { if (checksum[i] != nlAuthSha2Sign.Checksum[i]) { return false; } } } return true; }