1 // Transport Security Layer (TLS)
2 // Copyright (c) 2003-2004 Carlos Guzman Alvarez
3 // Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com)
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 using System.Collections;
28 using System.Threading;
30 using Mono.Security.Protocol.Tls.Handshake;
32 namespace Mono.Security.Protocol.Tls
34 internal abstract class RecordProtocol
38 private static ManualResetEvent record_processing = new ManualResetEvent (true);
40 protected Stream innerStream;
41 protected Context context;
47 public Context Context
49 get { return this.context; }
50 set { this.context = value; }
57 public RecordProtocol(Stream innerStream, Context context)
59 this.innerStream = innerStream;
60 this.context = context;
61 this.context.RecordProtocol = this;
66 #region Abstract Methods
68 public virtual void SendRecord(HandshakeType type)
71 IAsyncResult ar = this.BeginSendRecord(type, null, null);
73 this.EndSendRecord(ar);
77 protected abstract void ProcessHandshakeMessage(TlsStream handMsg);
79 protected virtual void ProcessChangeCipherSpec ()
81 Context ctx = this.Context;
83 // Reset sequence numbers
84 ctx.ReadSequenceNumber = 0;
86 if (ctx is ClientContext) {
87 ctx.EndSwitchingSecurityParameters (true);
89 ctx.StartSwitchingSecurityParameters (false);
93 public virtual HandshakeMessage GetMessage(HandshakeType type)
95 throw new NotSupportedException();
100 #region Receive Record Async Result
101 private class ReceiveRecordAsyncResult : IAsyncResult
103 private object locker = new object ();
104 private AsyncCallback _userCallback;
105 private object _userState;
106 private Exception _asyncException;
107 private ManualResetEvent handle;
108 private byte[] _resultingBuffer;
109 private Stream _record;
110 private bool completed;
112 private byte[] _initialBuffer;
114 public ReceiveRecordAsyncResult(AsyncCallback userCallback, object userState, byte[] initialBuffer, Stream record)
116 _userCallback = userCallback;
117 _userState = userState;
118 _initialBuffer = initialBuffer;
124 get { return _record; }
127 public byte[] ResultingBuffer
129 get { return _resultingBuffer; }
132 public byte[] InitialBuffer
134 get { return _initialBuffer; }
137 public object AsyncState
139 get { return _userState; }
142 public Exception AsyncException
144 get { return _asyncException; }
147 public bool CompletedWithError
151 return false; // Perhaps throw InvalidOperationExcetion?
153 return null != _asyncException;
157 public WaitHandle AsyncWaitHandle
162 handle = new ManualResetEvent (completed);
169 public bool CompletedSynchronously
171 get { return false; }
174 public bool IsCompleted
183 private void SetComplete(Exception ex, byte[] resultingBuffer)
190 _asyncException = ex;
191 _resultingBuffer = resultingBuffer;
195 if (_userCallback != null)
196 _userCallback.BeginInvoke (this, null, null);
200 public void SetComplete(Exception ex)
202 SetComplete(ex, null);
205 public void SetComplete(byte[] resultingBuffer)
207 SetComplete(null, resultingBuffer);
210 public void SetComplete()
212 SetComplete(null, null);
217 #region Receive Record Async Result
218 private class SendRecordAsyncResult : IAsyncResult
220 private object locker = new object ();
221 private AsyncCallback _userCallback;
222 private object _userState;
223 private Exception _asyncException;
224 private ManualResetEvent handle;
225 private HandshakeMessage _message;
226 private bool completed;
228 public SendRecordAsyncResult(AsyncCallback userCallback, object userState, HandshakeMessage message)
230 _userCallback = userCallback;
231 _userState = userState;
235 public HandshakeMessage Message
237 get { return _message; }
240 public object AsyncState
242 get { return _userState; }
245 public Exception AsyncException
247 get { return _asyncException; }
250 public bool CompletedWithError
254 return false; // Perhaps throw InvalidOperationExcetion?
256 return null != _asyncException;
260 public WaitHandle AsyncWaitHandle
265 handle = new ManualResetEvent (completed);
272 public bool CompletedSynchronously
274 get { return false; }
277 public bool IsCompleted
286 public void SetComplete(Exception ex)
296 if (_userCallback != null)
297 _userCallback.BeginInvoke (this, null, null);
299 _asyncException = ex;
303 public void SetComplete()
310 #region Reveive Record Methods
312 public IAsyncResult BeginReceiveRecord(Stream record, AsyncCallback callback, object state)
314 if (this.context.ReceivedConnectionEnd)
316 throw new TlsException(
317 AlertDescription.InternalError,
318 "The session is finished and it's no longer valid.");
321 record_processing.Reset ();
322 byte[] recordTypeBuffer = new byte[1];
324 ReceiveRecordAsyncResult internalResult = new ReceiveRecordAsyncResult(callback, state, recordTypeBuffer, record);
326 record.BeginRead(internalResult.InitialBuffer, 0, internalResult.InitialBuffer.Length, new AsyncCallback(InternalReceiveRecordCallback), internalResult);
328 return internalResult;
331 private void InternalReceiveRecordCallback(IAsyncResult asyncResult)
333 ReceiveRecordAsyncResult internalResult = asyncResult.AsyncState as ReceiveRecordAsyncResult;
334 Stream record = internalResult.Record;
339 int bytesRead = internalResult.Record.EndRead(asyncResult);
341 //We're at the end of the stream. Time to bail.
344 internalResult.SetComplete((byte[])null);
348 // Try to read the Record Content Type
349 int type = internalResult.InitialBuffer[0];
351 // Set last handshake message received to None
352 this.context.LastHandshakeMsg = HandshakeType.ClientHello;
354 ContentType contentType = (ContentType)type;
355 byte[] buffer = this.ReadRecordBuffer(type, record);
358 // record incomplete (at the moment)
359 internalResult.SetComplete((byte[])null);
363 // Decrypt message contents if needed
364 if (contentType == ContentType.Alert && buffer.Length == 2)
367 else if ((this.Context.Read != null) && (this.Context.Read.Cipher != null))
369 buffer = this.decryptRecordFragment (contentType, buffer);
370 DebugHelper.WriteLine ("Decrypted record data", buffer);
376 case ContentType.Alert:
377 this.ProcessAlert((AlertLevel)buffer [0], (AlertDescription)buffer [1]);
380 // don't reprocess that memory block
381 record.SetLength (0);
386 case ContentType.ChangeCipherSpec:
387 this.ProcessChangeCipherSpec();
390 case ContentType.ApplicationData:
393 case ContentType.Handshake:
394 TlsStream message = new TlsStream (buffer);
397 this.ProcessHandshakeMessage(message);
401 case (ContentType)0x80:
402 this.context.HandshakeMessages.Write (buffer);
406 throw new TlsException(
407 AlertDescription.UnexpectedMessage,
408 "Unknown record received from server.");
411 internalResult.SetComplete(buffer);
415 internalResult.SetComplete(ex);
420 public byte[] EndReceiveRecord(IAsyncResult asyncResult)
422 ReceiveRecordAsyncResult internalResult = asyncResult as ReceiveRecordAsyncResult;
424 if (null == internalResult)
425 throw new ArgumentException("Either the provided async result is null or was not created by this RecordProtocol.");
427 if (!internalResult.IsCompleted)
428 internalResult.AsyncWaitHandle.WaitOne();
430 if (internalResult.CompletedWithError)
431 throw internalResult.AsyncException;
433 byte[] result = internalResult.ResultingBuffer;
434 record_processing.Set ();
438 public byte[] ReceiveRecord(Stream record)
440 if (this.context.ReceivedConnectionEnd)
442 throw new TlsException(
443 AlertDescription.InternalError,
444 "The session is finished and it's no longer valid.");
447 record_processing.Reset ();
448 byte[] recordTypeBuffer = new byte[1];
450 int bytesRead = record.Read(recordTypeBuffer, 0, recordTypeBuffer.Length);
452 //We're at the end of the stream. Time to bail.
458 // Try to read the Record Content Type
459 int type = recordTypeBuffer[0];
461 // Set last handshake message received to None
462 this.context.LastHandshakeMsg = HandshakeType.ClientHello;
464 ContentType contentType = (ContentType)type;
465 byte[] buffer = this.ReadRecordBuffer(type, record);
468 // record incomplete (at the moment)
472 // Decrypt message contents if needed
473 if (contentType == ContentType.Alert && buffer.Length == 2)
476 else if ((this.Context.Read != null) && (this.Context.Read.Cipher != null))
478 buffer = this.decryptRecordFragment (contentType, buffer);
479 DebugHelper.WriteLine ("Decrypted record data", buffer);
485 case ContentType.Alert:
486 this.ProcessAlert((AlertLevel)buffer [0], (AlertDescription)buffer [1]);
489 // don't reprocess that memory block
490 record.SetLength (0);
495 case ContentType.ChangeCipherSpec:
496 this.ProcessChangeCipherSpec();
499 case ContentType.ApplicationData:
502 case ContentType.Handshake:
503 TlsStream message = new TlsStream (buffer);
506 this.ProcessHandshakeMessage(message);
510 case (ContentType)0x80:
511 this.context.HandshakeMessages.Write (buffer);
515 throw new TlsException(
516 AlertDescription.UnexpectedMessage,
517 "Unknown record received from server.");
520 record_processing.Set ();
524 private byte[] ReadRecordBuffer (int contentType, Stream record)
529 return this.ReadClientHelloV2(record);
532 if (!Enum.IsDefined(typeof(ContentType), (ContentType)contentType))
534 throw new TlsException(AlertDescription.DecodeError);
536 return this.ReadStandardRecordBuffer(record);
540 private byte[] ReadClientHelloV2 (Stream record)
542 int msgLength = record.ReadByte ();
543 // process further only if the whole record is available
544 if (record.CanSeek && (msgLength + 1 > record.Length))
549 byte[] message = new byte[msgLength];
550 record.Read (message, 0, msgLength);
552 int msgType = message [0];
555 throw new TlsException(AlertDescription.DecodeError);
557 int protocol = (message [1] << 8 | message [2]);
558 int cipherSpecLength = (message [3] << 8 | message [4]);
559 int sessionIdLength = (message [5] << 8 | message [6]);
560 int challengeLength = (message [7] << 8 | message [8]);
561 int length = (challengeLength > 32) ? 32 : challengeLength;
564 byte[] cipherSpecV2 = new byte[cipherSpecLength];
565 Buffer.BlockCopy (message, 9, cipherSpecV2, 0, cipherSpecLength);
568 byte[] sessionId = new byte[sessionIdLength];
569 Buffer.BlockCopy (message, 9 + cipherSpecLength, sessionId, 0, sessionIdLength);
572 byte[] challenge = new byte[challengeLength];
573 Buffer.BlockCopy (message, 9 + cipherSpecLength + sessionIdLength, challenge, 0, challengeLength);
575 if (challengeLength < 16 || cipherSpecLength == 0 || (cipherSpecLength % 3) != 0)
577 throw new TlsException(AlertDescription.DecodeError);
580 // Updated the Session ID
581 if (sessionId.Length > 0)
583 this.context.SessionId = sessionId;
586 // Update the protocol version
587 this.Context.ChangeProtocol((short)protocol);
589 // Select the Cipher suite
590 this.ProcessCipherSpecV2Buffer(this.Context.SecurityProtocol, cipherSpecV2);
592 // Updated the Client Random
593 this.context.ClientRandom = new byte [32]; // Always 32
594 // 1. if challenge is bigger than 32 bytes only use the last 32 bytes
595 // 2. right justify (0) challenge in ClientRandom if less than 32
596 Buffer.BlockCopy (challenge, challenge.Length - length, this.context.ClientRandom, 32 - length, length);
599 this.context.LastHandshakeMsg = HandshakeType.ClientHello;
600 this.context.ProtocolNegotiated = true;
605 private byte[] ReadStandardRecordBuffer (Stream record)
607 byte[] header = new byte[4];
608 if (record.Read (header, 0, 4) != 4)
609 throw new TlsException ("buffer underrun");
611 short protocol = (short)((header [0] << 8) | header [1]);
612 short length = (short)((header [2] << 8) | header [3]);
614 // process further only if the whole record is available
615 // note: the first 5 bytes aren't part of the length
616 if (record.CanSeek && (length + 5 > record.Length))
622 int totalReceived = 0;
623 byte[] buffer = new byte[length];
624 while (totalReceived != length)
626 int justReceived = record.Read(buffer, totalReceived, buffer.Length - totalReceived);
628 //Make sure we get some data so we don't end up in an infinite loop here before shutdown.
629 if (0 == justReceived)
631 throw new TlsException(AlertDescription.CloseNotify, "Received 0 bytes from stream. It must be closed.");
634 totalReceived += justReceived;
637 // Check that the message has a valid protocol version
638 if (protocol != this.context.Protocol && this.context.ProtocolNegotiated)
640 throw new TlsException(
641 AlertDescription.ProtocolVersion, "Invalid protocol version on message received");
644 DebugHelper.WriteLine("Record data", buffer);
649 private void ProcessAlert(AlertLevel alertLevel, AlertDescription alertDesc)
653 case AlertLevel.Fatal:
654 throw new TlsException(alertLevel, alertDesc);
656 case AlertLevel.Warning:
660 case AlertDescription.CloseNotify:
661 this.context.ReceivedConnectionEnd = true;
670 #region Send Alert Methods
672 internal void SendAlert(ref Exception ex)
674 var tlsEx = ex as TlsException;
675 var alert = tlsEx != null ? tlsEx.Alert : new Alert(AlertDescription.InternalError);
679 } catch (Exception alertEx) {
680 ex = new IOException (string.Format ("Error while sending TLS Alert ({0}:{1}): {2}", alert.Level, alert.Description, ex), ex);
684 public void SendAlert(AlertDescription description)
686 this.SendAlert(new Alert(description));
689 public void SendAlert(AlertLevel level, AlertDescription description)
691 this.SendAlert(new Alert(level, description));
694 public void SendAlert(Alert alert)
697 AlertDescription description;
701 DebugHelper.WriteLine(">>>> Write Alert NULL");
702 level = AlertLevel.Fatal;
703 description = AlertDescription.InternalError;
706 DebugHelper.WriteLine(">>>> Write Alert ({0}|{1})", alert.Description, alert.Message);
708 description = alert.Description;
709 close = alert.IsCloseNotify;
713 this.SendRecord (ContentType.Alert, new byte[2] { (byte) level, (byte) description });
716 this.context.SentConnectionEnd = true;
722 #region Send Record Methods
724 public void SendChangeCipherSpec()
726 DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
728 // Send Change Cipher Spec message with the current cipher
729 // or as plain text if this is the initial negotiation
730 this.SendRecord(ContentType.ChangeCipherSpec, new byte[] {1});
732 Context ctx = this.context;
734 // Reset sequence numbers
735 ctx.WriteSequenceNumber = 0;
737 // all further data sent will be encrypted with the negotiated
738 // security parameters (now the current parameters)
739 if (ctx is ClientContext) {
740 ctx.StartSwitchingSecurityParameters (true);
742 ctx.EndSwitchingSecurityParameters (false);
746 public void SendChangeCipherSpec(Stream recordStream)
748 DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
750 byte[] record = this.EncodeRecord (ContentType.ChangeCipherSpec, new byte[] { 1 });
752 // Send Change Cipher Spec message with the current cipher
753 // or as plain text if this is the initial negotiation
754 recordStream.Write(record, 0, record.Length);
756 Context ctx = this.context;
758 // Reset sequence numbers
759 ctx.WriteSequenceNumber = 0;
761 // all further data sent will be encrypted with the negotiated
762 // security parameters (now the current parameters)
763 if (ctx is ClientContext) {
764 ctx.StartSwitchingSecurityParameters (true);
766 ctx.EndSwitchingSecurityParameters (false);
770 public IAsyncResult BeginSendChangeCipherSpec(AsyncCallback callback, object state)
772 DebugHelper.WriteLine (">>>> Write Change Cipher Spec");
774 // Send Change Cipher Spec message with the current cipher
775 // or as plain text if this is the initial negotiation
776 return this.BeginSendRecord (ContentType.ChangeCipherSpec, new byte[] { 1 }, callback, state);
779 public void EndSendChangeCipherSpec (IAsyncResult asyncResult)
781 this.EndSendRecord (asyncResult);
783 Context ctx = this.context;
785 // Reset sequence numbers
786 ctx.WriteSequenceNumber = 0;
788 // all further data sent will be encrypted with the negotiated
789 // security parameters (now the current parameters)
790 if (ctx is ClientContext) {
791 ctx.StartSwitchingSecurityParameters (true);
793 ctx.EndSwitchingSecurityParameters (false);
797 public IAsyncResult BeginSendRecord(HandshakeType handshakeType, AsyncCallback callback, object state)
799 HandshakeMessage msg = this.GetMessage(handshakeType);
803 DebugHelper.WriteLine(">>>> Write handshake record ({0}|{1})", context.Protocol, msg.ContentType);
805 SendRecordAsyncResult internalResult = new SendRecordAsyncResult(callback, state, msg);
807 this.BeginSendRecord(msg.ContentType, msg.EncodeMessage(), new AsyncCallback(InternalSendRecordCallback), internalResult);
809 return internalResult;
812 private void InternalSendRecordCallback(IAsyncResult ar)
814 SendRecordAsyncResult internalResult = ar.AsyncState as SendRecordAsyncResult;
818 this.EndSendRecord(ar);
821 internalResult.Message.Update();
823 // Reset message contents
824 internalResult.Message.Reset();
826 internalResult.SetComplete();
830 internalResult.SetComplete(ex);
834 public IAsyncResult BeginSendRecord(ContentType contentType, byte[] recordData, AsyncCallback callback, object state)
836 if (this.context.SentConnectionEnd)
838 throw new TlsException(
839 AlertDescription.InternalError,
840 "The session is finished and it's no longer valid.");
843 byte[] record = this.EncodeRecord(contentType, recordData);
845 return this.innerStream.BeginWrite(record, 0, record.Length, callback, state);
848 public void EndSendRecord(IAsyncResult asyncResult)
850 if (asyncResult is SendRecordAsyncResult)
852 SendRecordAsyncResult internalResult = asyncResult as SendRecordAsyncResult;
853 if (!internalResult.IsCompleted)
854 internalResult.AsyncWaitHandle.WaitOne();
855 if (internalResult.CompletedWithError)
856 throw internalResult.AsyncException;
860 this.innerStream.EndWrite(asyncResult);
864 public void SendRecord(ContentType contentType, byte[] recordData)
866 IAsyncResult ar = this.BeginSendRecord(contentType, recordData, null, null);
868 this.EndSendRecord(ar);
871 public byte[] EncodeRecord(ContentType contentType, byte[] recordData)
873 return this.EncodeRecord(
880 public byte[] EncodeRecord(
881 ContentType contentType,
886 if (this.context.SentConnectionEnd)
888 throw new TlsException(
889 AlertDescription.InternalError,
890 "The session is finished and it's no longer valid.");
893 TlsStream record = new TlsStream();
895 int position = offset;
897 while (position < ( offset + count ))
899 short fragmentLength = 0;
902 if ((count + offset - position) > Context.MAX_FRAGMENT_SIZE)
904 fragmentLength = Context.MAX_FRAGMENT_SIZE;
908 fragmentLength = (short)(count + offset - position);
911 // Fill the fragment data
912 fragment = new byte[fragmentLength];
913 Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);
915 if ((this.Context.Write != null) && (this.Context.Write.Cipher != null))
918 fragment = this.encryptRecordFragment (contentType, fragment);
922 record.Write((byte)contentType);
923 record.Write(this.context.Protocol);
924 record.Write((short)fragment.Length);
925 record.Write(fragment);
927 DebugHelper.WriteLine("Record data", fragment);
929 // Update buffer position
930 position += fragmentLength;
933 return record.ToArray();
936 public byte[] EncodeHandshakeRecord(HandshakeType handshakeType)
938 HandshakeMessage msg = this.GetMessage(handshakeType);
942 var bytes = this.EncodeRecord (msg.ContentType, msg.EncodeMessage ());
953 #region Cryptography Methods
955 private byte[] encryptRecordFragment(
956 ContentType contentType,
961 // Calculate message MAC
962 if (this.Context is ClientContext)
964 mac = this.context.Write.Cipher.ComputeClientRecordMAC(contentType, fragment);
968 mac = this.context.Write.Cipher.ComputeServerRecordMAC (contentType, fragment);
971 DebugHelper.WriteLine(">>>> Record MAC", mac);
973 // Encrypt the message
974 byte[] ecr = this.context.Write.Cipher.EncryptRecord (fragment, mac);
976 // Update sequence number
977 this.context.WriteSequenceNumber++;
982 private byte[] decryptRecordFragment(
983 ContentType contentType,
986 byte[] dcrFragment = null;
987 byte[] dcrMAC = null;
991 this.context.Read.Cipher.DecryptRecord (fragment, out dcrFragment, out dcrMAC);
995 if (this.context is ServerContext)
997 this.Context.RecordProtocol.SendAlert(AlertDescription.DecryptionFailed);
1002 // Generate record MAC
1005 if (this.Context is ClientContext)
1007 mac = this.context.Read.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);
1011 mac = this.context.Read.Cipher.ComputeClientRecordMAC (contentType, dcrFragment);
1014 DebugHelper.WriteLine(">>>> Record MAC", mac);
1017 if (!Compare (mac, dcrMAC))
1019 throw new TlsException(AlertDescription.BadRecordMAC, "Bad record MAC");
1022 // Update sequence number
1023 this.context.ReadSequenceNumber++;
1028 private bool Compare (byte[] array1, byte[] array2)
1031 return (array2 == null);
1034 if (array1.Length != array2.Length)
1036 for (int i = 0; i < array1.Length; i++) {
1037 if (array1[i] != array2[i])
1045 #region CipherSpecV2 processing
1047 private void ProcessCipherSpecV2Buffer (SecurityProtocolType protocol, byte[] buffer)
1049 TlsStream codes = new TlsStream(buffer);
1051 string prefix = (protocol == SecurityProtocolType.Ssl3) ? "SSL_" : "TLS_";
1053 while (codes.Position < codes.Length)
1055 byte check = codes.ReadByte();
1059 // SSL/TLS cipher spec
1060 short code = codes.ReadInt16();
1061 int index = this.Context.SupportedCiphers.IndexOf(code);
1064 this.Context.Negotiating.Cipher = this.Context.SupportedCiphers[index];
1070 byte[] tmp = new byte[2];
1071 codes.Read(tmp, 0, tmp.Length);
1073 int tmpCode = ((check & 0xff) << 16) | ((tmp[0] & 0xff) << 8) | (tmp[1] & 0xff);
1074 CipherSuite cipher = this.MapV2CipherCode(prefix, tmpCode);
1078 this.Context.Negotiating.Cipher = cipher;
1084 if (this.Context.Negotiating == null)
1086 throw new TlsException(AlertDescription.InsuficientSecurity, "Insuficient Security");
1090 private CipherSuite MapV2CipherCode(string prefix, int code)
1097 // TLS_RC4_128_WITH_MD5
1098 return this.Context.SupportedCiphers[prefix + "RSA_WITH_RC4_128_MD5"];
1101 // TLS_RC4_128_EXPORT40_WITH_MD5
1102 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC4_40_MD5"];
1105 // TLS_RC2_CBC_128_CBC_WITH_MD5
1106 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
1109 // TLS_RC2_CBC_128_CBC_EXPORT40_WITH_MD5
1110 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
1113 // TLS_IDEA_128_CBC_WITH_MD5
1117 // TLS_DES_64_CBC_WITH_MD5
1121 // TLS_DES_192_EDE3_CBC_WITH_MD5