/// <summary> /// Determine the negotiated MechType that supported by both sides /// </summary> /// <param name="localMechList">The local MechType list</param> /// <param name="remoteMechList">The remote MechType list</param> /// <exception cref="ApplicationException">Throw exception if negotiation failed</exception> private void Negotiate(MechTypeList localMechList, MechTypeList remoteMechList) { // remoteList is the first priority to compare for (int i = 0; i < remoteMechList.Elements.Length; i++) { // find in localList for (int j = 0; j < localMechList.Elements.Length; j++) { // if support current MechType if (ArrayUtility.CompareArrays <int>(remoteMechList.Elements[i].Value, localMechList.Elements[j].Value)) { this.Context.NegotiatedMechType = localMechList.Elements[j]; if (i == 0) { // "i == 0" means it is the perferred MechType of remote endpoint this.Context.NegotiationState = SpngNegotiationState.AcceptIncomplete; } else { this.Context.NegotiationState = SpngNegotiationState.RequestMic; } return; } } } if (this.Context.NegotiatedMechType == null) { this.Context.NegotiationState = SpngNegotiationState.Reject; } }
/// <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> /// RC4-HMAC / RC4-HMAC-EXP decryption /// </summary> /// <param name="key">the secret key</param> /// <param name="cipher">the encrypted cipher data</param> /// <param name="usage">key usage number</param> /// <param name="encryptionType">encryption type</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, EncryptionType encryptionType, GetToBeSignedDataFunc getToBeSignedDateCallback) { // check inputs if (null == key) { throw new ArgumentNullException("key"); } if (null == cipher) { throw new ArgumentNullException("cipher"); } // get checksum and encrypted data byte[] checksum = ArrayUtility.SubArray(cipher, 0, ConstValue.MD5_CHECKSUM_SIZE); byte[] encryptedData = ArrayUtility.SubArray(cipher, ConstValue.MD5_CHECKSUM_SIZE); // get salt and hash byte[] salt = GetSalt(usage, encryptionType); byte[] hash = CryptoUtility.ComputeMd5Hmac(key, salt); byte[] hashExp = GetExpHash(hash, encryptionType); // get rc4 decryption key byte[] rc4Key = CryptoUtility.ComputeMd5Hmac(hashExp, checksum); // decrypt ICryptoTransform decryptor = CryptoUtility.CreateRc4Decryptor(rc4Key); byte[] plain = decryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length); byte[] toBeSignedData; if (getToBeSignedDateCallback != null) { toBeSignedData = getToBeSignedDateCallback(plain); } else { toBeSignedData = plain; } // verify checksum byte[] expectedChecksum = CryptoUtility.ComputeMd5Hmac(hash, toBeSignedData); if (!ArrayUtility.CompareArrays(checksum, expectedChecksum)) { throw new FormatException("Decryption: invalid checksum."); } // remove confounder return(ArrayUtility.SubArray(plain, ConstValue.CONFOUNDER_SIZE)); }
/// <summary> /// Verify and remove a token header from AP request/response, /// wrap token or getmic token. /// </summary> /// <param name="completeToken">The complete token got from application message. /// This argument can be null. If it is null, null will be returned.</param> /// <returns>The token body without the header.</returns> /// <exception cref="System.FormatException">Throw FormatException if the token header is incorrect.</exception> internal static byte[] VerifyGssApiTokenHeader(byte[] completeToken) { // kerberos oid (1.2.840.113554.1.2.2) byte[] oid = ConstValue.KERBEROS_OID; if (completeToken == null || completeToken.Length < ConstValue.KERBEROS_OID.Length) { throw new FormatException("The GSS-API token header is incomplete!"); } if (completeToken[0] != ConstValue.KERBEROS_TAG) { throw new FormatException("The GSS-API token header is incorrect!"); } int length = 0; int index = 2; // the tag and length fields // If the length value is 128 or more. if ((completeToken[1] & 0x80) == 0x80) { // If the indicated value is 128 or more, it shall be represented in two or more octets, // with bit 8 of the first octet set to "1" and the remaining bits of the first octet // specifying the number of additional octets. The subsequent octets carry the value, // 8 bits per octet, most significant digit first. int num = completeToken[1] & 0x7F; index += num; if (num < 1 || num > 4) { throw new FormatException("The GSS-API token length is incorrect!"); } for (int i = 0; i < num; ++i) { length = (length << 8) | completeToken[2 + i]; } } else { // If the indicated value is less than 128, it shall be represented in a single octet with bit 8 // (high order) set to "0" and the remaining bits representing the value. [rfc 2743] length = completeToken[1]; } if (!ArrayUtility.CompareArrays(oid, ArrayUtility.SubArray(completeToken, index, oid.Length))) { throw new FormatException("The GSS-API token oid is incorrect!"); } byte[] tokenBody = ArrayUtility.SubArray(completeToken, index + oid.Length); return(tokenBody); }
/// <summary> /// DES-CBC-MD5 / DES-CBC-CRC32 decryption /// </summary> /// <param name="key">the secret key</param> /// <param name="cipher">the encrypted cipher data</param> /// <param name="encryptionType">encryption type</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, EncryptionType encryptionType, GetToBeSignedDataFunc getToBeSignedDateCallback) { // check inputs if (null == key) { throw new ArgumentNullException("key"); } if (null == cipher) { throw new ArgumentNullException("cipher"); } // des-cbc decryption byte[] initialVector = GetInitialVector(key, encryptionType); ICryptoTransform decryptor = CryptoUtility.CreateDesCbcDecryptor(key, initialVector, PaddingMode.None); byte[] decryptedData = decryptor.TransformFinalBlock(cipher, 0, cipher.Length); // get checksum, set checksum field to zeros int checksumSize = GetChecksumSize(encryptionType); byte[] checksum = ArrayUtility.SubArray <byte>(decryptedData, ConstValue.DES_BLOCK_SIZE, checksumSize); Array.Clear(decryptedData, ConstValue.DES_BLOCK_SIZE, checksumSize); byte[] toBeSignedData; if (getToBeSignedDateCallback != null) { toBeSignedData = getToBeSignedDateCallback(decryptedData); } else { toBeSignedData = decryptedData; } // verify checksum byte[] expectedChecksum = CalculateChecksum(toBeSignedData, encryptionType); if (!ArrayUtility.CompareArrays <byte>(checksum, expectedChecksum)) { throw new FormatException( "Decryption: inconsistent checksum."); } // remove confounder and checksum decryptedData = ArrayUtility.SubArray <byte>(decryptedData, ConstValue.DES_BLOCK_SIZE + checksumSize); return(decryptedData); }
/// <summary> /// valid the authenticate message /// </summary> /// <param name="authenticate"> /// the authenticate packet of client, contains the challenge response calculated by client /// </param> /// <param name="expectedNtChallengeResponse">the nt challenge response calculated by server</param> /// <param name="expectedLmChallengeResponse">the lm challenge response calculated by server</param> private static void ValidAuthenticateMessage( NlmpAuthenticatePacket authenticate, byte[] expectedNtChallengeResponse, byte[] expectedLmChallengeResponse) { if (authenticate.Payload.NtChallengeResponseFields.Len == 0 || !ArrayUtility.CompareArrays <byte>(expectedNtChallengeResponse, authenticate.Payload.NtChallengeResponse)) { if (authenticate.Payload.LmChallengeResponseFields.Len == 0 || !ArrayUtility.CompareArrays <byte>(expectedLmChallengeResponse, authenticate.Payload.LmChallengeResponse)) { throw new InvalidOperationException("INVALID message error"); } } }
/// <summary> /// Read null terminate string /// </summary> /// <param name="payloadBytes">payload byte array</param> /// <param name="offset">reading offset value</param> /// <param name="textEncoding">text encoding type</param> /// <returns></returns> private static string ReadNullTerminateString( byte[] payloadBytes, ref uint offset, Encoding textEncoding ) { if (offset >= payloadBytes.Length - 1) { throw new ArgumentException( "invalid offset value", "offset"); } int readBytes = 0; int count = (int)(payloadBytes.Length - offset); byte[] nullBytes = textEncoding.GetBytes(ApdsUtility.NULL_TERMINATOR_STRING); StringBuilder sb = new StringBuilder(); using (BinaryReader binaryReader = new BinaryReader( new MemoryStream(payloadBytes, (int)offset, count))) { while (true) { byte[] data = binaryReader.ReadBytes(nullBytes.Length); readBytes += nullBytes.Length; if (data == null) { throw new ArgumentException( "invalid payload byte arrays", "payloadBytes"); } if (ArrayUtility.CompareArrays <byte>(data, nullBytes)) { break; } else { sb.Append(textEncoding.GetString(data)); } } } offset = offset + (uint)readBytes; return(sb.ToString()); }
/// <summary> /// Validates the client credential. /// </summary> /// <param name="sessionKey">the session key negotiated</param> /// <param name="negotiateFlags">the negotiation flags</param> /// <param name="clientChallenge">client challenge received</param> /// <param name="clientCredentialReceived">the client credential received</param> private void ValidateClientCredential(byte[] sessionKey, uint negotiateFlags, byte[] clientChallenge, _NETLOGON_CREDENTIAL?clientCredentialReceived) { if (clientCredentialReceived == null || clientCredentialReceived.Value.data == null) { throw new InvalidOperationException("Client Credential doesn't match"); } byte[] clientCredentialComputed = NrpcUtility.ComputeNetlogonCredential(sessionKey, negotiateFlags, clientChallenge); bool compareResult = ArrayUtility.CompareArrays <byte>(clientCredentialReceived.Value.data, clientCredentialComputed); if (!compareResult) { throw new InvalidOperationException("Client Credential doesn't match"); } StoredCredential = clientCredentialComputed; }
/// <summary> /// Convert MechType to SecurityPackage enum /// </summary> /// <param name="mechType">The MechType value to be convert</param> /// <returns>The converted AuthMech enum value</returns> public static SecurityPackageType ConvertMechType(MechType mechType) { SecurityPackageType authType = SecurityPackageType.Unknown; if (ArrayUtility.CompareArrays <int>(mechType.Value, SspiLib.Consts.MsKerbOidInt)) { authType = SecurityPackageType.Kerberos; } else if (ArrayUtility.CompareArrays <int>(mechType.Value, SspiLib.Consts.NlmpOidInt)) { authType = SecurityPackageType.Ntlm; } else if (ArrayUtility.CompareArrays <int>(mechType.Value, SspiLib.Consts.KerbOidInt)) { authType = SecurityPackageType.Kerberos; } else if (ArrayUtility.CompareArrays <int>(mechType.Value, SspiLib.Consts.NegoExOidInt)) { authType = SecurityPackageType.Unknown; } return(authType); }
/// <summary> /// Verify the authenticate packet locally /// </summary> /// <param name="authenticatePacket">actual authenticate packet</param> /// <param name="authenticateInformation">expected authenticate information</param> /// <param name="exportedSessionKey">exported session key</param> /// <returns></returns> private ClientAuthenticateInfomation VerifyAuthenticatePacketLocally( NlmpAuthenticatePacket authenticatePacket, ClientAuthenticateInfomation authenticateInformation, out byte[] exportedSessionKey) { // valid user name if (authenticateInformation.UserName.ToUpper() != this.nlmpServer.Context.ClientCredential.AccountName.ToUpper()) { throw new InvalidOperationException( "the user name is invalid!" + " the user name retrieved form authenticate packet is not equal to the context."); } // calc the basekeys byte[] responseKeyLm; byte[] expectedNtChallengeResponse; byte[] expectedLmChallengeResponse; byte[] sessionBaseKey; byte[] keyExchangeKey; CalculateBaseKeys( authenticateInformation.ClientChallenge, this.systemTime, authenticateInformation.ServerName, authenticateInformation.DomainName, authenticateInformation.UserName, this.nlmpServer.Context.ClientCredential.Password, out responseKeyLm, out expectedNtChallengeResponse, out expectedLmChallengeResponse, out sessionBaseKey, out keyExchangeKey); // valid message ValidAuthenticateMessage(authenticatePacket, expectedNtChallengeResponse, expectedLmChallengeResponse); // generate keys. if (NlmpUtility.IsKeyExch(this.nlmpServer.Context.NegFlg)) { exportedSessionKey = NlmpUtility.RC4( keyExchangeKey, authenticatePacket.Payload.EncryptedRandomSessionKey); } else { exportedSessionKey = keyExchangeKey; } // validate mic byte[] messageMic = authenticatePacket.Payload.MIC; byte[] zeroMic = new byte[16]; if (messageMic != null && !ArrayUtility.CompareArrays <byte>(messageMic, zeroMic)) { AUTHENTICATE_MESSAGE payload = authenticatePacket.Payload; payload.MIC = zeroMic; authenticatePacket.Payload = payload; byte[] mic = NlmpUtility.GetMic(exportedSessionKey, this.negotiate, this.challenge, this.authenticate); if (!ArrayUtility.CompareArrays <byte>(messageMic, mic)) { throw new InvalidOperationException("mic of authenticate packet is invalid"); } } return(authenticateInformation); }
/// <summary> /// update context on receiving RPCE CO Bind PDU. /// </summary> /// <param name="bindPdu">Bind PDU to receive.</param> internal void UpdateContextOnReceivingBindPdu(RpceCoBindPdu bindPdu) { this.MaxTransmitFragmentSize = bindPdu.max_xmit_frag; this.MaxReceiveFragmentSize = bindPdu.max_recv_frag; if (bindPdu.assoc_group_id != 0) { this.AssociateGroupId = bindPdu.assoc_group_id; } else { if (this.associateGroupIdList.Count == 0) { this.AssociateGroupId = bindPdu.assoc_group_id + 1; } else { this.associateGroupIdList.Sort(); this.AssociateGroupId = this.associateGroupIdList[this.associateGroupIdList.Count - 1] + 1; } } associateGroupIdList.Add(this.AssociateGroupId); if (bindPdu.p_context_elem.p_cont_elem != null && bindPdu.p_context_elem.p_cont_elem.Length > 0) { this.InterfaceId = bindPdu.p_context_elem.p_cont_elem[0].abstract_syntax.if_uuid; this.InterfaceMajorVersion = bindPdu.p_context_elem.p_cont_elem[0].abstract_syntax.if_vers_major; this.InterfaceMinorVersion = bindPdu.p_context_elem.p_cont_elem[0].abstract_syntax.if_vers_minor; this.NdrVersion = RpceNdrVersion.None; this.PresentationContextsTable.Clear(); for (int i = 0; i < bindPdu.p_context_elem.p_cont_elem.Length; i++) { p_cont_elem_t p_cont_elem = bindPdu.p_context_elem.p_cont_elem[i]; if (p_cont_elem.transfer_syntaxes == null) { continue; } for (int j = 0; j < p_cont_elem.transfer_syntaxes.Length; j++) { p_syntax_id_t transfer_syntax = p_cont_elem.transfer_syntaxes[j]; if (transfer_syntax.if_uuid == RpceUtility.NDR_INTERFACE_UUID && transfer_syntax.if_vers_major == RpceUtility.NDR_INTERFACE_MAJOR_VERSION && transfer_syntax.if_vers_minor == RpceUtility.NDR_INTERFACE_MINOR_VERSION) { this.NdrVersion |= RpceNdrVersion.NDR; this.PresentationContextsTable.Add(p_cont_elem.p_cont_id, RpceNdrVersion.NDR); } else if (transfer_syntax.if_uuid == RpceUtility.NDR64_INTERFACE_UUID && transfer_syntax.if_vers_major == RpceUtility.NDR64_INTERFACE_MAJOR_VERSION && transfer_syntax.if_vers_minor == RpceUtility.NDR64_INTERFACE_MINOR_VERSION) { this.NdrVersion |= RpceNdrVersion.NDR64; this.PresentationContextsTable.Add(p_cont_elem.p_cont_id, RpceNdrVersion.NDR64); } else { byte[] uuid = transfer_syntax.if_uuid.ToByteArray(); if (ArrayUtility.CompareArrays( ArrayUtility.SubArray( uuid, 0, RpceUtility.BIND_TIME_FEATURE_NEGOTIATION_BITMASK_PREFIX_LENGTH), ArrayUtility.SubArray( RpceUtility.BIND_TIME_FEATURE_NEGOTIATION_BITMASK_GUID_BYTES, 0, RpceUtility.BIND_TIME_FEATURE_NEGOTIATION_BITMASK_PREFIX_LENGTH)) && transfer_syntax.if_vers_major == 1 && transfer_syntax.if_vers_minor == 0) { this.BindTimeFeatureNegotiationBitmask = (RpceBindTimeFeatureNegotiationBitmask) uuid[RpceUtility.BIND_TIME_FEATURE_NEGOTIATION_BITMASK_PREFIX_LENGTH]; } } } } } }
public void BVT_ApplySnapshot() { BaseTestSite.Log.Add(LogEntryKind.TestStep, "1. Client opens a shared virtual disk file and expects success."); OpenSharedVHD(TestConfig.NameOfSharedVHDS, RSVD_PROTOCOL_VERSION.RSVD_PROTOCOL_VERSION_2); BaseTestSite.Log.Add(LogEntryKind.TestStep, "2. Client reads 512 bytes and saves it for later comparation."); byte[] byteBeforeChanged = null; uint status = client.Read(0, 512, out byteBeforeChanged); BaseTestSite.Assert.AreEqual( (uint)0, status, "Read Status should be {0}, actual is {1}", GetStatus(0), GetStatus(status)); Guid snapshotId = Guid.NewGuid(); BaseTestSite.Log.Add(LogEntryKind.TestStep, "3. Client creates a snapshot."); CreateSnapshot(snapshotId); BaseTestSite.Log.Add(LogEntryKind.TestStep, "4. Client closes the file."); client.CloseSharedVirtualDisk(); BaseTestSite.Log.Add(LogEntryKind.TestStep, "5. Client reopens the shared virtual disk file and expects success."); RequestIdentifier = 0; OpenSharedVHD(TestConfig.NameOfSharedVHDS, RSVD_PROTOCOL_VERSION.RSVD_PROTOCOL_VERSION_2); BaseTestSite.Log.Add(LogEntryKind.TestStep, "6. Client sends Write request to change the file and expects success."); byte[] payload = Smb2Utility.CreateRandomByteArray(512); status = client.Write(0, payload); BaseTestSite.Assert.AreEqual( (uint)0, status, "Write Status should be {0}, actual is {1}", GetStatus(0), GetStatus(status)); BaseTestSite.Log.Add(LogEntryKind.TestStep, "7. Client sends Apply Snapshot request to apply the previous snapshot."); SVHDX_APPLY_SNAPSHOT_PARAMS applySnapshot = new SVHDX_APPLY_SNAPSHOT_PARAMS(); applySnapshot.SnapshotID = snapshotId; applySnapshot.SnapshotType = Snapshot_Type.SvhdxSnapshotTypeVM; SVHDX_META_OPERATION_START_REQUEST startRequest = new SVHDX_META_OPERATION_START_REQUEST(); startRequest.TransactionId = Guid.NewGuid(); startRequest.OperationType = Operation_Type.SvhdxMetaOperationTypeApplySnapshot; payload = client.CreateTunnelMetaOperationStartApplySnapshotRequest(startRequest, applySnapshot); SVHDX_TUNNEL_OPERATION_HEADER?header; SVHDX_TUNNEL_OPERATION_HEADER?response; status = client.TunnelOperation <SVHDX_TUNNEL_OPERATION_HEADER>( true, //true for Async operation, false for non-async operation RSVD_TUNNEL_OPERATION_CODE.RSVD_TUNNEL_META_OPERATION_START, ++RequestIdentifier, payload, out header, out response); BaseTestSite.Assert.AreEqual( (uint)Smb2Status.STATUS_SUCCESS, status, "Ioctl should succeed, actual status: {0}", GetStatus(status)); VerifyTunnelOperationHeader(header.Value, RSVD_TUNNEL_OPERATION_CODE.RSVD_TUNNEL_META_OPERATION_START, (uint)RsvdStatus.STATUS_SVHDX_SUCCESS, RequestIdentifier); BaseTestSite.Log.Add(LogEntryKind.TestStep, "8. Client rereads 512 bytes and compares it with the previously saved bytes."); byte[] byteAfterApplied = null; status = client.Read(0, 512, out byteAfterApplied); BaseTestSite.Assert.AreEqual( (uint)0, status, "Read Status should be {0}, actual is {1}", GetStatus(0), GetStatus(status)); bool equal = ArrayUtility.CompareArrays(byteBeforeChanged, byteAfterApplied); BaseTestSite.Assert.AreEqual( true, equal, "The bytes after snapshot applied should be the same with the original."); BaseTestSite.Log.Add(LogEntryKind.TestStep, "9. Client deletes the snapshot."); DeleteSnapshot(snapshotId); BaseTestSite.Log.Add(LogEntryKind.TestStep, "10. Client closes the file."); client.CloseSharedVirtualDisk(); }
/// <summary> /// Verify Server checksum (TD section 2.8.1) and KDC checksum (TD section 2.8.2) /// of the PAC message. /// </summary> /// <param name="serverSignKey">The server signature key used to generate server signature.</param> /// <param name="kdcSignKey">The KDC signature key used to generate KDC signature.</param> /// <param name="isServerSignValid">true if server signature is valid.</param> /// <param name="isKdcSignValid">true if KDC signature is valid.</param> /// <exception cref="ArgumentNullException">serverSignkey or kdcSignKey is null.</exception> public void ValidateSign(byte[] serverSignKey, byte[] kdcSignKey, out bool isServerSignValid, out bool isKdcSignValid) { if (serverSignKey == null) { throw new ArgumentNullException("serverSignKey"); } if (kdcSignKey == null) { throw new ArgumentNullException("kdcSignKey"); } // locate server and KDC signatures. PacSignatureData serverSign; PacSignatureData kdcSign; FindSignatures(out serverSign, out kdcSign); // these validate operation will change signature values, so must backup and restore. byte[] oldServerSign = null; byte[] oldKdcSign = null; isServerSignValid = false; isKdcSignValid = false; try { // types PAC_SIGNATURE_DATA_SignatureType_Values serverSignType = serverSign.NativePacSignatureData.SignatureType; PAC_SIGNATURE_DATA_SignatureType_Values kdcSignType = kdcSign.NativePacSignatureData.SignatureType; // backup and clear signatures. int serverLength = PacSignatureData.CalculateSignatureLength(serverSignType); oldServerSign = serverSign.NativePacSignatureData.Signature; serverSign.NativePacSignatureData.Signature = new byte[serverLength]; int kdcLength = PacSignatureData.CalculateSignatureLength(kdcSignType); oldKdcSign = kdcSign.NativePacSignatureData.Signature; kdcSign.NativePacSignatureData.Signature = new byte[kdcLength]; // validate server sign byte[] serverSignResult = PacSignatureData.Sign( ToBytes(), serverSignType, serverSignKey); isServerSignValid = ArrayUtility.CompareArrays <byte>(serverSignResult, oldServerSign); // validate KDC sign byte[] kdcSignResult = PacSignatureData.Sign( serverSignResult, kdcSignType, kdcSignKey); isKdcSignValid = ArrayUtility.CompareArrays <byte>(kdcSignResult, oldKdcSign); } finally { // restore server signature if (oldServerSign != null) { serverSign.NativePacSignatureData.Signature = oldServerSign; } // restore KDC signature if (oldKdcSign != null) { kdcSign.NativePacSignatureData.Signature = oldKdcSign; } } }