/// <summary> /// Signs the message using the context's session key. /// </summary> /// <remarks> /// The structure of the returned buffer is as follows: /// - 4 bytes, unsigned big-endian integer indicating the length of the plaintext message /// - 2 bytes, unsigned big-endian integer indicating the length of the signture /// - The plaintext message /// - The message's signature. /// </remarks> /// <param name="message"></param> /// <returns></returns> public byte[] MakeSignature(byte[] message) { CSecurityStatus status = CSecurityStatus.InternalError; CSecPkgContext_Sizes sizes; CSecureBuffer dataBuffer; CSecureBuffer signatureBuffer; CSecureBufferAdapter adapter; CheckLifecycle(); sizes = QueryBufferSizes(); dataBuffer = new CSecureBuffer(new byte[message.Length], CBufferType.Data); signatureBuffer = new CSecureBuffer(new byte[sizes.MaxSignature], CBufferType.Token); Array.Copy(message, dataBuffer.Buffer, message.Length); using (adapter = new CSecureBufferAdapter(new[] { dataBuffer, signatureBuffer })) { status = CContextNativeMethods.SafeMakeSignature( this.ContextHandle, 0, adapter, 0 ); } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to create message signature.", status); } byte[] outMessage; int position = 0; // Enough room for // - original message length (4 bytes) // - signature length (2 bytes) // - original message // - signature outMessage = new byte[4 + 2 + dataBuffer.Length + signatureBuffer.Length]; CByteWriter.WriteInt32_BE(dataBuffer.Length, outMessage, position); position += 4; CByteWriter.WriteInt16_BE((Int16)signatureBuffer.Length, outMessage, position); position += 2; Array.Copy(dataBuffer.Buffer, 0, outMessage, position, dataBuffer.Length); position += dataBuffer.Length; Array.Copy(signatureBuffer.Buffer, 0, outMessage, position, signatureBuffer.Length); position += signatureBuffer.Length; return(outMessage); }
/// <summary> /// Verifies the signature of a signed message /// </summary> /// <remarks> /// The expected structure of the signed message buffer is as follows: /// - 4 bytes, unsigned integer in big endian format indicating the length of the plaintext message /// - 2 bytes, unsigned integer in big endian format indicating the length of the signture /// - The plaintext message /// - The message's signature. /// </remarks> /// <param name="signedMessage">The packed signed message.</param> /// <param name="origMessage">The extracted original message.</param> /// <returns>True if the message has a valid signature, false otherwise.</returns> public bool VerifySignature(byte[] signedMessage, out byte[] origMessage) { CSecurityStatus status = CSecurityStatus.InternalError; CSecPkgContext_Sizes sizes; CSecureBuffer dataBuffer; CSecureBuffer signatureBuffer; CSecureBufferAdapter adapter; CheckLifecycle(); sizes = QueryBufferSizes(); if (signedMessage.Length < 2 + 4 + sizes.MaxSignature) { throw new ArgumentException("Input message is too small to possibly fit a valid message"); } int position = 0; int messageLen; int sigLen; messageLen = CByteWriter.ReadInt32_BE(signedMessage, 0); position += 4; sigLen = CByteWriter.ReadInt16_BE(signedMessage, position); position += 2; if (messageLen + sigLen + 2 + 4 > signedMessage.Length) { throw new ArgumentException("The buffer contains invalid data - the embedded length data does not add up."); } dataBuffer = new CSecureBuffer(new byte[messageLen], CBufferType.Data); Array.Copy(signedMessage, position, dataBuffer.Buffer, 0, messageLen); position += messageLen; signatureBuffer = new CSecureBuffer(new byte[sigLen], CBufferType.Token); Array.Copy(signedMessage, position, signatureBuffer.Buffer, 0, sigLen); position += sigLen; using (adapter = new CSecureBufferAdapter(new[] { dataBuffer, signatureBuffer })) { status = CContextNativeMethods.SafeVerifySignature( this.ContextHandle, 0, adapter, 0 ); } if (status == CSecurityStatus.OK) { origMessage = dataBuffer.Buffer; return(true); } else if (status == CSecurityStatus.MessageAltered || status == CSecurityStatus.OutOfSequence) { origMessage = null; return(false); } else { throw new CSSPIException("Failed to determine the veracity of a signed message.", status); } }
/// <summary> /// Decrypts a previously encrypted message. /// </summary> /// <remarks> /// The expected format of the buffer is as follows: /// - 2 bytes, an unsigned big-endian integer indicating the length of the trailer buffer size /// - 4 bytes, an unsigned big-endian integer indicating the length of the message buffer size. /// - 2 bytes, an unsigned big-endian integer indicating the length of the encryption padding buffer size. /// - The trailer buffer /// - The message buffer /// - The padding buffer. /// </remarks> /// <param name="input">The packed and encrypted data.</param> /// <returns>The original plaintext message.</returns> public byte[] Decrypt(byte[] input) { CSecPkgContext_Sizes sizes; CSecureBuffer trailerBuffer; CSecureBuffer dataBuffer; CSecureBuffer paddingBuffer; CSecureBufferAdapter adapter; CSecurityStatus status; byte[] result = null; int remaining; int position; int trailerLength; int dataLength; int paddingLength; CheckLifecycle(); sizes = QueryBufferSizes(); // This check is required, but not sufficient. We could be stricter. if (input.Length < 2 + 4 + 2 + sizes.SecurityTrailer) { throw new ArgumentException("Buffer is too small to possibly contain an encrypted message"); } position = 0; trailerLength = CByteWriter.ReadInt16_BE(input, position); position += 2; dataLength = CByteWriter.ReadInt32_BE(input, position); position += 4; paddingLength = CByteWriter.ReadInt16_BE(input, position); position += 2; if (trailerLength + dataLength + paddingLength + 2 + 4 + 2 > input.Length) { throw new ArgumentException("The buffer contains invalid data - the embedded length data does not add up."); } trailerBuffer = new CSecureBuffer(new byte[trailerLength], CBufferType.Token); dataBuffer = new CSecureBuffer(new byte[dataLength], CBufferType.Data); paddingBuffer = new CSecureBuffer(new byte[paddingLength], CBufferType.Padding); remaining = input.Length - position; if (trailerBuffer.Length <= remaining) { Array.Copy(input, position, trailerBuffer.Buffer, 0, trailerBuffer.Length); position += trailerBuffer.Length; remaining -= trailerBuffer.Length; } else { throw new ArgumentException("Input is missing data - it is not long enough to contain a fully encrypted message"); } if (dataBuffer.Length <= remaining) { Array.Copy(input, position, dataBuffer.Buffer, 0, dataBuffer.Length); position += dataBuffer.Length; remaining -= dataBuffer.Length; } else { throw new ArgumentException("Input is missing data - it is not long enough to contain a fully encrypted message"); } if (paddingBuffer.Length <= remaining) { Array.Copy(input, position, paddingBuffer.Buffer, 0, paddingBuffer.Length); } // else there was no padding. using (adapter = new CSecureBufferAdapter(new [] { trailerBuffer, dataBuffer, paddingBuffer })) { status = CContextNativeMethods.SafeDecryptMessage( this.ContextHandle, 0, adapter, 0 ); } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to encrypt message", status); } result = new byte[dataBuffer.Length]; Array.Copy(dataBuffer.Buffer, 0, result, 0, dataBuffer.Length); return(result); }
/// <summary> /// Encrypts the byte array using the context's session key. /// </summary> /// <remarks> /// The structure of the returned data is as follows: /// - 2 bytes, an unsigned big-endian integer indicating the length of the trailer buffer size /// - 4 bytes, an unsigned big-endian integer indicating the length of the message buffer size. /// - 2 bytes, an unsigned big-endian integer indicating the length of the encryption padding buffer size. /// - The trailer buffer /// - The message buffer /// - The padding buffer. /// </remarks> /// <param name="input">The raw message to encrypt.</param> /// <returns>The packed and encrypted message.</returns> public byte[] Encrypt(byte[] input) { // The message is encrypted in place in the buffer we provide to Win32 EncryptMessage CSecPkgContext_Sizes sizes; CSecureBuffer trailerBuffer; CSecureBuffer dataBuffer; CSecureBuffer paddingBuffer; CSecureBufferAdapter adapter; CSecurityStatus status = CSecurityStatus.InvalidHandle; byte[] result; CheckLifecycle(); sizes = QueryBufferSizes(); trailerBuffer = new CSecureBuffer(new byte[sizes.SecurityTrailer], CBufferType.Token); dataBuffer = new CSecureBuffer(new byte[input.Length], CBufferType.Data); paddingBuffer = new CSecureBuffer(new byte[sizes.BlockSize], CBufferType.Padding); Array.Copy(input, dataBuffer.Buffer, input.Length); using (adapter = new CSecureBufferAdapter(new[] { trailerBuffer, dataBuffer, paddingBuffer })) { status = CContextNativeMethods.SafeEncryptMessage( this.ContextHandle, 0, adapter, 0 ); } if (status != CSecurityStatus.OK) { throw new CSSPIException("Failed to encrypt message", status); } int position = 0; // Enough room to fit: // -- 2 bytes for the trailer buffer size // -- 4 bytes for the message size // -- 2 bytes for the padding size. // -- The encrypted message result = new byte[2 + 4 + 2 + trailerBuffer.Length + dataBuffer.Length + paddingBuffer.Length]; CByteWriter.WriteInt16_BE((short)trailerBuffer.Length, result, position); position += 2; CByteWriter.WriteInt32_BE(dataBuffer.Length, result, position); position += 4; CByteWriter.WriteInt16_BE((short)paddingBuffer.Length, result, position); position += 2; Array.Copy(trailerBuffer.Buffer, 0, result, position, trailerBuffer.Length); position += trailerBuffer.Length; Array.Copy(dataBuffer.Buffer, 0, result, position, dataBuffer.Length); position += dataBuffer.Length; Array.Copy(paddingBuffer.Buffer, 0, result, position, paddingBuffer.Length); position += paddingBuffer.Length; return(result); }