/// <summary> /// initial netlogon signature token when AES is not 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 buffers, contains input plain-text; output cipher-text and signature. /// </param> private static void InitialNetlogonSignatureTokenWhenAesIsNotNegotiated( ref ulong sequenceNumber, byte[] sessionKey, bool requestConfidentiality, bool isClientSideOutbound, SecurityBuffer[] securityBuffers) { byte[] plainText; byte[] cipherText; byte[] token; NL_AUTH_SIGNATURE nlAuthSign = new NL_AUTH_SIGNATURE(); //The SignatureAlgorithm first byte MUST be set to 0x77 //and the second byte MUST be set to 0x00. nlAuthSign.SignatureAlgorithm = SignatureAlgorithm_Values.HMACMD5; if (requestConfidentiality) { //If the Confidentiality option (section 3.3.1) is requested //from the application, then the SealAlgorithm first byte MUST //be set to 0x7A, the second byte MUST be set to 0x00, and //the Confounder MUST be filled with cryptographically random data. nlAuthSign.SealAlgorithm = SealAlgorithm_Values.RC4; nlAuthSign.Confounder = GenerateNonce(NL_AUTH_SIGNATURE_CONFOUNDER_LENGTH); } else { //If the Confidentiality option is not requested, then the //SealAlgorithm MUST be filled with two bytes of value 0xff, //and the Confounder is not included in the token. nlAuthSign.SealAlgorithm = SealAlgorithm_Values.NotEncrypted; nlAuthSign.Confounder = null; } //The Pad MUST be filled with 0xff bytes. nlAuthSign.Pad = Pad_Values.V1; //The Flags MUST be filled with 0x00 bytes. nlAuthSign.Flags = new byte[NL_AUTH_SIGNATURE_FLAGS_LENGTH]; //The SequenceNumber MUST be computed using the following algorithm. nlAuthSign.SequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound); //The ClientSequenceNumber MUST be incremented by 1. sequenceNumber += 1; //Compute signature plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers); nlAuthSign.Checksum = ComputeSignatureWhenAesIsNotNegotiated( nlAuthSign, sessionKey, requestConfidentiality, plainText); //If the Confidentiality option is requested, the data and the Confounder //field MUST be encrypted. If AES is not negotiated, it MUST use the //RC4 algorithm. if (requestConfidentiality) { byte[] rc4EncryptionKey = ComputeEncryptionKeyWhenAesIsNotNegotiated( sessionKey, nlAuthSign.SequenceNumber); using (RC4 rc4 = RC4.Create()) { rc4.Key = rc4EncryptionKey; nlAuthSign.Confounder = rc4.CreateEncryptor().TransformFinalBlock( nlAuthSign.Confounder, 0, nlAuthSign.Confounder.Length); plainText = SspiUtility.ConcatenateReadWriteSecurityBuffers( securityBuffers, SecurityBufferType.Data); cipherText = rc4.CreateEncryptor().TransformFinalBlock( plainText, 0, plainText.Length); } SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, cipherText); } else { cipherText = null; } //The SequenceNumber MUST be encrypted. //If AES is not negotiated, it MUST use the RC4 algorithm. //The RC4 key MUST be derived as follows: //SET zeroes to 4 bytes of 0 //CALL hmac_md5(zeroes, [4 bytes], SessionKey, size of SessionKey, TmpData) //CALL hmac_md5(Checksum, size of Checksum, TmpData, size of TmpData, EncryptionKey) byte[] tmpData; byte[] sequenceNumberEncryptionKey; using (HMACMD5 hmacMd5 = new HMACMD5(sessionKey)) { tmpData = hmacMd5.ComputeHash(new byte[4]); } using (HMACMD5 hmacMd5 = new HMACMD5(tmpData)) { sequenceNumberEncryptionKey = hmacMd5.ComputeHash(nlAuthSign.Checksum); } using (RC4 rc4 = RC4.Create()) { rc4.Key = sequenceNumberEncryptionKey; nlAuthSign.SequenceNumber = rc4.CreateEncryptor().TransformFinalBlock( nlAuthSign.SequenceNumber, 0, nlAuthSign.SequenceNumber.Length); } token = TypeMarshal.ToBytes(nlAuthSign); SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Token, token); }
/// <summary> /// Compute signature when AES is not negotiated /// </summary> /// <param name="nlAuthSign">NL_AUTH_SIGNATURE struct</param> /// <param name="sessionKey">session key</param> /// <param name="requestConfidentiality">confidentiality is required or not</param> /// <param name="plainText">plain-text message to make signature</param> /// <returns>signature</returns> private static byte[] ComputeSignatureWhenAesIsNotNegotiated( NL_AUTH_SIGNATURE nlAuthSign, byte[] sessionKey, bool requestConfidentiality, byte[] plainText) { //If AES is not negotiated, a signature MUST be computed using //the following algorithm: //SET zeroes to 4 bytes of 0 //CALL MD5Init(md5context) //CALL MD5Update(md5context, zeroes, [4 bytes]) //CALL MD5Update(md5context, NL_AUTH_SIGNATURE, [8 bytes]) //IF Confidentiality requested //CALL MD5Update(md5context, Confounder, [8 bytes]) //CALL MD5Update(md5context, Message, size of Message) //CALL MD5Final(md5context) //CALL HMAC_MD5(md5context.digest, md5context.digest length, //Session-Key, size of Session Key, output) //SET Signature to output //Note: In the second call to MD5Update, only the first 8-bytes //of the NL_AUTH_SIGNATURE structure are used. byte[] signature; byte[] buf = ArrayUtility.ConcatenateArrays( new byte[4], BitConverter.GetBytes((ushort)nlAuthSign.SignatureAlgorithm), BitConverter.GetBytes((ushort)nlAuthSign.SealAlgorithm), BitConverter.GetBytes((ushort)nlAuthSign.Pad), nlAuthSign.Flags); if (requestConfidentiality) { buf = ArrayUtility.ConcatenateArrays( buf, nlAuthSign.Confounder); } buf = ArrayUtility.ConcatenateArrays( buf, plainText); using (MD5 md5 = MD5.Create()) { signature = md5.ComputeHash(buf); } using (HMACMD5 hmacMd5 = new HMACMD5(sessionKey)) { signature = hmacMd5.ComputeHash(signature); } //After the signature is computed, the signature MUST be truncated, //with only the first 8 bytes being copied into the Checksum field //of NL_AUTH_SIGNATURE. signature = ArrayUtility.SubArray(signature, 0, NL_AUTH_SIGNATURE_CHECKSUM_LENGTH); return signature; }