1414using System . Net . Sockets ;
1515using System . Reflection ;
1616using System . Runtime . CompilerServices ;
17+ using System . Runtime . InteropServices ;
1718using System . Security . Authentication ;
1819using System . Security . Cryptography ;
1920using System . Security . Cryptography . X509Certificates ;
@@ -113,6 +114,7 @@ public static void RAND_add(object buf, double entropy) {
113114 public class _SSLContext {
114115 internal readonly X509Certificate2Collection _cert_store = new X509Certificate2Collection ( ) ;
115116 internal string _cafile ;
117+ internal X509Certificate2 _cert ;
116118 private int _verify_mode = SSL_VERIFY_NONE ;
117119
118120 public _SSLContext ( CodeContext context , int protocol ) {
@@ -148,6 +150,10 @@ public int verify_mode {
148150 if ( _verify_mode != CERT_NONE && _verify_mode != CERT_OPTIONAL && _verify_mode != CERT_REQUIRED ) {
149151 throw PythonOps . ValueError ( "invalid value for verify_mode" ) ;
150152 }
153+ // TODO: change this in 3.7
154+ if ( check_hostname && value == CERT_NONE ) {
155+ throw PythonOps . ValueError ( "Cannot set verify_mode to CERT_NONE when check_hostname is enabled." ) ;
156+ }
151157 _verify_mode = value ;
152158 }
153159 }
@@ -156,8 +162,16 @@ public int protocol {
156162 get ; set ;
157163 }
158164
165+ private bool _check_hostname ;
159166 public bool check_hostname {
160- get ; set ;
167+ get => _check_hostname ;
168+ set {
169+ // TODO: change this in 3.7
170+ if ( value && _verify_mode != CERT_OPTIONAL && _verify_mode != CERT_REQUIRED ) {
171+ throw PythonOps . ValueError ( "check_hostname needs a SSL context with either CERT_OPTIONAL or CERT_REQUIRED" ) ;
172+ }
173+ _check_hostname = value ;
174+ }
161175 }
162176
163177 public void set_default_verify_paths ( CodeContext context ) {
@@ -174,8 +188,19 @@ public void set_ecdh_curve(CodeContext context, [NotNull] Bytes curve) {
174188 throw PythonOps . ValueError ( $ "unknown elliptic curve name { PythonOps . Repr ( context , curve ) } ") ;
175189 }
176190
177- public void load_cert_chain ( string certfile , string keyfile = null , object password = null ) {
191+ public void load_cert_chain ( CodeContext context , string certfile , string keyfile = null , object password = null ) {
192+ if ( keyfile is not null ) throw new NotImplementedException ( nameof ( keyfile ) ) ;
193+ if ( password is not null ) throw new NotImplementedException ( nameof ( password ) ) ;
194+ #if NET5_0_OR_GREATER
195+ _cert = X509Certificate2 . CreateFromPemFile ( certfile , keyfile ) ;
196+ #else
197+ _cert = ReadCertificate ( context , certfile , readKey : true ) ;
198+ #endif
199+ }
178200
201+ public PythonList get_ca_certs ( CodeContext context , bool binary_form = false ) {
202+ if ( binary_form ) throw new NotImplementedException ( nameof ( binary_form ) ) ;
203+ return new PythonList ( _cert_store . Cast < X509Certificate2 > ( ) . Select ( c => CertificateToPython ( context , c ) ) ) ;
179204 }
180205
181206 public void load_verify_locations ( CodeContext context , object cafile = null , string capath = null , object cadata = null ) {
@@ -199,23 +224,38 @@ public void load_verify_locations(CodeContext context, object cafile = null, str
199224 }
200225
201226 if ( capath != null ) {
227+ // TODO
202228 }
203229
204- if ( cadata != null ) {
205- var cabuf = cadata as IBufferProtocol ;
206- if ( cabuf != null ) {
207- int pos = 0 ;
208- byte [ ] contents ;
209- using ( IPythonBuffer buf = cabuf . GetBuffer ( ) ) {
210- contents = buf . AsReadOnlySpan ( ) . ToArray ( ) ;
211- }
212- while ( pos < contents . Length ) {
213- byte [ ] curr = new byte [ contents . Length - pos ] ;
214- Array . Copy ( contents , pos , curr , 0 , contents . Length - pos ) ;
215- var cert = new X509Certificate2 ( curr ) ;
230+ if ( cadata is not null ) {
231+ if ( cadata is string s ) {
232+ if ( ! StringOps . TryEncodeAscii ( s , out Bytes ascii ) )
233+ throw PythonOps . ValueError ( "cadata should be an ASCII string or a bytes-like object" ) ;
234+ #if NET5_0_OR_GREATER
235+ _cert_store . ImportFromPem ( s ) ;
236+ #else
237+ string line ;
238+ var lines = new List < string > ( ) ;
239+ using var stream = new MemoryStream ( ascii . UnsafeByteArray ) ;
240+ using var sr = new StreamReader ( stream ) ;
241+ while ( ( line = sr . ReadLine ( ) ) != null )
242+ lines . Add ( line ) ;
243+ _cert_store . Add ( ReadCertificate ( context , string . Empty , lines . ToArray ( ) ) ) ;
244+ #endif
245+ } else if ( cadata is IBufferProtocol cabuf ) {
246+ using IPythonBuffer buf = cabuf . GetBuffer ( ) ;
247+ var contents = buf . AsReadOnlySpan ( ) ;
248+ while ( contents . Length > 0 ) {
249+ #if NET5_0_OR_GREATER
250+ var cert = new X509Certificate2 ( contents ) ;
251+ #else
252+ var cert = new X509Certificate2 ( contents . ToArray ( ) ) ;
253+ #endif
216254 _cert_store . Add ( cert ) ;
217- pos += cert . GetRawCertData ( ) . Length ;
255+ contents = contents . Slice ( cert . GetRawCertData ( ) . Length ) ;
218256 }
257+ } else {
258+ throw PythonOps . ValueError ( "cadata should be an ASCII string or a bytes-like object" ) ;
219259 }
220260 }
221261 }
@@ -230,7 +270,6 @@ public class _SSLSocket {
230270 private SslStream _sslStream ;
231271 private readonly PythonSocket . socket _socket ;
232272 private readonly X509Certificate2Collection _certCollection ;
233- private readonly X509Certificate _cert ;
234273 private readonly int _protocol , _certsMode ;
235274 private readonly bool _validate , _serverSide ;
236275 private readonly CodeContext _context ;
@@ -276,10 +315,6 @@ internal _SSLSocket(CodeContext context, _SSLContext sslcontext, PythonSocket.so
276315 _certCollection = sslcontext . _cert_store ;
277316 }
278317
279- if ( sslcontext . _cafile != null ) {
280- _cert = PythonSsl . ReadCertificate ( context , sslcontext . _cafile ) ;
281- }
282-
283318 _socket = sock ;
284319
285320 EnsureSslStream ( false ) ;
@@ -357,36 +392,23 @@ internal bool CertValidationCallbackRequired(object sender, X509Certificate cert
357392 }
358393
359394 private void ValidateCertificate ( X509Certificate certificate , X509Chain chain , SslPolicyErrors sslPolicyErrors ) {
360- chain = new X509Chain ( ) ;
361- X509Certificate2Collection certificates = new X509Certificate2Collection ( ) ;
362- foreach ( object cert in _certCollection ) {
363- if ( cert is X509Certificate2 ) {
364- certificates . Add ( ( X509Certificate2 ) cert ) ;
365- } else if ( cert is X509Certificate ) {
366- certificates . Add ( new X509Certificate2 ( ( X509Certificate ) cert ) ) ;
367- }
368- }
369- chain . ChainPolicy . ExtraStore . AddRange ( certificates ) ;
370- chain . Build ( new X509Certificate2 ( certificate ) ) ;
371-
372- if ( chain . ChainStatus . Length > 0 ) {
373- foreach ( var elem in chain . ChainStatus ) {
374- if ( elem . Status == X509ChainStatusFlags . UntrustedRoot ) {
375- bool isOk = false ;
376- foreach ( var cert in _certCollection ) {
377- if ( certificate . Issuer == cert . Subject ) {
378- isOk = true ;
379- }
380- }
381-
382- if ( isOk ) {
383- continue ;
395+ Debug . Assert ( chain . ChainStatus . Length > 0 ) ;
396+ foreach ( var elem in chain . ChainStatus ) {
397+ if ( elem . Status == X509ChainStatusFlags . UntrustedRoot ) {
398+ bool isOk = false ;
399+ foreach ( var cert in _certCollection ) {
400+ if ( certificate . Issuer == cert . Subject ) {
401+ isOk = true ;
384402 }
385403 }
386404
387- ValidationError ( sslPolicyErrors ) ;
388- break ;
405+ if ( isOk ) {
406+ continue ;
407+ }
389408 }
409+
410+ ValidationError ( sslPolicyErrors ) ;
411+ break ;
390412 }
391413 }
392414
@@ -411,15 +433,13 @@ public void do_handshake() {
411433
412434 try {
413435 if ( _serverSide ) {
436+ var _cert = context . _cert ;
437+ if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ) {
438+ _cert = new X509Certificate2 ( _cert . Export ( X509ContentType . Pkcs12 ) ) ;
439+ }
414440 _sslStream . AuthenticateAsServer ( _cert , _certsMode == PythonSsl . CERT_REQUIRED , enabledSslProtocols , false ) ;
415441 } else {
416-
417- var collection = new X509CertificateCollection ( ) ;
418-
419- if ( _cert != null ) {
420- collection . Add ( _cert ) ;
421- }
422- _sslStream . AuthenticateAsClient ( _serverHostName ?? _socket . _hostName , collection , enabledSslProtocols , false ) ;
442+ _sslStream . AuthenticateAsClient ( _serverHostName ?? _socket . _hostName , context . _cert_store , enabledSslProtocols , false ) ;
423443 }
424444 } catch ( AuthenticationException e ) {
425445 ( ( IDisposable ) _socket . _socket ) . Dispose ( ) ;
@@ -500,6 +520,8 @@ public PythonTuple cipher() {
500520 return null ;
501521 }
502522
523+ public object compression ( ) => null ; // TODO
524+
503525 private string ProtocolToPython ( ) {
504526 switch ( _sslStream . SslProtocol ) {
505527#pragma warning disable CA5397 // Do not use deprecated SslProtocols values
@@ -592,13 +614,20 @@ public string server() {
592614 [ Documentation ( @"Writes the bytes-like object b into the SSL object.
593615
594616Returns the number of bytes written." ) ]
595- public int write ( CodeContext /*!*/ context , Bytes data ) {
617+ public int write ( CodeContext /*!*/ context , IBufferProtocol data ) {
596618 EnsureSslStream ( true ) ;
597619
598- byte [ ] buffer = data . UnsafeByteArray ;
620+ using var buffer = data . GetBuffer ( ) ;
599621 try {
600- _sslStream . Write ( buffer ) ;
601- return buffer . Length ;
622+ #if NETCOREAPP
623+ var bytes = buffer . AsReadOnlySpan ( ) ;
624+ _sslStream . Write ( bytes ) ;
625+ return bytes . Length ;
626+ #else
627+ var bytes = buffer . AsUnsafeArray ( ) ?? buffer . ToArray ( ) ;
628+ _sslStream . Write ( bytes ) ;
629+ return bytes . Length ;
630+ #endif
602631 } catch ( Exception e ) {
603632 throw PythonSocket . MakeException ( context , e ) ;
604633 }
@@ -921,6 +950,10 @@ private static X509Certificate2 ReadCertificate(CodeContext context, string file
921950 throw PythonExceptions . CreateThrowable ( SSLError ( context ) , "Can't open file " , filename ) ;
922951 }
923952
953+ return ReadCertificate ( context , filename , lines , readKey ) ;
954+ }
955+
956+ private static X509Certificate2 ReadCertificate ( CodeContext context , string filename , string [ ] lines , bool readKey = false ) {
924957 X509Certificate2 cert = null ;
925958 RSACryptoServiceProvider key = null ;
926959 try {
@@ -945,6 +978,16 @@ private static X509Certificate2 ReadCertificate(CodeContext context, string file
945978 throw ErrorDecoding ( context , filename , e ) ;
946979 }
947980 }
981+ } else if ( lines [ i ] == "-----BEGIN PRIVATE KEY-----" ) {
982+ var keyStr = ReadToEnd ( lines , ref i , "-----END PRIVATE KEY-----" ) ;
983+ if ( readKey ) {
984+ try {
985+ var keyBytes = Convert . FromBase64String ( keyStr . ToString ( ) ) ;
986+ // TODO
987+ } catch ( Exception e ) {
988+ throw ErrorDecoding ( context , filename , e ) ;
989+ }
990+ }
948991 }
949992 }
950993 } catch ( InvalidOperationException e ) {
@@ -981,7 +1024,6 @@ private static X509Certificate2 CopyWithPrivateKey(this X509Certificate2 certifi
9811024 }
9821025#endif
9831026
984-
9851027 #region Private Key Parsing
9861028
9871029 private const int ClassOffset = 6 ;
@@ -1027,7 +1069,7 @@ private static RSACryptoServiceProvider ParsePkcs1DerEncodedPrivateKey(CodeConte
10271069 int version = ReadUniversalInt ( x , ref offset ) ;
10281070 if ( version != 0 ) {
10291071 // unsupported version
1030- throw new InvalidOperationException ( String . Format ( "bad vesion : {0}" , version ) ) ;
1072+ throw new InvalidOperationException ( String . Format ( "bad version : {0}" , version ) ) ;
10311073 }
10321074
10331075 // read in parameters and initialize provider
@@ -1071,7 +1113,7 @@ private static byte[] ReadUniversalIntAsBytes(byte[] x, ref int offset) {
10711113 private static void ReadIntType ( byte [ ] x , ref int offset ) {
10721114 int versionType = x [ offset ++ ] ;
10731115 if ( versionType != UniversalInteger ) {
1074- throw new InvalidOperationException ( String . Format ( "expected version, fonud {0}" , versionType ) ) ;
1116+ throw new InvalidOperationException ( String . Format ( "expected version, found {0}" , versionType ) ) ;
10751117 }
10761118 }
10771119 private static int ReadUniversalInt ( byte [ ] x , ref int offset ) {
@@ -1134,8 +1176,8 @@ private static Exception ErrorDecoding(CodeContext context, params object[] args
11341176 public const int CERT_OPTIONAL = 1 ;
11351177 public const int CERT_REQUIRED = 2 ;
11361178
1137- public const int PROTOCOL_SSLv2 = 0 ;
1138- public const int PROTOCOL_SSLv3 = 1 ;
1179+ private const int PROTOCOL_SSLv2 = 0 ;
1180+ private const int PROTOCOL_SSLv3 = 1 ;
11391181 public const int PROTOCOL_SSLv23 = 2 ;
11401182 public const int PROTOCOL_TLSv1 = 3 ;
11411183 public const int PROTOCOL_TLSv1_1 = 4 ;
0 commit comments