/// <devdoc> /// <para> /// Performs encryption of an array of buffers, /// proceeds buffer by buffer, if the individual /// buffer size exceeds a SSL limit of 64K, /// the buffers are then split into smaller ones. /// Returns a new array of encrypted buffers. /// </para> /// </devdoc> private BufferOffsetSize [] EncryptBuffers(BufferOffsetSize[] buffers) { // do we need lock this write as well? ArrayList encryptedBufferList = new ArrayList(buffers.Length); for (int i = 0; i < buffers.Length; i++) { SecureChannel chkSecureChannel = SecureChannel; if (chkSecureChannel == null) { InnerException = new IOException(SR.GetString(SR.net_io_writefailure)); throw InnerException; } int offset = buffers[i].Offset; int totalLeft = buffers[i].Size; do { byte [] tempOutput = null; int size = Math.Min(totalLeft, chkSecureChannel.MaxDataSize); totalLeft -= size; int errorCode = chkSecureChannel.Encrypt(buffers[i].Buffer, offset, size, ref tempOutput); if (errorCode != (int)SecurityStatus.OK) { ProtocolToken message = new ProtocolToken(null, errorCode); InnerException = message.GetException(); throw InnerException; } encryptedBufferList.Add(new BufferOffsetSize(tempOutput, false)); offset += size; } while (totalLeft > 0); } return((BufferOffsetSize [])encryptedBufferList.ToArray(typeof(BufferOffsetSize))); }
// // Handshake - the Handshake is perhaps the most important part of the SSL process, // this is a Handshake protocol between server & client, where we send a // a HELLO message / server responds, we respond back, and after a few round trips, // we have an SSL connection with the server. But this process may be repeated, // if a higher level of security is required for by the server, therefore, // this function may be called several times in the life of the connection. // // returns an Exception on error, containing the error code of the failure // private Exception Handshake(ProtocolToken message) { // // With some SSPI APIs, the SSPI wrapper may throw // uncaught Win32Exceptions, so we need to add // this try - catch here. // try { int round = 0; byte[] incoming = null; // will be null == message on connection creation/otherwise should be // renegotation if (message == null) { GlobalLog.Assert( (SecureChannel == null), "had assembed a null SecureChannel at this point", "SecureChannel != null"); m_SecureChannel = new SecureChannel(m_DestinationHost, m_ClientCertificates); } else { incoming = message.Payload; } do { GlobalLog.Print("Handshake::Round #" + round); // // this code runs in the constructor, hence there's no // way SecureChannel can become null // message = SecureChannel.NextMessage(incoming); #if TRAVE GlobalLog.Print("Handshake::generating TLS message(Status:" + SecureChannel.MapSecurityStatus((uint)message.Status) + " Done:" + message.Done.ToString() + ")"); #endif if (message.Failed) { break; } if (message.Payload != null) { GlobalLog.Print("Handshake::Outgoing message size: " + message.Payload.Length); GlobalLog.Dump(message.Payload); base.Write(message.Payload, 0, message.Payload.Length); } else { GlobalLog.Print("Handshake::No message necessary."); } if (message.Done) { break; } // // ReadFullRecord attempts to parse read data // from the byte stream, this can be dangerous as its not // always sure about protocols, at this point // incoming = ReadFullRecord(null, 0); if (incoming == null) { // // Handshake failed // GlobalLog.Print("Handshake::ReadFullRecord is null, Handshake failed"); GlobalLog.Assert( (!message.Done), "attempted bad return / must always fail", "message.Done"); return(message.GetException()); } GlobalLog.Print("Handshake::Incoming message size: " + incoming.Length); round++; } while (!message.Done); if (message.Done) { SecureChannel.ProcessHandshakeSuccess(); GlobalLog.Print("Handshake::Handshake completed successfully."); } else { // SEC_I_CONTINUE_NEEDED #if TRAVE GlobalLog.Print("Handshake::FAILED Handshake, last error: " + SecureChannel.MapSecurityStatus((uint)message.Status)); #endif } return(message.GetException()); } catch (Exception exception) { return(exception); } }
private IAsyncResult InnerWrite(bool async, byte[] buffer, int offset, int size, AsyncCallback asyncCallback, object asyncState) { // after shutdown/Close throw an exception if (m_ShutDown > 0) { throw new ObjectDisposedException(this.GetType().FullName); } // on earlier error throw an exception if (InnerException != null) { throw InnerException; } // // parameter validation // if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0 || offset > buffer.Length) { throw new ArgumentOutOfRangeException("offset"); } if (size < 0 || size > buffer.Length - offset) { throw new ArgumentOutOfRangeException("size"); } // // Lock the Write: this is inefficent, but we need to prevent // writing data while the Stream is doing a handshake with the server. // writing other data during the handshake would cause the server to // fail and close the connection. // lock (this) { // // encrypt the data // byte[] ciphertext = null; GlobalLog.Print("Encrypt[" + Encoding.ASCII.GetString(buffer, 0, Math.Min(buffer.Length, 512)) + "]"); SecureChannel chkSecureChannel = SecureChannel; if (chkSecureChannel == null) { InnerException = new IOException(SR.GetString(SR.net_io_writefailure)); throw InnerException; } if (size > chkSecureChannel.MaxDataSize) { BufferOffsetSize [] buffers = new BufferOffsetSize[1]; buffers[0] = new BufferOffsetSize(buffer, offset, size, false); if (async) { return(BeginMultipleWrite(buffers, asyncCallback, asyncState)); } else { MultipleWrite(buffers); return(null); } } int errorCode = chkSecureChannel.Encrypt(buffer, offset, size, ref ciphertext); if (errorCode != (int)SecurityStatus.OK) { ProtocolToken message = new ProtocolToken(null, errorCode); InnerException = message.GetException(); throw InnerException; } try { if (async) { IAsyncResult asyncResult = base.BeginWrite( ciphertext, 0, ciphertext.Length, asyncCallback, asyncState); return(asyncResult); } else { base.Write(ciphertext, 0, ciphertext.Length); return(null); } } catch (Exception exception) { // // some sort of error occured Writing to the Trasport, // set the Exception as InnerException and throw // InnerException = new IOException(SR.GetString(SR.net_io_writefailure), exception); throw InnerException; } } }
// // NextRecord - called typically in Callback // to indicate that we need more bytes from the wire // to be decrypted. It is called either by a worker // thread or by the Read directly, it reads one chunk // of data, and attempts to decrypt. As soon as it has // the chunk of unencrypted data, it returns it in // m_ArrivingData and m_ExistingAmount contains, // the amount data that was decrypted. // // ASSUMES: we have an empty buffer of unencrypted bytes // RETURNS: upon error, by either leaving this buffer empty (0), // with an Exception set on this object, or on success, // by updating the global state (m_ArrivingData) // with unencrypted bytes // // WARNING: Can Throw! // private void NextRecord(byte[] buffer, int length) { byte[] packet = null; GlobalLog.Assert( (m_ExistingAmount == 0), "m_ExistingAmount != 0", "Has assumed internal SSL buffer would be empty"); // // This LOOP below will keep going until (EITHER): // 1) we have ONE succesful chunk of unencrypted data // 2) we have an error either from a renegotiate handhake (OR) Read (OR) Decrypt // do { packet = ReadFullRecord(buffer, length); if (packet == null) { return; } lock (this) { SecureChannel chkSecureChannel = SecureChannel; if (chkSecureChannel == null) { return; } int errorCode = chkSecureChannel.Decrypt(packet, ref m_ArrivingData); if (errorCode == (int)SecurityStatus.OK) { // SUCCESS - we have our decrypted Bytes GlobalLog.Print("TlsStream::NextRecord called (success) Decrypt[" + (m_ArrivingData != null ? (Encoding.ASCII.GetString(m_ArrivingData, 0, Math.Min(m_ArrivingData.Length, 512))) : "null") + "]"); break; } else { // ERROR - examine what kind ProtocolToken message = new ProtocolToken(packet, errorCode); GlobalLog.Print("TlsStream:: Decrypt errorCode = " + errorCode.ToString()); if (message.Renegotiate) { // HANDSHAKE - do a handshake between us and server InnerException = Handshake(message); if (InnerException != null) { return; // failure } // CONTINUE - Read On! we pick up from where // we were before the handshake and try to get // one block of unencrypted bytes, the earlier block // of data was control information for the handshake. // We need to read in the new header. if (ForceRead(buffer, 0, length) < length) { InnerException = new IOException(SR.GetString(SR.net_io_readfailure)); return; //failure } } else if (message.CloseConnection) { // CLOSE - server ordered us to shut down Close(); // close down the socket return; } else { // EXCEPTION - throw later on InnerException = message.GetException(); return; } } } // continue here in the case where we had a handshake, and needed // to reget new Data } while (true); // m_ExistingAmount was 0 on entry! if (m_ArrivingData == null) { return; } m_ExistingAmount = m_ArrivingData.Length; return; }