6
6
using System . Reflection ;
7
7
using System . Threading . Tasks ;
8
8
using System . Threading ;
9
+ using System . Linq ;
9
10
10
11
namespace Titanium . Web . Proxy . Helpers
11
12
{
12
- public class CertificateManager : IDisposable
13
+ internal class CachedCertificate
14
+ {
15
+ internal X509Certificate2 Certificate { get ; set ; }
16
+
17
+ internal DateTime LastAccess { get ; set ; }
18
+
19
+ internal CachedCertificate ( )
20
+ {
21
+ LastAccess = DateTime . Now ;
22
+ }
23
+
24
+ }
25
+ internal class CertificateManager : IDisposable
13
26
{
14
27
private const string CertCreateFormat =
15
28
"-ss {0} -n \" CN={1}, O={2}\" -sky {3} -cy {4} -m 120 -a sha256 -eku 1.3.6.1.5.5.7.3.1 {5}" ;
16
29
17
- private readonly IDictionary < string , X509Certificate2 > _certificateCache ;
30
+ private readonly IDictionary < string , CachedCertificate > certificateCache ;
18
31
private static SemaphoreSlim semaphoreLock = new SemaphoreSlim ( 1 ) ;
19
32
20
- public string Issuer { get ; private set ; }
21
- public string RootCertificateName { get ; private set ; }
33
+ internal string Issuer { get ; private set ; }
34
+ internal string RootCertificateName { get ; private set ; }
22
35
23
- public X509Store MyStore { get ; private set ; }
24
- public X509Store RootStore { get ; private set ; }
36
+ internal X509Store MyStore { get ; private set ; }
37
+ internal X509Store RootStore { get ; private set ; }
25
38
26
- public CertificateManager ( string issuer , string rootCertificateName )
39
+ internal CertificateManager ( string issuer , string rootCertificateName )
27
40
{
28
41
Issuer = issuer ;
29
42
RootCertificateName = rootCertificateName ;
30
43
31
44
MyStore = new X509Store ( StoreName . My , StoreLocation . CurrentUser ) ;
32
45
RootStore = new X509Store ( StoreName . Root , StoreLocation . CurrentUser ) ;
33
46
34
- _certificateCache = new Dictionary < string , X509Certificate2 > ( ) ;
47
+ certificateCache = new Dictionary < string , CachedCertificate > ( ) ;
35
48
}
36
49
37
50
/// <summary>
38
51
/// Attempts to move a self-signed certificate to the root store.
39
52
/// </summary>
40
53
/// <returns>true if succeeded, else false</returns>
41
- public async Task < bool > CreateTrustedRootCertificate ( )
54
+ internal async Task < bool > CreateTrustedRootCertificate ( )
42
55
{
43
56
X509Certificate2 rootCertificate =
44
- await CreateCertificate ( RootStore , RootCertificateName ) ;
57
+ await CreateCertificate ( RootStore , RootCertificateName , true ) ;
45
58
46
59
return rootCertificate != null ;
47
60
}
48
61
/// <summary>
49
62
/// Attempts to remove the self-signed certificate from the root store.
50
63
/// </summary>
51
64
/// <returns>true if succeeded, else false</returns>
52
- public async Task < bool > DestroyTrustedRootCertificate ( )
65
+ internal bool DestroyTrustedRootCertificate ( )
53
66
{
54
- return await DestroyCertificate ( RootStore , RootCertificateName ) ;
67
+ return DestroyCertificate ( RootStore , RootCertificateName , false ) ;
55
68
}
56
69
57
- public X509Certificate2Collection FindCertificates ( string certificateSubject )
70
+ internal X509Certificate2Collection FindCertificates ( string certificateSubject )
58
71
{
59
72
return FindCertificates ( MyStore , certificateSubject ) ;
60
73
}
@@ -67,29 +80,36 @@ protected virtual X509Certificate2Collection FindCertificates(X509Store store, s
67
80
discoveredCertificates : null ;
68
81
}
69
82
70
- public async Task < X509Certificate2 > CreateCertificate ( string certificateName )
83
+ internal async Task < X509Certificate2 > CreateCertificate ( string certificateName , bool isRootCertificate )
71
84
{
72
- return await CreateCertificate ( MyStore , certificateName ) ;
85
+ return await CreateCertificate ( MyStore , certificateName , isRootCertificate ) ;
73
86
}
74
- protected async virtual Task < X509Certificate2 > CreateCertificate ( X509Store store , string certificateName )
75
- {
76
-
77
- if ( _certificateCache . ContainsKey ( certificateName ) )
78
- return _certificateCache [ certificateName ] ;
79
87
88
+ protected async virtual Task < X509Certificate2 > CreateCertificate ( X509Store store , string certificateName , bool isRootCertificate )
89
+ {
80
90
await semaphoreLock . WaitAsync ( ) ;
81
91
82
- X509Certificate2 certificate = null ;
83
92
try
84
93
{
94
+ if ( certificateCache . ContainsKey ( certificateName ) )
95
+ {
96
+ var cached = certificateCache [ certificateName ] ;
97
+ cached . LastAccess = DateTime . Now ;
98
+ return cached . Certificate ;
99
+ }
100
+ X509Certificate2 certificate = null ;
85
101
store . Open ( OpenFlags . ReadWrite ) ;
86
102
string certificateSubject = string . Format ( "CN={0}, O={1}" , certificateName , Issuer ) ;
87
103
88
- var certificates =
89
- FindCertificates ( store , certificateSubject ) ;
104
+ X509Certificate2Collection certificates ;
105
+
106
+ if ( isRootCertificate )
107
+ {
108
+ certificates = FindCertificates ( store , certificateSubject ) ;
90
109
91
- if ( certificates != null )
92
- certificate = certificates [ 0 ] ;
110
+ if ( certificates != null )
111
+ certificate = certificates [ 0 ] ;
112
+ }
93
113
94
114
if ( certificate == null )
95
115
{
@@ -99,22 +119,26 @@ protected async virtual Task<X509Certificate2> CreateCertificate(X509Store store
99
119
await CreateCertificate ( args ) ;
100
120
certificates = FindCertificates ( store , certificateSubject ) ;
101
121
102
- return certificates != null ?
103
- certificates [ 0 ] : null ;
122
+ //remove it from store
123
+ if ( ! isRootCertificate )
124
+ DestroyCertificate ( certificateName ) ;
125
+
126
+ if ( certificates != null )
127
+ certificate = certificates [ 0 ] ;
104
128
}
105
129
106
130
store . Close ( ) ;
107
- if ( certificate != null && ! _certificateCache . ContainsKey ( certificateName ) )
108
- _certificateCache . Add ( certificateName , certificate ) ;
131
+ if ( certificate != null && ! certificateCache . ContainsKey ( certificateName ) )
132
+ certificateCache . Add ( certificateName , new CachedCertificate ( ) { Certificate = certificate } ) ;
109
133
110
134
return certificate ;
111
135
}
112
136
finally
113
137
{
114
- semaphoreLock . Release ( ) ;
138
+ semaphoreLock . Release ( ) ;
115
139
}
116
-
117
140
}
141
+
118
142
protected virtual Task < int > CreateCertificate ( string [ ] args )
119
143
{
120
144
@@ -153,40 +177,32 @@ protected virtual Task<int> CreateCertificate(string[] args)
153
177
154
178
}
155
179
156
- public async Task < bool > DestroyCertificate ( string certificateName )
180
+ internal bool DestroyCertificate ( string certificateName )
157
181
{
158
- return await DestroyCertificate ( MyStore , certificateName ) ;
182
+ return DestroyCertificate ( MyStore , certificateName , false ) ;
159
183
}
160
- protected virtual async Task < bool > DestroyCertificate ( X509Store store , string certificateName )
161
- {
162
- await semaphoreLock . WaitAsync ( ) ;
163
184
185
+ protected virtual bool DestroyCertificate ( X509Store store , string certificateName , bool removeFromCache )
186
+ {
164
187
X509Certificate2Collection certificates = null ;
165
- try
166
- {
167
- store . Open ( OpenFlags . ReadWrite ) ;
168
- string certificateSubject = string . Format ( "CN={0}, O={1}" , certificateName , Issuer ) ;
169
188
189
+ store . Open ( OpenFlags . ReadWrite ) ;
190
+ string certificateSubject = string . Format ( "CN={0}, O={1}" , certificateName , Issuer ) ;
191
+
192
+ certificates = FindCertificates ( store , certificateSubject ) ;
193
+ if ( certificates != null )
194
+ {
195
+ store . RemoveRange ( certificates ) ;
170
196
certificates = FindCertificates ( store , certificateSubject ) ;
171
- if ( certificates != null )
172
- {
173
- store . RemoveRange ( certificates ) ;
174
- certificates = FindCertificates ( store , certificateSubject ) ;
175
- }
176
-
177
- store . Close ( ) ;
178
- if ( certificates == null &&
179
- _certificateCache . ContainsKey ( certificateName ) )
180
- {
181
- _certificateCache . Remove ( certificateName ) ;
182
- }
183
- return certificates == null ;
184
197
}
185
- finally
198
+
199
+ store . Close ( ) ;
200
+ if ( removeFromCache &&
201
+ certificateCache . ContainsKey ( certificateName ) )
186
202
{
187
- semaphoreLock . Release ( ) ;
203
+ certificateCache . Remove ( certificateName ) ;
188
204
}
189
-
205
+ return certificates == null ;
190
206
}
191
207
192
208
protected virtual string GetCertificateCreateArgs ( X509Store store , string certificateName )
@@ -203,6 +219,37 @@ protected virtual string GetCertificateCreateArgs(X509Store store, string certif
203
219
return certCreatArgs ;
204
220
}
205
221
222
+ private static bool clearCertificates { get ; set ; }
223
+
224
+ internal void StopClearIdleCertificates ( )
225
+ {
226
+ clearCertificates = false ;
227
+ }
228
+
229
+ internal async void ClearIdleCertificates ( )
230
+ {
231
+ clearCertificates = true ;
232
+ while ( clearCertificates )
233
+ {
234
+ await semaphoreLock . WaitAsync ( ) ;
235
+
236
+ try
237
+ {
238
+ var cutOff = DateTime . Now . AddSeconds ( - 60 ) ;
239
+
240
+ var outdated = certificateCache
241
+ . Where ( x => x . Value . LastAccess < cutOff )
242
+ . ToList ( ) ;
243
+
244
+ foreach ( var cache in outdated )
245
+ certificateCache . Remove ( cache . Key ) ;
246
+ }
247
+ finally { semaphoreLock . Release ( ) ; }
248
+
249
+ await Task . Delay ( 1000 * 60 * 3 ) . ConfigureAwait ( false ) ;
250
+ }
251
+ }
252
+
206
253
public void Dispose ( )
207
254
{
208
255
if ( MyStore != null )
0 commit comments