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);
92 ctx.ChangeCipherSpecDone = true;
95 public virtual HandshakeMessage GetMessage(HandshakeType type)
97 throw new NotSupportedException();
102 #region Receive Record Async Result
103 private class ReceiveRecordAsyncResult : IAsyncResult
105 private object locker = new object ();
106 private AsyncCallback _userCallback;
107 private object _userState;
108 private Exception _asyncException;
109 private ManualResetEvent handle;
110 private byte[] _resultingBuffer;
111 private Stream _record;
112 private bool completed;
114 private byte[] _initialBuffer;
116 public ReceiveRecordAsyncResult(AsyncCallback userCallback, object userState, byte[] initialBuffer, Stream record)
118 _userCallback = userCallback;
119 _userState = userState;
120 _initialBuffer = initialBuffer;
126 get { return _record; }
129 public byte[] ResultingBuffer
131 get { return _resultingBuffer; }
134 public byte[] InitialBuffer
136 get { return _initialBuffer; }
139 public object AsyncState
141 get { return _userState; }
144 public Exception AsyncException
146 get { return _asyncException; }
149 public bool CompletedWithError
153 return false; // Perhaps throw InvalidOperationExcetion?
155 return null != _asyncException;
159 public WaitHandle AsyncWaitHandle
164 handle = new ManualResetEvent (completed);
171 public bool CompletedSynchronously
173 get { return false; }
176 public bool IsCompleted
185 private void SetComplete(Exception ex, byte[] resultingBuffer)
192 _asyncException = ex;
193 _resultingBuffer = resultingBuffer;
197 if (_userCallback != null)
198 _userCallback.BeginInvoke (this, null, null);
202 public void SetComplete(Exception ex)
204 SetComplete(ex, null);
207 public void SetComplete(byte[] resultingBuffer)
209 SetComplete(null, resultingBuffer);
212 public void SetComplete()
214 SetComplete(null, null);
219 #region Receive Record Async Result
220 private class SendRecordAsyncResult : IAsyncResult
222 private object locker = new object ();
223 private AsyncCallback _userCallback;
224 private object _userState;
225 private Exception _asyncException;
226 private ManualResetEvent handle;
227 private HandshakeMessage _message;
228 private bool completed;
230 public SendRecordAsyncResult(AsyncCallback userCallback, object userState, HandshakeMessage message)
232 _userCallback = userCallback;
233 _userState = userState;
237 public HandshakeMessage Message
239 get { return _message; }
242 public object AsyncState
244 get { return _userState; }
247 public Exception AsyncException
249 get { return _asyncException; }
252 public bool CompletedWithError
256 return false; // Perhaps throw InvalidOperationExcetion?
258 return null != _asyncException;
262 public WaitHandle AsyncWaitHandle
267 handle = new ManualResetEvent (completed);
274 public bool CompletedSynchronously
276 get { return false; }
279 public bool IsCompleted
288 public void SetComplete(Exception ex)
298 if (_userCallback != null)
299 _userCallback.BeginInvoke (this, null, null);
301 _asyncException = ex;
305 public void SetComplete()
312 #region Reveive Record Methods
314 public IAsyncResult BeginReceiveRecord(Stream record, AsyncCallback callback, object state)
316 if (this.context.ReceivedConnectionEnd)
318 throw new TlsException(
319 AlertDescription.InternalError,
320 "The session is finished and it's no longer valid.");
323 record_processing.Reset ();
324 byte[] recordTypeBuffer = new byte[1];
326 ReceiveRecordAsyncResult internalResult = new ReceiveRecordAsyncResult(callback, state, recordTypeBuffer, record);
328 record.BeginRead(internalResult.InitialBuffer, 0, internalResult.InitialBuffer.Length, new AsyncCallback(InternalReceiveRecordCallback), internalResult);
330 return internalResult;
333 private void InternalReceiveRecordCallback(IAsyncResult asyncResult)
335 ReceiveRecordAsyncResult internalResult = asyncResult.AsyncState as ReceiveRecordAsyncResult;
336 Stream record = internalResult.Record;
341 int bytesRead = internalResult.Record.EndRead(asyncResult);
343 //We're at the end of the stream. Time to bail.
346 internalResult.SetComplete((byte[])null);
350 // Try to read the Record Content Type
351 int type = internalResult.InitialBuffer[0];
353 ContentType contentType = (ContentType)type;
354 byte[] buffer = this.ReadRecordBuffer(type, record);
357 // record incomplete (at the moment)
358 internalResult.SetComplete((byte[])null);
362 // Decrypt message contents if needed
363 if (contentType == ContentType.Alert && buffer.Length == 2)
366 else if ((this.Context.Read != null) && (this.Context.Read.Cipher != null))
368 buffer = this.decryptRecordFragment (contentType, buffer);
369 DebugHelper.WriteLine ("Decrypted record data", buffer);
375 case ContentType.Alert:
376 this.ProcessAlert((AlertLevel)buffer [0], (AlertDescription)buffer [1]);
379 // don't reprocess that memory block
380 record.SetLength (0);
385 case ContentType.ChangeCipherSpec:
386 this.ProcessChangeCipherSpec();
389 case ContentType.ApplicationData:
392 case ContentType.Handshake:
393 TlsStream message = new TlsStream (buffer);
396 this.ProcessHandshakeMessage(message);
400 case (ContentType)0x80:
401 this.context.HandshakeMessages.Write (buffer);
405 throw new TlsException(
406 AlertDescription.UnexpectedMessage,
407 "Unknown record received from server.");
410 internalResult.SetComplete(buffer);
414 internalResult.SetComplete(ex);
419 public byte[] EndReceiveRecord(IAsyncResult asyncResult)
421 ReceiveRecordAsyncResult internalResult = asyncResult as ReceiveRecordAsyncResult;
423 if (null == internalResult)
424 throw new ArgumentException("Either the provided async result is null or was not created by this RecordProtocol.");
426 if (!internalResult.IsCompleted)
427 internalResult.AsyncWaitHandle.WaitOne();
429 if (internalResult.CompletedWithError)
430 throw internalResult.AsyncException;
432 byte[] result = internalResult.ResultingBuffer;
433 record_processing.Set ();
437 public byte[] ReceiveRecord(Stream record)
439 if (this.context.ReceivedConnectionEnd)
441 throw new TlsException(
442 AlertDescription.InternalError,
443 "The session is finished and it's no longer valid.");
446 record_processing.Reset ();
447 byte[] recordTypeBuffer = new byte[1];
449 int bytesRead = record.Read(recordTypeBuffer, 0, recordTypeBuffer.Length);
451 //We're at the end of the stream. Time to bail.
457 // Try to read the Record Content Type
458 int type = recordTypeBuffer[0];
460 ContentType contentType = (ContentType)type;
461 byte[] buffer = this.ReadRecordBuffer(type, record);
464 // record incomplete (at the moment)
468 // Decrypt message contents if needed
469 if (contentType == ContentType.Alert && buffer.Length == 2)
472 else if ((this.Context.Read != null) && (this.Context.Read.Cipher != null))
474 buffer = this.decryptRecordFragment (contentType, buffer);
475 DebugHelper.WriteLine ("Decrypted record data", buffer);
481 case ContentType.Alert:
482 this.ProcessAlert((AlertLevel)buffer [0], (AlertDescription)buffer [1]);
485 // don't reprocess that memory block
486 record.SetLength (0);
491 case ContentType.ChangeCipherSpec:
492 this.ProcessChangeCipherSpec();
495 case ContentType.ApplicationData:
498 case ContentType.Handshake:
499 TlsStream message = new TlsStream (buffer);
502 this.ProcessHandshakeMessage(message);
506 case (ContentType)0x80:
507 this.context.HandshakeMessages.Write (buffer);
511 throw new TlsException(
512 AlertDescription.UnexpectedMessage,
513 "Unknown record received from server.");
516 record_processing.Set ();
520 private byte[] ReadRecordBuffer (int contentType, Stream record)
525 return this.ReadClientHelloV2(record);
528 if (!Enum.IsDefined(typeof(ContentType), (ContentType)contentType))
530 throw new TlsException(AlertDescription.DecodeError);
532 return this.ReadStandardRecordBuffer(record);
536 private byte[] ReadClientHelloV2 (Stream record)
538 int msgLength = record.ReadByte ();
539 // process further only if the whole record is available
540 if (record.CanSeek && (msgLength + 1 > record.Length))
545 byte[] message = new byte[msgLength];
546 record.Read (message, 0, msgLength);
548 int msgType = message [0];
551 throw new TlsException(AlertDescription.DecodeError);
553 int protocol = (message [1] << 8 | message [2]);
554 int cipherSpecLength = (message [3] << 8 | message [4]);
555 int sessionIdLength = (message [5] << 8 | message [6]);
556 int challengeLength = (message [7] << 8 | message [8]);
557 int length = (challengeLength > 32) ? 32 : challengeLength;
560 byte[] cipherSpecV2 = new byte[cipherSpecLength];
561 Buffer.BlockCopy (message, 9, cipherSpecV2, 0, cipherSpecLength);
564 byte[] sessionId = new byte[sessionIdLength];
565 Buffer.BlockCopy (message, 9 + cipherSpecLength, sessionId, 0, sessionIdLength);
568 byte[] challenge = new byte[challengeLength];
569 Buffer.BlockCopy (message, 9 + cipherSpecLength + sessionIdLength, challenge, 0, challengeLength);
571 if (challengeLength < 16 || cipherSpecLength == 0 || (cipherSpecLength % 3) != 0)
573 throw new TlsException(AlertDescription.DecodeError);
576 // Updated the Session ID
577 if (sessionId.Length > 0)
579 this.context.SessionId = sessionId;
582 // Update the protocol version
583 this.Context.ChangeProtocol((short)protocol);
585 // Select the Cipher suite
586 this.ProcessCipherSpecV2Buffer(this.Context.SecurityProtocol, cipherSpecV2);
588 // Updated the Client Random
589 this.context.ClientRandom = new byte [32]; // Always 32
590 // 1. if challenge is bigger than 32 bytes only use the last 32 bytes
591 // 2. right justify (0) challenge in ClientRandom if less than 32
592 Buffer.BlockCopy (challenge, challenge.Length - length, this.context.ClientRandom, 32 - length, length);
595 this.context.LastHandshakeMsg = HandshakeType.ClientHello;
596 this.context.ProtocolNegotiated = true;
601 private byte[] ReadStandardRecordBuffer (Stream record)
603 byte[] header = new byte[4];
604 if (record.Read (header, 0, 4) != 4)
605 throw new TlsException ("buffer underrun");
607 short protocol = (short)((header [0] << 8) | header [1]);
608 short length = (short)((header [2] << 8) | header [3]);
610 // process further only if the whole record is available
611 // note: the first 5 bytes aren't part of the length
612 if (record.CanSeek && (length + 5 > record.Length))
618 int totalReceived = 0;
619 byte[] buffer = new byte[length];
620 while (totalReceived != length)
622 int justReceived = record.Read(buffer, totalReceived, buffer.Length - totalReceived);
624 //Make sure we get some data so we don't end up in an infinite loop here before shutdown.
625 if (0 == justReceived)
627 throw new TlsException(AlertDescription.CloseNotify, "Received 0 bytes from stream. It must be closed.");
630 totalReceived += justReceived;
633 // Check that the message has a valid protocol version
634 if (protocol != this.context.Protocol && this.context.ProtocolNegotiated)
636 throw new TlsException(
637 AlertDescription.ProtocolVersion, "Invalid protocol version on message received");
640 DebugHelper.WriteLine("Record data", buffer);
645 private void ProcessAlert(AlertLevel alertLevel, AlertDescription alertDesc)
649 case AlertLevel.Fatal:
650 throw new TlsException(alertLevel, alertDesc);
652 case AlertLevel.Warning:
656 case AlertDescription.CloseNotify:
657 this.context.ReceivedConnectionEnd = true;
666 #region Send Alert Methods
668 internal void SendAlert(ref Exception ex)
670 var tlsEx = ex as TlsException;
671 var alert = tlsEx != null ? tlsEx.Alert : new Alert(AlertDescription.InternalError);
675 } catch (Exception alertEx) {
676 ex = new IOException (string.Format ("Error while sending TLS Alert ({0}:{1}): {2}", alert.Level, alert.Description, ex), ex);
680 public void SendAlert(AlertDescription description)
682 this.SendAlert(new Alert(description));
685 public void SendAlert(AlertLevel level, AlertDescription description)
687 this.SendAlert(new Alert(level, description));
690 public void SendAlert(Alert alert)
693 AlertDescription description;
697 DebugHelper.WriteLine(">>>> Write Alert NULL");
698 level = AlertLevel.Fatal;
699 description = AlertDescription.InternalError;
702 DebugHelper.WriteLine(">>>> Write Alert ({0}|{1})", alert.Description, alert.Message);
704 description = alert.Description;
705 close = alert.IsCloseNotify;
709 this.SendRecord (ContentType.Alert, new byte[2] { (byte) level, (byte) description });
712 this.context.SentConnectionEnd = true;
718 #region Send Record Methods
720 public void SendChangeCipherSpec()
722 DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
724 // Send Change Cipher Spec message with the current cipher
725 // or as plain text if this is the initial negotiation
726 this.SendRecord(ContentType.ChangeCipherSpec, new byte[] {1});
728 Context ctx = this.context;
730 // Reset sequence numbers
731 ctx.WriteSequenceNumber = 0;
733 // all further data sent will be encrypted with the negotiated
734 // security parameters (now the current parameters)
735 if (ctx is ClientContext) {
736 ctx.StartSwitchingSecurityParameters (true);
738 ctx.EndSwitchingSecurityParameters (false);
742 public void SendChangeCipherSpec(Stream recordStream)
744 DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
746 byte[] record = this.EncodeRecord (ContentType.ChangeCipherSpec, new byte[] { 1 });
748 // Send Change Cipher Spec message with the current cipher
749 // or as plain text if this is the initial negotiation
750 recordStream.Write(record, 0, record.Length);
752 Context ctx = this.context;
754 // Reset sequence numbers
755 ctx.WriteSequenceNumber = 0;
757 // all further data sent will be encrypted with the negotiated
758 // security parameters (now the current parameters)
759 if (ctx is ClientContext) {
760 ctx.StartSwitchingSecurityParameters (true);
762 ctx.EndSwitchingSecurityParameters (false);
766 public IAsyncResult BeginSendChangeCipherSpec(AsyncCallback callback, object state)
768 DebugHelper.WriteLine (">>>> Write Change Cipher Spec");
770 // Send Change Cipher Spec message with the current cipher
771 // or as plain text if this is the initial negotiation
772 return this.BeginSendRecord (ContentType.ChangeCipherSpec, new byte[] { 1 }, callback, state);
775 public void EndSendChangeCipherSpec (IAsyncResult asyncResult)
777 this.EndSendRecord (asyncResult);
779 Context ctx = this.context;
781 // Reset sequence numbers
782 ctx.WriteSequenceNumber = 0;
784 // all further data sent will be encrypted with the negotiated
785 // security parameters (now the current parameters)
786 if (ctx is ClientContext) {
787 ctx.StartSwitchingSecurityParameters (true);
789 ctx.EndSwitchingSecurityParameters (false);
793 public IAsyncResult BeginSendRecord(HandshakeType handshakeType, AsyncCallback callback, object state)
795 HandshakeMessage msg = this.GetMessage(handshakeType);
799 DebugHelper.WriteLine(">>>> Write handshake record ({0}|{1})", context.Protocol, msg.ContentType);
801 SendRecordAsyncResult internalResult = new SendRecordAsyncResult(callback, state, msg);
803 this.BeginSendRecord(msg.ContentType, msg.EncodeMessage(), new AsyncCallback(InternalSendRecordCallback), internalResult);
805 return internalResult;
808 private void InternalSendRecordCallback(IAsyncResult ar)
810 SendRecordAsyncResult internalResult = ar.AsyncState as SendRecordAsyncResult;
814 this.EndSendRecord(ar);
817 internalResult.Message.Update();
819 // Reset message contents
820 internalResult.Message.Reset();
822 internalResult.SetComplete();
826 internalResult.SetComplete(ex);
830 public IAsyncResult BeginSendRecord(ContentType contentType, byte[] recordData, AsyncCallback callback, object state)
832 if (this.context.SentConnectionEnd)
834 throw new TlsException(
835 AlertDescription.InternalError,
836 "The session is finished and it's no longer valid.");
839 byte[] record = this.EncodeRecord(contentType, recordData);
841 return this.innerStream.BeginWrite(record, 0, record.Length, callback, state);
844 public void EndSendRecord(IAsyncResult asyncResult)
846 if (asyncResult is SendRecordAsyncResult)
848 SendRecordAsyncResult internalResult = asyncResult as SendRecordAsyncResult;
849 if (!internalResult.IsCompleted)
850 internalResult.AsyncWaitHandle.WaitOne();
851 if (internalResult.CompletedWithError)
852 throw internalResult.AsyncException;
856 this.innerStream.EndWrite(asyncResult);
860 public void SendRecord(ContentType contentType, byte[] recordData)
862 IAsyncResult ar = this.BeginSendRecord(contentType, recordData, null, null);
864 this.EndSendRecord(ar);
867 public byte[] EncodeRecord(ContentType contentType, byte[] recordData)
869 return this.EncodeRecord(
876 public byte[] EncodeRecord(
877 ContentType contentType,
882 if (this.context.SentConnectionEnd)
884 throw new TlsException(
885 AlertDescription.InternalError,
886 "The session is finished and it's no longer valid.");
889 TlsStream record = new TlsStream();
891 int position = offset;
893 while (position < ( offset + count ))
895 short fragmentLength = 0;
898 if ((count + offset - position) > Context.MAX_FRAGMENT_SIZE)
900 fragmentLength = Context.MAX_FRAGMENT_SIZE;
904 fragmentLength = (short)(count + offset - position);
907 // Fill the fragment data
908 fragment = new byte[fragmentLength];
909 Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);
911 if ((this.Context.Write != null) && (this.Context.Write.Cipher != null))
914 fragment = this.encryptRecordFragment (contentType, fragment);
918 record.Write((byte)contentType);
919 record.Write(this.context.Protocol);
920 record.Write((short)fragment.Length);
921 record.Write(fragment);
923 DebugHelper.WriteLine("Record data", fragment);
925 // Update buffer position
926 position += fragmentLength;
929 return record.ToArray();
932 public byte[] EncodeHandshakeRecord(HandshakeType handshakeType)
934 HandshakeMessage msg = this.GetMessage(handshakeType);
938 var bytes = this.EncodeRecord (msg.ContentType, msg.EncodeMessage ());
949 #region Cryptography Methods
951 private byte[] encryptRecordFragment(
952 ContentType contentType,
957 // Calculate message MAC
958 if (this.Context is ClientContext)
960 mac = this.context.Write.Cipher.ComputeClientRecordMAC(contentType, fragment);
964 mac = this.context.Write.Cipher.ComputeServerRecordMAC (contentType, fragment);
967 DebugHelper.WriteLine(">>>> Record MAC", mac);
969 // Encrypt the message
970 byte[] ecr = this.context.Write.Cipher.EncryptRecord (fragment, mac);
972 // Update sequence number
973 this.context.WriteSequenceNumber++;
978 private byte[] decryptRecordFragment(
979 ContentType contentType,
982 byte[] dcrFragment = null;
983 byte[] dcrMAC = null;
987 this.context.Read.Cipher.DecryptRecord (fragment, out dcrFragment, out dcrMAC);
991 if (this.context is ServerContext)
993 this.Context.RecordProtocol.SendAlert(AlertDescription.DecryptionFailed);
998 // Generate record MAC
1001 if (this.Context is ClientContext)
1003 mac = this.context.Read.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);
1007 mac = this.context.Read.Cipher.ComputeClientRecordMAC (contentType, dcrFragment);
1010 DebugHelper.WriteLine(">>>> Record MAC", mac);
1013 if (!Compare (mac, dcrMAC))
1015 throw new TlsException(AlertDescription.BadRecordMAC, "Bad record MAC");
1018 // Update sequence number
1019 this.context.ReadSequenceNumber++;
1024 private bool Compare (byte[] array1, byte[] array2)
1027 return (array2 == null);
1030 if (array1.Length != array2.Length)
1032 for (int i = 0; i < array1.Length; i++) {
1033 if (array1[i] != array2[i])
1041 #region CipherSpecV2 processing
1043 private void ProcessCipherSpecV2Buffer (SecurityProtocolType protocol, byte[] buffer)
1045 TlsStream codes = new TlsStream(buffer);
1047 string prefix = (protocol == SecurityProtocolType.Ssl3) ? "SSL_" : "TLS_";
1049 while (codes.Position < codes.Length)
1051 byte check = codes.ReadByte();
1055 // SSL/TLS cipher spec
1056 short code = codes.ReadInt16();
1057 int index = this.Context.SupportedCiphers.IndexOf(code);
1060 this.Context.Negotiating.Cipher = this.Context.SupportedCiphers[index];
1066 byte[] tmp = new byte[2];
1067 codes.Read(tmp, 0, tmp.Length);
1069 int tmpCode = ((check & 0xff) << 16) | ((tmp[0] & 0xff) << 8) | (tmp[1] & 0xff);
1070 CipherSuite cipher = this.MapV2CipherCode(prefix, tmpCode);
1074 this.Context.Negotiating.Cipher = cipher;
1080 if (this.Context.Negotiating == null)
1082 throw new TlsException(AlertDescription.InsuficientSecurity, "Insuficient Security");
1086 private CipherSuite MapV2CipherCode(string prefix, int code)
1093 // TLS_RC4_128_WITH_MD5
1094 return this.Context.SupportedCiphers[prefix + "RSA_WITH_RC4_128_MD5"];
1097 // TLS_RC4_128_EXPORT40_WITH_MD5
1098 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC4_40_MD5"];
1101 // TLS_RC2_CBC_128_CBC_WITH_MD5
1102 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
1105 // TLS_RC2_CBC_128_CBC_EXPORT40_WITH_MD5
1106 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
1109 // TLS_IDEA_128_CBC_WITH_MD5
1113 // TLS_DES_64_CBC_WITH_MD5
1117 // TLS_DES_192_EDE3_CBC_WITH_MD5