/// <summary> /// Validates the client Netlogon authenticator by incrementing the Netlogon credential /// in the Netlogon authenticator by one, performs the computation described in section /// 3.1.4.4, Netlogon Credential Computation, and stores the new Netlogon credential. /// The server returns a Netlogon authenticator that contains the new Netlogon credential /// to the client. /// </summary> /// <param name="clientAuthenticator"> /// Authenticator from client to validate. /// </param> /// <param name="algorithm"> /// Algorithm to validate the authenticator. /// </param> /// <param name="serverStoredCredential"> /// A byte array containing the credential that is maintained by /// the server and that is used during computation and /// verification of the Netlogon authenticator. /// </param> /// <param name="sessionKey">Session Key</param> /// <returns>Return true if validate successfully; otherwise, false.</returns> /// <exception cref="ArgumentNullException"> /// Thrown when serverStoredCredential or sessionKey is null. /// </exception> /// <exception cref="ArgumentException"> /// Thrown when Credential.data field of clientAuthenticator is null. /// Thrown when session key length is incorrect. /// </exception> public static bool ValidateClientNetlogonAuthenticator( _NETLOGON_AUTHENTICATOR clientAuthenticator, NrpcComputeNetlogonCredentialAlgorithm algorithm, ref byte[] serverStoredCredential, byte[] sessionKey) { if (serverStoredCredential == null) { throw new ArgumentNullException("serverStoredCredential"); } if (sessionKey == null) { throw new ArgumentNullException("sessionKey"); } if (sessionKey.Length != NETLOGON_SESSION_KEY_LENGTH) { throw new ArgumentException("Session key length is incorrect.", "sessionKey"); } if (clientAuthenticator.Credential.data == null) { throw new ArgumentException("clientAuthenticator is invalid.", "clientAuthenticator"); } //SET ServerStoredCredential = ServerStoredCredential + // ClientAuthenticator.Timestamp; //CALL ComputeNetlogonCredential(ServerStoredCredential, // Session-Key, TempCredential); //IF TempCredential != ClientAuthenticator.Credential //THEN return access denied error //In each of the addition operations previously performed, //the least-significant 4 bytes of the credential are added //with the 4-byte time stamp value (or the constant 1), //and overflow is ignored. This leaves the most-significant //4 bytes of the credential unmodified. uint credential = BitConverter.ToUInt32(serverStoredCredential, 0); credential += clientAuthenticator.Timestamp; byte[] buf = BitConverter.GetBytes(credential); Array.Copy(buf, serverStoredCredential, buf.Length); byte[] tempCredential = ComputeNetlogonCredential( algorithm, serverStoredCredential, sessionKey); return ArrayUtility.CompareArrays(tempCredential, clientAuthenticator.Credential.data); }
/// <summary> /// Compute the Netlogon Credential. /// </summary> /// <param name="algorithm"> /// Algorithm to compute the Netlogon authenticator. /// </param> /// <param name="input"> /// A byte array that contains the input. /// </param> /// <param name="sessionKey"> /// Session Key. /// </param> /// <returns>Netlogon Credential</returns> /// <exception cref="ArgumentNullException"> /// Thrown when input or sessionKey parameter /// passed to the method is null. /// </exception> /// <exception cref="ArgumentException"> /// Thrown when computation algorithm is not supported. /// Thrown when session key length is incorrect. /// </exception> public static byte[] ComputeNetlogonCredential( NrpcComputeNetlogonCredentialAlgorithm algorithm, byte[] input, byte[] sessionKey) { if (input == null) { throw new ArgumentNullException("input"); } if (sessionKey == null) { throw new ArgumentNullException("sessionKey"); } if (sessionKey.Length != NETLOGON_SESSION_KEY_LENGTH) { throw new ArgumentException("Session key length is incorrect.", "sessionKey"); } byte[] credential; switch (algorithm) { case NrpcComputeNetlogonCredentialAlgorithm.AES128: //ComputeNetlogonCredential(Input, Sk, Output) //SET IV = 0 //CALL AesEncrypt(Input, Sk, IV, Output) using (BCryptAlgorithm aes = new BCryptAlgorithm("AES")) { aes.Mode = BCryptCipherMode.CFB; aes.Key = sessionKey; aes.IV = new byte[aes.BlockSize]; //AES128's IV is 128 bits. credential = aes.Encrypt(input); } break; case NrpcComputeNetlogonCredentialAlgorithm.DESECB: //ComputeNetlogonCredential(Input, Sk, Output) //SET k1 to bytes(0, 6, Sk) //CALL InitLMKey(k1, k3) //SET k2 to bytes(7, 13, Sk) //CALL InitLMKey(k2, k4) //CALL DES_ECB(Input, k3, &output1) //CALL DES_ECB(output1, k4, &output2) //SET Output to output2 byte[] k1 = ArrayUtility.SubArray(sessionKey, 0, 7); byte[] k2 = ArrayUtility.SubArray(sessionKey, 7, 7); using (DES des = DES.Create()) { des.Mode = CipherMode.ECB; des.Padding = PaddingMode.None; des.Key = InitLMKey(k1); byte[] output1 = des.CreateEncryptor().TransformFinalBlock(input, 0, input.Length); des.Key = InitLMKey(k2); byte[] output2 = des.CreateEncryptor().TransformFinalBlock(output1, 0, output1.Length); credential = output2; } break; default: throw new ArgumentException( "Specified netlogon credential computation algorithm is not valid.", "algorithm"); } return credential; }
/// <summary> /// Compute the Netlogon Authenticator at server-side. /// </summary> /// <param name="algorithm"> /// Algorithm to compute the authenticator. /// </param> /// <param name="serverStoredCredential"> /// A byte array containing the credential that is created by the server and /// received by the client and that is used during computation and /// verification of the Netlogon authenticator. /// </param> /// <param name="sessionKey"> /// Session Key. /// </param> /// <returns>Netlogon Authenticator</returns> /// <exception cref="ArgumentNullException"> /// Thrown when serverStoredCredential or sessionKey is null. /// </exception> /// <exception cref="ArgumentException"> /// Thrown when session key length is incorrect. /// </exception> public static _NETLOGON_AUTHENTICATOR ComputeServerNetlogonAuthenticator( NrpcComputeNetlogonCredentialAlgorithm algorithm, ref byte[] serverStoredCredential, byte[] sessionKey) { if (serverStoredCredential == null) { throw new ArgumentNullException("serverStoredCredential"); } if (sessionKey == null) { throw new ArgumentNullException("sessionKey"); } if (sessionKey.Length != NETLOGON_SESSION_KEY_LENGTH) { throw new ArgumentException("Session key length is incorrect.", "sessionKey"); } //SET ServerStoredCredential = ServerStoredCredential + 1; //CALL ComputeNetlogonCredential(ServerStoredCredential, //Session-Key, ServerAuthenticator.Credential); _NETLOGON_AUTHENTICATOR authenticator = new _NETLOGON_AUTHENTICATOR(); //In each of the addition operations previously performed, //the least-significant 4 bytes of the credential are added //with the 4-byte time stamp value (or the constant 1), //and overflow is ignored. This leaves the most-significant //4 bytes of the credential unmodified. uint credential = BitConverter.ToUInt32(serverStoredCredential, 0); credential += 1; byte[] buf = BitConverter.GetBytes(credential); Array.Copy(buf, serverStoredCredential, buf.Length); authenticator.Credential = new _NETLOGON_CREDENTIAL(); authenticator.Credential.data = ComputeNetlogonCredential( algorithm, serverStoredCredential, sessionKey); return authenticator; }
/// <summary> /// Compute the Netlogon Authenticator at client-side. /// </summary> /// <param name="algorithm"> /// Algorithm to compute the authenticator. /// </param> /// <param name="time"> /// Current time on client. /// </param> /// <param name="clientStoredCredential"> /// A byte array containing the credential that is created by the client and /// received by the server and that is used during computation and /// verification of the Netlogon authenticator. /// </param> /// <param name="sessionKey"> /// Session Key. /// </param> /// <returns>Netlogon Authenticator</returns> /// <exception cref="ArgumentNullException"> /// Thrown when clientStoredCredential or sessionKey is null. /// </exception> /// <exception cref="ArgumentException"> /// Thrown when session key length is incorrect. /// </exception> public static _NETLOGON_AUTHENTICATOR ComputeClientNetlogonAuthenticator( NrpcComputeNetlogonCredentialAlgorithm algorithm, DateTime time, ref byte[] clientStoredCredential, byte[] sessionKey) { if (clientStoredCredential == null) { throw new ArgumentNullException("clientStoredCredential"); } if (sessionKey == null) { throw new ArgumentNullException("sessionKey"); } if (sessionKey.Length != NETLOGON_SESSION_KEY_LENGTH) { throw new ArgumentException("Session key length is incorrect.", "sessionKey"); } //SET TimeNow = current time; //SET ClientAuthenticator.Timestamp = TimeNow; //SET ClientStoredCredential = ClientStoredCredential + TimeNow; //CALL ComputeNetlogonCredential(ClientStoredCredential, Session-Key, ClientAuthenticator.Credential); //Timestamp is an integer value that contains the time of day at //which the client constructed this authentication credential, //represented as the number of elapsed seconds since 00:00:00 of January 1, 1970 (UTC). DateTime time19700101 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); _NETLOGON_AUTHENTICATOR authenticator = new _NETLOGON_AUTHENTICATOR(); authenticator.Timestamp = (uint)( (time.ToUniversalTime() - time19700101).Ticks / TimeSpan.TicksPerSecond); //In each of the addition operations previously performed, //the least-significant 4 bytes of the credential are added //with the 4-byte time stamp value (or the constant 1), //and overflow is ignored. This leaves the most-significant //4 bytes of the credential unmodified. uint credential = BitConverter.ToUInt32(clientStoredCredential, 0); credential += authenticator.Timestamp; byte[] buf = BitConverter.GetBytes(credential); Array.Copy(buf, clientStoredCredential, buf.Length); authenticator.Credential = new _NETLOGON_CREDENTIAL(); authenticator.Credential.data = ComputeNetlogonCredential( algorithm, clientStoredCredential, sessionKey); return authenticator; }