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 public void SendAlert(AlertDescription description)
674 this.SendAlert(new Alert(description));
677 public void SendAlert(
679 AlertDescription description)
681 this.SendAlert(new Alert(level, description));
684 public void SendAlert(Alert alert)
687 AlertDescription description;
691 DebugHelper.WriteLine(">>>> Write Alert NULL");
692 level = AlertLevel.Fatal;
693 description = AlertDescription.InternalError;
696 DebugHelper.WriteLine(">>>> Write Alert ({0}|{1})", alert.Description, alert.Message);
698 description = alert.Description;
699 close = alert.IsCloseNotify;
703 this.SendRecord (ContentType.Alert, new byte[2] { (byte) level, (byte) description });
706 this.context.SentConnectionEnd = true;
712 #region Send Record Methods
714 public void SendChangeCipherSpec()
716 DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
718 // Send Change Cipher Spec message with the current cipher
719 // or as plain text if this is the initial negotiation
720 this.SendRecord(ContentType.ChangeCipherSpec, new byte[] {1});
722 Context ctx = this.context;
724 // Reset sequence numbers
725 ctx.WriteSequenceNumber = 0;
727 // all further data sent will be encrypted with the negotiated
728 // security parameters (now the current parameters)
729 if (ctx is ClientContext) {
730 ctx.StartSwitchingSecurityParameters (true);
732 ctx.EndSwitchingSecurityParameters (false);
736 public void SendChangeCipherSpec(Stream recordStream)
738 DebugHelper.WriteLine(">>>> Write Change Cipher Spec");
740 byte[] record = this.EncodeRecord (ContentType.ChangeCipherSpec, new byte[] { 1 });
742 // Send Change Cipher Spec message with the current cipher
743 // or as plain text if this is the initial negotiation
744 recordStream.Write(record, 0, record.Length);
746 Context ctx = this.context;
748 // Reset sequence numbers
749 ctx.WriteSequenceNumber = 0;
751 // all further data sent will be encrypted with the negotiated
752 // security parameters (now the current parameters)
753 if (ctx is ClientContext) {
754 ctx.StartSwitchingSecurityParameters (true);
756 ctx.EndSwitchingSecurityParameters (false);
760 public IAsyncResult BeginSendChangeCipherSpec(AsyncCallback callback, object state)
762 DebugHelper.WriteLine (">>>> Write Change Cipher Spec");
764 // Send Change Cipher Spec message with the current cipher
765 // or as plain text if this is the initial negotiation
766 return this.BeginSendRecord (ContentType.ChangeCipherSpec, new byte[] { 1 }, callback, state);
769 public void EndSendChangeCipherSpec (IAsyncResult asyncResult)
771 this.EndSendRecord (asyncResult);
773 Context ctx = this.context;
775 // Reset sequence numbers
776 ctx.WriteSequenceNumber = 0;
778 // all further data sent will be encrypted with the negotiated
779 // security parameters (now the current parameters)
780 if (ctx is ClientContext) {
781 ctx.StartSwitchingSecurityParameters (true);
783 ctx.EndSwitchingSecurityParameters (false);
787 public IAsyncResult BeginSendRecord(HandshakeType handshakeType, AsyncCallback callback, object state)
789 HandshakeMessage msg = this.GetMessage(handshakeType);
793 DebugHelper.WriteLine(">>>> Write handshake record ({0}|{1})", context.Protocol, msg.ContentType);
795 SendRecordAsyncResult internalResult = new SendRecordAsyncResult(callback, state, msg);
797 this.BeginSendRecord(msg.ContentType, msg.EncodeMessage(), new AsyncCallback(InternalSendRecordCallback), internalResult);
799 return internalResult;
802 private void InternalSendRecordCallback(IAsyncResult ar)
804 SendRecordAsyncResult internalResult = ar.AsyncState as SendRecordAsyncResult;
808 this.EndSendRecord(ar);
811 internalResult.Message.Update();
813 // Reset message contents
814 internalResult.Message.Reset();
816 internalResult.SetComplete();
820 internalResult.SetComplete(ex);
824 public IAsyncResult BeginSendRecord(ContentType contentType, byte[] recordData, AsyncCallback callback, object state)
826 if (this.context.SentConnectionEnd)
828 throw new TlsException(
829 AlertDescription.InternalError,
830 "The session is finished and it's no longer valid.");
833 byte[] record = this.EncodeRecord(contentType, recordData);
835 return this.innerStream.BeginWrite(record, 0, record.Length, callback, state);
838 public void EndSendRecord(IAsyncResult asyncResult)
840 if (asyncResult is SendRecordAsyncResult)
842 SendRecordAsyncResult internalResult = asyncResult as SendRecordAsyncResult;
843 if (!internalResult.IsCompleted)
844 internalResult.AsyncWaitHandle.WaitOne();
845 if (internalResult.CompletedWithError)
846 throw internalResult.AsyncException;
850 this.innerStream.EndWrite(asyncResult);
854 public void SendRecord(ContentType contentType, byte[] recordData)
856 IAsyncResult ar = this.BeginSendRecord(contentType, recordData, null, null);
858 this.EndSendRecord(ar);
861 public byte[] EncodeRecord(ContentType contentType, byte[] recordData)
863 return this.EncodeRecord(
870 public byte[] EncodeRecord(
871 ContentType contentType,
876 if (this.context.SentConnectionEnd)
878 throw new TlsException(
879 AlertDescription.InternalError,
880 "The session is finished and it's no longer valid.");
883 TlsStream record = new TlsStream();
885 int position = offset;
887 while (position < ( offset + count ))
889 short fragmentLength = 0;
892 if ((count + offset - position) > Context.MAX_FRAGMENT_SIZE)
894 fragmentLength = Context.MAX_FRAGMENT_SIZE;
898 fragmentLength = (short)(count + offset - position);
901 // Fill the fragment data
902 fragment = new byte[fragmentLength];
903 Buffer.BlockCopy(recordData, position, fragment, 0, fragmentLength);
905 if ((this.Context.Write != null) && (this.Context.Write.Cipher != null))
908 fragment = this.encryptRecordFragment (contentType, fragment);
912 record.Write((byte)contentType);
913 record.Write(this.context.Protocol);
914 record.Write((short)fragment.Length);
915 record.Write(fragment);
917 DebugHelper.WriteLine("Record data", fragment);
919 // Update buffer position
920 position += fragmentLength;
923 return record.ToArray();
926 public byte[] EncodeHandshakeRecord(HandshakeType handshakeType)
928 HandshakeMessage msg = this.GetMessage(handshakeType);
932 var bytes = this.EncodeRecord (msg.ContentType, msg.EncodeMessage ());
943 #region Cryptography Methods
945 private byte[] encryptRecordFragment(
946 ContentType contentType,
951 // Calculate message MAC
952 if (this.Context is ClientContext)
954 mac = this.context.Write.Cipher.ComputeClientRecordMAC(contentType, fragment);
958 mac = this.context.Write.Cipher.ComputeServerRecordMAC (contentType, fragment);
961 DebugHelper.WriteLine(">>>> Record MAC", mac);
963 // Encrypt the message
964 byte[] ecr = this.context.Write.Cipher.EncryptRecord (fragment, mac);
966 // Update sequence number
967 this.context.WriteSequenceNumber++;
972 private byte[] decryptRecordFragment(
973 ContentType contentType,
976 byte[] dcrFragment = null;
977 byte[] dcrMAC = null;
981 this.context.Read.Cipher.DecryptRecord (fragment, out dcrFragment, out dcrMAC);
985 if (this.context is ServerContext)
987 this.Context.RecordProtocol.SendAlert(AlertDescription.DecryptionFailed);
992 // Generate record MAC
995 if (this.Context is ClientContext)
997 mac = this.context.Read.Cipher.ComputeServerRecordMAC(contentType, dcrFragment);
1001 mac = this.context.Read.Cipher.ComputeClientRecordMAC (contentType, dcrFragment);
1004 DebugHelper.WriteLine(">>>> Record MAC", mac);
1007 if (!Compare (mac, dcrMAC))
1009 throw new TlsException(AlertDescription.BadRecordMAC, "Bad record MAC");
1012 // Update sequence number
1013 this.context.ReadSequenceNumber++;
1018 private bool Compare (byte[] array1, byte[] array2)
1021 return (array2 == null);
1024 if (array1.Length != array2.Length)
1026 for (int i = 0; i < array1.Length; i++) {
1027 if (array1[i] != array2[i])
1035 #region CipherSpecV2 processing
1037 private void ProcessCipherSpecV2Buffer (SecurityProtocolType protocol, byte[] buffer)
1039 TlsStream codes = new TlsStream(buffer);
1041 string prefix = (protocol == SecurityProtocolType.Ssl3) ? "SSL_" : "TLS_";
1043 while (codes.Position < codes.Length)
1045 byte check = codes.ReadByte();
1049 // SSL/TLS cipher spec
1050 short code = codes.ReadInt16();
1051 int index = this.Context.SupportedCiphers.IndexOf(code);
1054 this.Context.Negotiating.Cipher = this.Context.SupportedCiphers[index];
1060 byte[] tmp = new byte[2];
1061 codes.Read(tmp, 0, tmp.Length);
1063 int tmpCode = ((check & 0xff) << 16) | ((tmp[0] & 0xff) << 8) | (tmp[1] & 0xff);
1064 CipherSuite cipher = this.MapV2CipherCode(prefix, tmpCode);
1068 this.Context.Negotiating.Cipher = cipher;
1074 if (this.Context.Negotiating == null)
1076 throw new TlsException(AlertDescription.InsuficientSecurity, "Insuficient Security");
1080 private CipherSuite MapV2CipherCode(string prefix, int code)
1087 // TLS_RC4_128_WITH_MD5
1088 return this.Context.SupportedCiphers[prefix + "RSA_WITH_RC4_128_MD5"];
1091 // TLS_RC4_128_EXPORT40_WITH_MD5
1092 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC4_40_MD5"];
1095 // TLS_RC2_CBC_128_CBC_WITH_MD5
1096 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
1099 // TLS_RC2_CBC_128_CBC_EXPORT40_WITH_MD5
1100 return this.Context.SupportedCiphers[prefix + "RSA_EXPORT_WITH_RC2_CBC_40_MD5"];
1103 // TLS_IDEA_128_CBC_WITH_MD5
1107 // TLS_DES_64_CBC_WITH_MD5
1111 // TLS_DES_192_EDE3_CBC_WITH_MD5