-
Notifications
You must be signed in to change notification settings - Fork 564
/
Copy pathSslCertCache.cs
113 lines (97 loc) · 4.7 KB
/
SslCertCache.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using QuickFix.Util;
namespace QuickFix.Transport;
internal static class SslCertCache {
/// <summary>
/// Cache loaded certificates since loading a certificate can be a costly operation
/// </summary>
private static readonly Dictionary<string, X509Certificate2> CertificateCache = new ();
/// <summary>
/// Loads the specified certificate given a path, DistinguishedName or subject name
/// </summary>
/// <param name="name">The certificate path or DistinguishedName/subjectname
/// if it should be loaded from the personal certificate store.</param>
/// <param name="password">The certificate password.</param>
/// <returns>The specified certificate</returns>
internal static X509Certificate2? LoadCertificate(string name, string? password)
{
// TODO: Change _certificateCache's type to ConcurrentDictionary once we start targeting .NET 4,
// then remove this lock and use GetOrAdd function of concurrent dictionary
// e.g.: certificate = _certificateCache.GetOrAdd(name, (key) => LoadCertificateInner(name, password));
lock (CertificateCache)
{
if (CertificateCache.TryGetValue(name, out X509Certificate2? certificate))
return certificate;
try {
certificate = LoadCertificateInner(name, password);
CertificateCache.Add(name, certificate);
return certificate;
} catch (ApplicationException) {
// TODO refactor this function+callers to throw an exception up the stack instead of returning null
// Callers should log as appropriate
return null;
}
}
}
/// <summary>
/// Perform the actual loading of a certificate
/// </summary>
/// <param name="name">The certificate path or DistinguishedName/subjectname if it should be loaded from the personal certificate store.</param>
/// <param name="password">The certificate password.</param>
/// <exception cref="ApplicationException">Certificate could not be loaded from file or store</exception>
/// <returns>The specified certificate</returns>
private static X509Certificate2 LoadCertificateInner(string name, string? password)
{
var certPath = StringUtil.FixSlashes(name);
// If cert file not found, then try to get from certificate store
if (!File.Exists(certPath))
{
var certFromStore = GetCertificateFromStore(StringUtil.FixSlashes(name));
if (certFromStore is not null)
return certFromStore;
string msg =
$"Certificate '{name}' could not be loaded from store or path '{Directory.GetCurrentDirectory()}'";
Console.WriteLine(msg);
throw new ApplicationException(msg);
}
return password is not null
? new X509Certificate2(name, password)
: new X509Certificate2(name);
}
/// <summary>
/// Gets the certificate from store.
/// </summary>
/// <remarks>See http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.aspx for complete example</remarks>
/// <param name="certName">Name of the cert.</param>
/// <returns>The cert, or null if not found</returns>
private static X509Certificate2? GetCertificateFromStore(string certName)
{
return GetCertificateFromStoreHelper(certName, new X509Store(StoreLocation.LocalMachine))
?? GetCertificateFromStoreHelper(certName, new X509Store(StoreLocation.CurrentUser));
}
private static X509Certificate2? GetCertificateFromStoreHelper(string certName, X509Store store)
{
try
{
store.Open(OpenFlags.ReadOnly);
// Place all certificates in an X509Certificate2Collection object.
X509Certificate2Collection certCollection = store.Certificates;
// If using a certificate with a trusted root you do not need to FindByTimeValid, instead:
// currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certName, true);
X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
currentCerts = currentCerts.Find(certName.Contains("CN=")
? X509FindType.FindBySubjectDistinguishedName
: X509FindType.FindBySubjectName, certName, false);
if (currentCerts.Count == 0)
return null;
return currentCerts[0];
}
finally
{
store.Close();
}
}
}