Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Security / TlsSspiNegotiation.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.ServiceModel.Security
6 {
7     using System.ComponentModel;
8     using System.Diagnostics;
9     using System.IdentityModel;
10     using System.IdentityModel.Selectors;
11     using System.IdentityModel.Tokens;
12     using System.Runtime.InteropServices;
13     using System.Security;
14     using System.Security.Authentication.ExtendedProtection;
15     using System.Security.Cryptography;
16     using System.Security.Cryptography.X509Certificates;
17     using System.Security.Principal;
18     using System.Threading;
19
20     using DiagnosticUtility = System.ServiceModel.DiagnosticUtility;
21     using SR = System.ServiceModel.SR;
22
23     sealed class TlsSspiNegotiation : ISspiNegotiation
24     {
25         static SspiContextFlags ClientStandardFlags;
26         static SspiContextFlags ServerStandardFlags;
27         static SspiContextFlags StandardFlags;
28
29         SspiContextFlags attributes;
30         X509Certificate2 clientCertificate;
31         bool clientCertRequired;
32         SslConnectionInfo connectionInfo;
33         SafeFreeCredentials credentialsHandle;
34         string destination;
35         bool disposed;
36         bool isCompleted;
37         bool isServer;
38         SchProtocols protocolFlags;
39         X509Certificate2 remoteCertificate;
40         SafeDeleteContext securityContext;
41
42         //also used as a static lock object
43         const string SecurityPackage = "Microsoft Unified Security Protocol Provider";
44
45         X509Certificate2 serverCertificate;
46         StreamSizes streamSizes;
47         Object syncObject = new Object();
48         bool wasClientCertificateSent;
49         X509Certificate2Collection remoteCertificateChain;
50         string incomingValueTypeUri;
51         /// <summary>
52         /// Client side ctor
53         /// </summary>
54         public TlsSspiNegotiation(
55             string destination,
56             SchProtocols protocolFlags,
57             X509Certificate2 clientCertificate) :
58             this(destination, false, protocolFlags, null, clientCertificate, false)
59         { }
60
61         /// <summary>
62         /// Server side ctor
63         /// </summary>
64         public TlsSspiNegotiation(
65             SchProtocols protocolFlags,
66             X509Certificate2 serverCertificate,
67             bool clientCertRequired) :
68             this(null, true, protocolFlags, serverCertificate, null, clientCertRequired)
69         { }
70
71         static TlsSspiNegotiation()
72         {
73             StandardFlags = SspiContextFlags.ReplayDetect | SspiContextFlags.Confidentiality | SspiContextFlags.AllocateMemory;
74             ServerStandardFlags = StandardFlags | SspiContextFlags.AcceptExtendedError | SspiContextFlags.AcceptStream;
75             ClientStandardFlags = StandardFlags | SspiContextFlags.InitManualCredValidation | SspiContextFlags.InitStream;
76         }
77
78         private TlsSspiNegotiation(
79             string destination,
80             bool isServer,
81             SchProtocols protocolFlags,
82             X509Certificate2 serverCertificate,
83             X509Certificate2 clientCertificate,
84             bool clientCertRequired)
85         {
86             SspiWrapper.GetVerifyPackageInfo(SecurityPackage);
87             this.destination = destination;
88             this.isServer = isServer;
89             this.protocolFlags = protocolFlags;
90             this.serverCertificate = serverCertificate;
91             this.clientCertificate = clientCertificate;
92             this.clientCertRequired = clientCertRequired;
93             this.securityContext = null;
94             if (isServer)
95             {
96                 ValidateServerCertificate();
97             }
98             else
99             {
100                 ValidateClientCertificate();
101             }
102             if (this.isServer)
103             {
104                 // This retry is to address intermittent failure when accessing private key (MB56153)
105                 try
106                 {
107                     AcquireServerCredentials();
108                 }
109                 catch (Win32Exception ex)
110                 {
111                     if (ex.NativeErrorCode != (int)SecurityStatus.UnknownCredential)
112                     {
113                         throw;
114                     }
115
116                     DiagnosticUtility.TraceHandledException(ex, TraceEventType.Information);
117
118                     // Yield
119                     Thread.Sleep(0);
120                     AcquireServerCredentials();
121                 }
122             }
123             else
124             {
125                 // delay client credentials presenting till they are asked for
126                 AcquireDummyCredentials();
127             }
128         }
129
130         /// <summary>
131         /// Local cert of client side
132         /// </summary>
133         public X509Certificate2 ClientCertificate
134         {
135             get
136             {
137                 ThrowIfDisposed();
138                 return this.clientCertificate;
139             }
140         }
141
142         public bool ClientCertRequired
143         {
144             get
145             {
146                 ThrowIfDisposed();
147                 return this.clientCertRequired;
148             }
149         }
150
151         public string Destination
152         {
153             get
154             {
155                 ThrowIfDisposed();
156                 return this.destination;
157             }
158         }
159
160         public DateTime ExpirationTimeUtc
161         {
162             get
163             {
164                 ThrowIfDisposed();
165                 return SecurityUtils.MaxUtcDateTime;
166             }
167         }
168
169         public bool IsCompleted
170         {
171             get
172             {
173                 ThrowIfDisposed();
174                 return this.isCompleted;
175             }
176         }
177
178         public bool IsMutualAuthFlag
179         {
180             get
181             {
182                 ThrowIfDisposed();
183                 return (this.attributes & SspiContextFlags.MutualAuth) != 0;
184             }
185         }
186
187         public bool IsValidContext
188         {
189             get
190             {
191                 return (this.securityContext != null && this.securityContext.IsInvalid == false);
192             }
193         }
194
195         public string KeyEncryptionAlgorithm
196         {
197             get
198             {
199                 return SecurityAlgorithms.TlsSspiKeyWrap;
200             }
201         }
202
203         /// <summary>
204         /// The cert of the remote party
205         /// </summary>
206         public X509Certificate2 RemoteCertificate
207         {
208             get
209             {
210                 ThrowIfDisposed();
211                 if (!IsValidContext)
212                 {
213                     // PreSharp Bug: Property get methods should not throw exceptions.
214 #pragma warning suppress 56503
215                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception((int)SecurityStatus.InvalidHandle));
216                 }
217                 if (this.remoteCertificate == null)
218                 {
219                     ExtractRemoteCertificate();
220                 }
221                 return this.remoteCertificate;
222             }
223         }
224
225         public X509Certificate2Collection RemoteCertificateChain
226         {
227             get
228             {
229                 ThrowIfDisposed();
230                 if (!IsValidContext)
231                 {
232                     // PreSharp Bug: Property get methods should not throw exceptions.
233 #pragma warning suppress 56503
234                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception((int)SecurityStatus.InvalidHandle));
235                 }
236                 if (this.remoteCertificateChain == null)
237                 {
238                     ExtractRemoteCertificate();
239                 }
240                 return this.remoteCertificateChain;
241             }
242         }
243
244         /// <summary>
245         /// Local cert of server side
246         /// </summary>
247         public X509Certificate2 ServerCertificate
248         {
249             get
250             {
251                 ThrowIfDisposed();
252                 return this.serverCertificate;
253             }
254         }
255
256         public bool WasClientCertificateSent
257         {
258             get
259             {
260                 ThrowIfDisposed();
261                 return this.wasClientCertificateSent;
262             }
263         }
264
265         internal SslConnectionInfo ConnectionInfo
266         {
267             get
268             {
269                 ThrowIfDisposed();
270                 if (!IsValidContext)
271                 {
272                     // PreSharp Bug: Property get methods should not throw exceptions.
273 #pragma warning suppress 56503
274                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception((int)SecurityStatus.InvalidHandle));
275                 }
276                 if (this.connectionInfo == null)
277                 {
278                     SslConnectionInfo tmpInfo = SspiWrapper.QueryContextAttributes(
279                         this.securityContext,
280                         ContextAttribute.ConnectionInfo
281                         ) as SslConnectionInfo;
282                     if (IsCompleted)
283                     {
284                         this.connectionInfo = tmpInfo;
285                     }
286                     return tmpInfo;
287                 }
288                 return this.connectionInfo;
289             }
290         }
291
292         internal StreamSizes StreamSizes
293         {
294             get
295             {
296                 ThrowIfDisposed();
297                 if (this.streamSizes == null)
298                 {
299                     StreamSizes tmpSizes = (StreamSizes)SspiWrapper.QueryContextAttributes(this.securityContext, ContextAttribute.StreamSizes);
300                     if (this.IsCompleted)
301                     {
302                         this.streamSizes = tmpSizes;
303                     }
304                     return tmpSizes;
305                 }
306                 return this.streamSizes;
307             }
308         }
309
310         // This is for CDF1229 workaround to be able to echo incoming and outgoing ValueType
311         internal string IncomingValueTypeUri
312         {
313             get { return this.incomingValueTypeUri; }
314             set { this.incomingValueTypeUri = value; }
315         }
316
317         public string GetRemoteIdentityName()
318         {
319             if (!this.IsValidContext)
320             {
321                 return String.Empty;
322             }
323             X509Certificate2 cert = this.RemoteCertificate;
324             if (cert == null)
325             {
326                 return String.Empty;
327             }
328             return SecurityUtils.GetCertificateId(cert);
329         }
330
331         public byte[] Decrypt(byte[] encryptedContent)
332         {
333             ThrowIfDisposed();
334             byte[] dataBuffer = DiagnosticUtility.Utility.AllocateByteArray(encryptedContent.Length);
335
336             Buffer.BlockCopy(encryptedContent, 0, dataBuffer, 0, encryptedContent.Length);
337
338             int decryptedLen = 0;
339             int dataStartOffset;
340             this.DecryptInPlace(dataBuffer, out dataStartOffset, out decryptedLen);
341             byte[] outputBuffer = DiagnosticUtility.Utility.AllocateByteArray(decryptedLen);
342
343             Buffer.BlockCopy(dataBuffer, dataStartOffset, outputBuffer, 0, decryptedLen);
344             return outputBuffer;
345         }
346
347         public void Dispose()
348         {
349             Dispose(true);
350             GC.SuppressFinalize(this);
351         }
352
353         public byte[] Encrypt(byte[] input)
354         {
355             ThrowIfDisposed();
356             byte[] buffer = DiagnosticUtility.Utility.AllocateByteArray(checked(input.Length + StreamSizes.header + StreamSizes.trailer));
357
358             Buffer.BlockCopy(input, 0, buffer, StreamSizes.header, input.Length);
359
360             int encryptedSize = 0;
361
362             this.EncryptInPlace(buffer, 0, input.Length, out encryptedSize);
363             if (encryptedSize == buffer.Length)
364             {
365                 return buffer;
366             }
367             else
368             {
369                 byte[] outputBuffer = DiagnosticUtility.Utility.AllocateByteArray(encryptedSize);
370                 Buffer.BlockCopy(buffer, 0, outputBuffer, 0, encryptedSize);
371                 return outputBuffer;
372             }
373         }
374
375         public byte[] GetOutgoingBlob(byte[] incomingBlob, ChannelBinding channelbinding, ExtendedProtectionPolicy protectionPolicy)
376         {
377             ThrowIfDisposed();
378             SecurityBuffer incomingSecurity = null;
379             if (incomingBlob != null)
380             {
381                 incomingSecurity = new SecurityBuffer(incomingBlob, BufferType.Token);
382             }
383
384             SecurityBuffer outgoingSecurity = new SecurityBuffer(null, BufferType.Token);
385             this.remoteCertificate = null;
386             int statusCode = 0;
387             if (this.isServer == true)
388             {
389                 statusCode = SspiWrapper.AcceptSecurityContext(
390                     this.credentialsHandle,
391                     ref this.securityContext,
392                     ServerStandardFlags | (this.clientCertRequired ? SspiContextFlags.MutualAuth : SspiContextFlags.Zero),
393                     Endianness.Native,
394                     incomingSecurity,
395                     outgoingSecurity,
396                     ref this.attributes
397                     );
398
399             }
400             else
401             {
402                 statusCode = SspiWrapper.InitializeSecurityContext(
403                     this.credentialsHandle,
404                     ref this.securityContext,
405                     this.destination,
406                     ClientStandardFlags,
407                     Endianness.Native,
408                     incomingSecurity,
409                     outgoingSecurity,
410                     ref this.attributes
411                     );
412             }
413
414             if ((statusCode & unchecked((int)0x80000000)) != 0)
415             {
416                 this.Dispose();
417                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(statusCode));
418             }
419
420             if (statusCode == (int)SecurityStatus.OK)
421             {
422                 // we're done
423                 // ensure that the key negotiated is strong enough
424                 if (SecurityUtils.ShouldValidateSslCipherStrength())
425                 {
426                     SslConnectionInfo connectionInfo = (SslConnectionInfo)SspiWrapper.QueryContextAttributes(this.securityContext, ContextAttribute.ConnectionInfo);
427                     if (connectionInfo == null)
428                     {
429                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityNegotiationException(SR.GetString(SR.CannotObtainSslConnectionInfo)));
430                     }
431                     SecurityUtils.ValidateSslCipherStrength(connectionInfo.DataKeySize);
432                 }
433                 this.isCompleted = true;
434             }
435             else if (statusCode == (int)SecurityStatus.CredentialsNeeded)
436             {
437                 // the server requires the client to supply creds
438                 // Currently we dont attempt to find the client cert to choose at runtime
439                 // so just re-call the function
440                 AcquireClientCredentials();
441                 if (this.ClientCertificate != null)
442                 {
443                     this.wasClientCertificateSent = true;
444                 }
445                 return this.GetOutgoingBlob(incomingBlob, channelbinding, protectionPolicy);
446             }
447             else if (statusCode != (int)SecurityStatus.ContinueNeeded)
448             {
449                 this.Dispose();
450                 if (statusCode == (int)SecurityStatus.InternalError)
451                 {
452                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(statusCode, SR.GetString(SR.LsaAuthorityNotContacted)));
453                 }
454                 else
455                 {
456                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(statusCode));
457                 }
458             }
459             return outgoingSecurity.token;
460         }
461
462         /// <summary>
463         /// The decrypted data will start header bytes from the start of
464         /// encryptedContent array.
465         /// </summary>
466         internal unsafe void DecryptInPlace(byte[] encryptedContent, out int dataStartOffset, out int dataLen)
467         {
468             ThrowIfDisposed();
469             dataStartOffset = StreamSizes.header;
470             dataLen = 0;
471
472             byte[] emptyBuffer1 = new byte[0];
473             byte[] emptyBuffer2 = new byte[0];
474             byte[] emptyBuffer3 = new byte[0];
475
476             SecurityBuffer[] securityBuffer = new SecurityBuffer[4];
477             securityBuffer[0] = new SecurityBuffer(encryptedContent, 0, encryptedContent.Length, BufferType.Data);
478             securityBuffer[1] = new SecurityBuffer(emptyBuffer1, BufferType.Empty);
479             securityBuffer[2] = new SecurityBuffer(emptyBuffer2, BufferType.Empty);
480             securityBuffer[3] = new SecurityBuffer(emptyBuffer3, BufferType.Empty);
481
482             int errorCode = SspiWrapper.DecryptMessage(this.securityContext, securityBuffer, 0, false);
483             if (errorCode != 0)
484             {
485                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(errorCode));
486             }
487
488             for (int i = 0; i < securityBuffer.Length; ++i)
489             {
490                 if (securityBuffer[i].type == BufferType.Data)
491                 {
492                     dataLen = securityBuffer[i].size;
493                     return;
494                 }
495             }
496
497             OnBadData();
498         }
499
500         /// <summary>
501         /// Assumes that the data to encrypt is "header" bytes ahead of bufferStartOffset
502         /// </summary>
503         internal unsafe void EncryptInPlace(byte[] buffer, int bufferStartOffset, int dataLen, out int encryptedDataLen)
504         {
505             ThrowIfDisposed();
506             encryptedDataLen = 0;
507             if (bufferStartOffset + dataLen + StreamSizes.header + StreamSizes.trailer > buffer.Length)
508             {
509                 OnBadData();
510             }
511
512             byte[] emptyBuffer = new byte[0];
513             int trailerOffset = bufferStartOffset + StreamSizes.header + dataLen;
514
515             SecurityBuffer[] securityBuffer = new SecurityBuffer[4];
516             securityBuffer[0] = new SecurityBuffer(buffer, bufferStartOffset, StreamSizes.header, BufferType.Header);
517             securityBuffer[1] = new SecurityBuffer(buffer, bufferStartOffset + StreamSizes.header, dataLen, BufferType.Data);
518             securityBuffer[2] = new SecurityBuffer(buffer, trailerOffset, StreamSizes.trailer, BufferType.Trailer);
519             securityBuffer[3] = new SecurityBuffer(emptyBuffer, BufferType.Empty);
520
521             int errorCode = SspiWrapper.EncryptMessage(this.securityContext, securityBuffer, 0);
522             if (errorCode != 0)
523             {
524                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(errorCode));
525             }
526
527             int trailerSize = 0;
528             for (int i = 0; i < securityBuffer.Length; ++i)
529             {
530                 if (securityBuffer[i].type == BufferType.Trailer)
531                 {
532                     trailerSize = securityBuffer[i].size;
533                     encryptedDataLen = StreamSizes.header + dataLen + trailerSize;
534                     return;
535                 }
536             }
537
538             OnBadData();
539         }
540
541         static void ValidatePrivateKey(X509Certificate2 certificate)
542         {
543             bool hasPrivateKey = false;
544             try
545             {
546                 hasPrivateKey = certificate != null && certificate.PrivateKey != null;
547             }
548             catch (SecurityException e)
549             {
550                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SslCertMayNotDoKeyExchange, certificate.SubjectName.Name), e));
551             }
552             catch (CryptographicException e)
553             {
554                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SslCertMayNotDoKeyExchange, certificate.SubjectName.Name), e));
555             }
556             if (!hasPrivateKey)
557             {
558                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.GetString(SR.SslCertMustHavePrivateKey, certificate.SubjectName.Name)));
559             }
560         }
561
562         void ValidateServerCertificate()
563         {
564             if (this.serverCertificate == null)
565             {
566                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("serverCertificate");
567             }
568
569             ValidatePrivateKey(this.serverCertificate);
570         }
571
572         void ValidateClientCertificate()
573         {
574             if (this.clientCertificate != null)
575             {
576                 ValidatePrivateKey(this.clientCertificate);
577             }
578         }
579
580         private void AcquireClientCredentials()
581         {
582             SecureCredential secureCredential = new SecureCredential(SecureCredential.CurrentVersion, this.ClientCertificate, SecureCredential.Flags.ValidateManual | SecureCredential.Flags.NoDefaultCred, this.protocolFlags);
583             this.credentialsHandle = SspiWrapper.AcquireCredentialsHandle(
584                 SecurityPackage,
585                 CredentialUse.Outbound,
586                 secureCredential
587                 );
588         }
589
590         private void AcquireDummyCredentials()
591         {
592             SecureCredential secureCredential = new SecureCredential(SecureCredential.CurrentVersion, null, SecureCredential.Flags.ValidateManual | SecureCredential.Flags.NoDefaultCred, this.protocolFlags);
593             this.credentialsHandle = SspiWrapper.AcquireCredentialsHandle(SecurityPackage, CredentialUse.Outbound, secureCredential);
594         }
595
596         private void AcquireServerCredentials()
597         {
598             SecureCredential secureCredential = new SecureCredential(SecureCredential.CurrentVersion, this.serverCertificate, SecureCredential.Flags.Zero, this.protocolFlags);
599             this.credentialsHandle = SspiWrapper.AcquireCredentialsHandle(
600                 SecurityPackage,
601                 CredentialUse.Inbound,
602                 secureCredential
603                 );
604         }
605
606         private void Dispose(bool disposing)
607         {
608             lock (this.syncObject)
609             {
610                 if (this.disposed == false)
611                 {
612                     this.disposed = true;
613                     if (disposing)
614                     {
615                         if (this.securityContext != null)
616                         {
617                             this.securityContext.Close();
618                             this.securityContext = null;
619                         }
620                         if (this.credentialsHandle != null)
621                         {
622                             this.credentialsHandle.Close();
623                             this.credentialsHandle = null;
624                         }
625                     }
626
627                     // set to null any references that aren't finalizable
628                     this.connectionInfo = null;
629                     this.destination = null;
630                     this.streamSizes = null;
631                 }
632             }
633         }
634
635         private SafeFreeCertContext ExtractCertificateHandle(ContextAttribute contextAttribute)
636         {
637             SafeFreeCertContext result = SspiWrapper.QueryContextAttributes(this.securityContext, contextAttribute) as SafeFreeCertContext;
638             return result;
639         }
640
641         //This method extracts a remote certificate and chain upon request.
642         private void ExtractRemoteCertificate()
643         {
644             SafeFreeCertContext remoteContext = null;
645             this.remoteCertificate = null;
646             this.remoteCertificateChain = null;
647             try
648             {
649                 remoteContext = ExtractCertificateHandle(ContextAttribute.RemoteCertificate);
650                 if (remoteContext != null && !remoteContext.IsInvalid)
651                 {
652                     this.remoteCertificateChain = UnmanagedCertificateContext.GetStore(remoteContext);
653                     this.remoteCertificate = new X509Certificate2(remoteContext.DangerousGetHandle());
654                 }
655             }
656             finally
657             {
658                 if (remoteContext != null)
659                 {
660                     remoteContext.Close();
661                 }
662             }
663         }
664
665         internal bool TryGetContextIdentity(out WindowsIdentity mappedIdentity)
666         {
667             if (!IsValidContext)
668             {
669                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception((int)SecurityStatus.InvalidHandle));
670             }
671
672             SafeCloseHandle token = null;
673             try
674             {
675                 SecurityStatus status = (SecurityStatus)SspiWrapper.QuerySecurityContextToken(this.securityContext, out token);
676                 if (status != SecurityStatus.OK)
677                 {
678                     mappedIdentity = null;
679                     return false;
680                 }
681                 mappedIdentity = new WindowsIdentity(token.DangerousGetHandle(), SecurityUtils.AuthTypeCertMap);
682                 return true;
683             }
684             finally
685             {
686                 if (token != null)
687                 {
688                     token.Close();
689                 }
690             }
691         }
692
693         void OnBadData()
694         {
695             throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.BadData)));
696         }
697
698         void ThrowIfDisposed()
699         {
700             lock (this.syncObject)
701             {
702                 if (this.disposed)
703                 {
704                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException(null));
705                 }
706             }
707         }
708
709         unsafe static class UnmanagedCertificateContext
710         {
711
712             [StructLayout(LayoutKind.Sequential)]
713             private struct _CERT_CONTEXT
714             {
715                 internal Int32 dwCertEncodingType;
716                 internal IntPtr pbCertEncoded;
717                 internal Int32 cbCertEncoded;
718                 internal IntPtr pCertInfo;
719                 internal IntPtr hCertStore;
720             };
721
722             internal static X509Certificate2Collection GetStore(SafeFreeCertContext certContext)
723             {
724                 X509Certificate2Collection result = new X509Certificate2Collection();
725
726                 if (certContext.IsInvalid)
727                     return result;
728
729                 _CERT_CONTEXT context = (_CERT_CONTEXT)Marshal.PtrToStructure(certContext.DangerousGetHandle(), typeof(_CERT_CONTEXT));
730
731                 if (context.hCertStore != IntPtr.Zero)
732                 {
733                     X509Store store = null;
734                     try
735                     {
736                         store = new X509Store(context.hCertStore);
737                         result = store.Certificates;
738                     }
739                     finally
740                     {
741                         if (store != null)
742                             store.Close();
743                     }
744                 }
745                 return result;
746             }
747         }
748     }
749 }