Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
T
Titanium-Web-Proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Titanium-Web-Proxy
Commits
46926db7
Commit
46926db7
authored
Oct 29, 2018
by
Anton Ryzhov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Extract loading and saving certificates; some certificate cache improving
parent
e765f16a
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
256 additions
and
132 deletions
+256
-132
CachedCertificate.cs
src/Titanium.Web.Proxy/Network/CachedCertificate.cs
+9
-8
CertificateManager.cs
src/Titanium.Web.Proxy/Network/CertificateManager.cs
+96
-124
DefaultCertificateStorage.cs
src/Titanium.Web.Proxy/Network/DefaultCertificateStorage.cs
+119
-0
ICertificateStorage.cs
src/Titanium.Web.Proxy/Network/ICertificateStorage.cs
+32
-0
No files found.
src/Titanium.Web.Proxy/Network/CachedCertificate.cs
View file @
46926db7
using
System
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading.Tasks
;
namespace
Titanium.Web.Proxy.Network
{
/// <summary>
/// An object that holds the cached certificate
/// </summary>
internal
class
CachedCertificate
internal
sealed
class
CachedCertificate
{
internal
CachedCertificate
()
{
LastAccess
=
DateTime
.
Now
;
}
internal
X509Certificate2
Certificate
{
get
;
set
;
}
/// <summary>
/// last time this certificate was used
/// Usefull in determining its cache lifetime
/// Certificate creation task.
/// </summary>
internal
Task
<
X509Certificate2
>
CreationTask
{
get
;
set
;
}
/// <summary>
/// Last time this certificate was used.
/// Useful in determining its cache lifetime.
/// </summary>
internal
DateTime
LastAccess
{
get
;
set
;
}
}
...
...
src/Titanium.Web.Proxy/Network/CertificateManager.cs
View file @
46926db7
...
...
@@ -4,8 +4,8 @@ using System.Collections.Generic;
using
System.Diagnostics
;
using
System.IO
;
using
System.Linq
;
using
System.Reflection
;
using
System.Security.Cryptography.X509Certificates
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.Helpers
;
using
Titanium.Web.Proxy.Network.Certificate
;
...
...
@@ -30,7 +30,6 @@ namespace Titanium.Web.Proxy.Network
/// Bug #468 Reported.
/// </summary>
DefaultWindows
=
1
}
/// <summary>
...
...
@@ -47,7 +46,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
private
readonly
ConcurrentDictionary
<
string
,
CachedCertificate
>
certificateCache
;
private
readonly
C
oncurrentDictionary
<
string
,
Task
<
X509Certificate2
>>
pendingCertificateCreationTasks
;
private
readonly
C
ancellationTokenSource
clearCertificatesTokenSource
;
private
ICertificateMaker
certEngine
;
...
...
@@ -55,12 +54,12 @@ namespace Titanium.Web.Proxy.Network
private
string
issuer
;
private
bool
pfxFileExists
;
private
X509Certificate2
rootCertificate
;
private
string
rootCertificateName
;
private
ICertificateStorage
certificateStorage
;
/// <summary>
/// Initializes a new instance of the <see cref="CertificateManager"/> class.
/// </summary>
...
...
@@ -100,10 +99,11 @@ namespace Titanium.Web.Proxy.Network
CertificateEngine
=
CertificateEngine
.
BouncyCastle
;
certificateCache
=
new
ConcurrentDictionary
<
string
,
CachedCertificate
>();
pendingCertificateCreationTasks
=
new
ConcurrentDictionary
<
string
,
Task
<
X509Certificate2
>>();
}
private
bool
clearCertificates
{
get
;
set
;
}
clearCertificatesTokenSource
=
new
CancellationTokenSource
();
certificateStorage
=
new
DefaultCertificateStorage
();
}
/// <summary>
/// Is the root certificate used by this proxy is valid?
...
...
@@ -122,7 +122,7 @@ namespace Titanium.Web.Proxy.Network
internal
bool
MachineTrustRoot
{
get
;
set
;
}
/// <summary>
/// Whether trust operations should be done with elevated privil
la
ges
/// Whether trust operations should be done with elevated privil
e
ges
/// Will prompt with UAC if required. Works only on Windows.
/// </summary>
internal
bool
TrustRootAsAdministrator
{
get
;
set
;
}
...
...
@@ -215,14 +215,24 @@ namespace Titanium.Web.Proxy.Network
}
/// <summary>
/// Save all fake certificates
in folder "crts" (will be created in proxy dll directory)
.
/// Save all fake certificates
using <seealso cref="CertificateStorage"/>
.
/// <para>for can load the certificate and not make new certificate every time. </para>
/// </summary>
public
bool
SaveFakeCertificates
{
get
;
set
;
}
=
false
;
/// <summary>
/// The service to save fake certificates.
/// The default storage saves certificates in folder "crts" (will be created in proxy dll directory).
/// </summary>
public
ICertificateStorage
CertificateStorage
{
get
=>
certificateStorage
;
set
=>
certificateStorage
=
value
??
new
DefaultCertificateStorage
();
}
/// <summary>
/// Overwrite Root certificate file.
/// <para>true : replace an existing .pfx file if password is incorect or if RootCertificate = null.</para>
/// <para>true : replace an existing .pfx file if password is incor
r
ect or if RootCertificate = null.</para>
/// </summary>
public
bool
OverwritePfxFile
{
get
;
set
;
}
=
true
;
...
...
@@ -241,52 +251,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
public
void
Dispose
()
{
}
private
string
getRootCertificateDirectory
()
{
string
assemblyLocation
=
Assembly
.
GetExecutingAssembly
().
Location
;
// dynamically loaded assemblies returns string.Empty location
if
(
assemblyLocation
==
string
.
Empty
)
{
assemblyLocation
=
Assembly
.
GetEntryAssembly
().
Location
;
}
string
path
=
Path
.
GetDirectoryName
(
assemblyLocation
);
if
(
path
==
null
)
{
throw
new
NullReferenceException
();
}
return
path
;
}
private
string
getCertificatePath
()
{
string
path
=
getRootCertificateDirectory
();
string
certPath
=
Path
.
Combine
(
path
,
"crts"
);
if
(!
Directory
.
Exists
(
certPath
))
{
Directory
.
CreateDirectory
(
certPath
);
}
return
certPath
;
}
private
string
getRootCertificatePath
()
{
string
path
=
getRootCertificateDirectory
();
string
fileName
=
PfxFilePath
;
if
(
fileName
==
string
.
Empty
)
{
fileName
=
Path
.
Combine
(
path
,
"rootCert.pfx"
);
StorageFlag
=
X509KeyStorageFlags
.
Exportable
;
}
return
fileName
;
clearCertificatesTokenSource
.
Dispose
();
}
/// <summary>
...
...
@@ -412,43 +377,37 @@ namespace Titanium.Web.Proxy.Network
/// <returns></returns>
internal
X509Certificate2
CreateCertificate
(
string
certificateName
,
bool
isRootCertificate
)
{
X509Certificate2
certificate
=
null
;
X509Certificate2
certificate
;
try
{
if
(!
isRootCertificate
&&
SaveFakeCertificates
)
{
string
path
=
getCertificatePath
();
string
subjectName
=
ProxyConstants
.
CNRemoverRegex
.
Replace
(
certificateName
,
string
.
Empty
);
subjectName
=
subjectName
.
Replace
(
"*"
,
"$x$"
);
string
certificatePath
=
Path
.
Combine
(
path
,
subjectName
+
".pfx"
);
string
subjectName
=
ProxyConstants
.
CNRemoverRegex
.
Replace
(
certificateName
,
string
.
Empty
)
.
Replace
(
"*"
,
"$x$"
);
try
{
certificate
=
certificateStorage
.
LoadCertificate
(
subjectName
,
StorageFlag
);
}
catch
(
Exception
e
)
{
ExceptionFunc
(
new
Exception
(
"Failed to load fake certificate."
,
e
));
certificate
=
null
;
}
if
(
!
File
.
Exists
(
certificatePath
)
)
if
(
certificate
==
null
)
{
certificate
=
makeCertificate
(
certificateName
,
false
);
// store as cache
try
{
var
exported
=
certificate
.
Export
(
X509ContentType
.
Pkcs12
);
File
.
WriteAllBytes
(
certificatePath
,
exported
);
certificateStorage
.
SaveCertificate
(
subjectName
,
certificate
);
}
catch
(
Exception
e
)
{
ExceptionFunc
(
new
Exception
(
"Failed to save fake certificate."
,
e
));
}
}
else
{
try
{
certificate
=
new
X509Certificate2
(
certificatePath
,
string
.
Empty
,
StorageFlag
);
}
catch
{
// if load failed create again
certificate
=
makeCertificate
(
certificateName
,
false
);
}
}
}
else
...
...
@@ -459,6 +418,7 @@ namespace Titanium.Web.Proxy.Network
catch
(
Exception
e
)
{
ExceptionFunc
(
e
);
certificate
=
null
;
}
return
certificate
;
...
...
@@ -472,40 +432,41 @@ namespace Titanium.Web.Proxy.Network
internal
async
Task
<
X509Certificate2
>
CreateCertificateAsync
(
string
certificateName
)
{
// check in cache first
if
(
certificateCache
.
TryGetValue
(
certificateName
,
out
var
cached
))
var
item
=
certificateCache
.
GetOrAdd
(
certificateName
,
_
=>
{
cached
.
LastAccess
=
DateTime
.
Now
;
return
cached
.
Certificate
;
}
var
cached
=
new
CachedCertificate
();
cached
.
CreationTask
=
Task
.
Run
(()
=>
{
var
certificate
=
CreateCertificate
(
certificateName
,
false
);
// see http://www.albahari.com/threading/part4.aspx for the explanation
// why Thread.MemoryBarrier is used here and below
cached
.
Certificate
=
certificate
;
Thread
.
MemoryBarrier
();
cached
.
CreationTask
=
null
;
Thread
.
MemoryBarrier
();
return
certificate
;
});
// handle burst requests with same certificate name
// by checking for existing task for same certificate name
if
(
pendingCertificateCreationTasks
.
TryGetValue
(
certificateName
,
out
var
task
))
{
return
await
task
;
}
return
cached
;
});
// run certificate creation task & add it to pending tasks
task
=
Task
.
Run
(()
=>
item
.
LastAccess
=
DateTime
.
Now
;
if
(
item
.
Certificate
!=
null
)
{
var
result
=
CreateCertificate
(
certificateName
,
false
);
if
(
result
!=
null
)
{
certificateCache
.
TryAdd
(
certificateName
,
new
CachedCertificate
{
Certificate
=
result
});
}
return
item
.
Certificate
;
}
return
result
;
});
pendingCertificateCreationTasks
.
TryAdd
(
certificateName
,
task
);
// handle burst requests with same certificate name
// by checking for existing task
Thread
.
MemoryBarrier
();
var
task
=
item
.
CreationTask
;
// cleanup pending tasks & return result
var
certificate
=
await
task
;
pendingCertificateCreationTasks
.
TryRemove
(
certificateName
,
out
task
);
Thread
.
MemoryBarrier
();
return
certificate
;
// return result
return
item
.
Certificate
??
await
task
;
}
/// <summary>
...
...
@@ -513,8 +474,8 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
internal
async
void
ClearIdleCertificates
()
{
clearCertificates
=
true
;
while
(
clearCertificates
)
var
cancellationToken
=
clearCertificatesTokenSource
.
Token
;
while
(
!
cancellationToken
.
IsCancellationRequested
)
{
var
cutOff
=
DateTime
.
Now
.
AddMinutes
(-
1
*
CertificateCacheTimeOutMinutes
);
...
...
@@ -526,7 +487,14 @@ namespace Titanium.Web.Proxy.Network
}
// after a minute come back to check for outdated certificates in cache
await
Task
.
Delay
(
1000
*
60
);
try
{
await
Task
.
Delay
(
1000
*
60
,
cancellationToken
);
}
catch
(
TaskCanceledException
)
{
return
;
}
}
}
...
...
@@ -535,7 +503,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
internal
void
StopClearIdleCertificates
()
{
clearCertificates
=
false
;
clearCertificates
TokenSource
.
Cancel
()
;
}
/// <summary>
...
...
@@ -557,9 +525,20 @@ namespace Titanium.Web.Proxy.Network
return
true
;
}
if
(!
OverwritePfxFile
&&
pfxFileExists
)
if
(!
OverwritePfxFile
)
{
return
false
;
try
{
var
rootCert
=
certificateStorage
.
LoadRootCertificate
(
PfxFilePath
,
PfxPassword
,
X509KeyStorageFlags
.
Exportable
);
if
(
rootCert
!=
null
)
{
return
false
;
}
}
catch
{
// root cert cannot be loaded
}
}
try
...
...
@@ -577,15 +556,14 @@ namespace Titanium.Web.Proxy.Network
{
try
{
Directory
.
Delete
(
getCertificatePath
(),
true
);
certificateStorage
.
Clear
(
);
}
catch
{
// ignore
}
string
fileName
=
getRootCertificatePath
();
File
.
WriteAllBytes
(
fileName
,
RootCertificate
.
Export
(
X509ContentType
.
Pkcs12
,
PfxPassword
));
certificateStorage
.
SaveRootCertificate
(
PfxFilePath
,
PfxPassword
,
RootCertificate
);
}
catch
(
Exception
e
)
{
...
...
@@ -602,16 +580,9 @@ namespace Titanium.Web.Proxy.Network
/// <returns></returns>
public
X509Certificate2
LoadRootCertificate
()
{
string
fileName
=
getRootCertificatePath
();
pfxFileExists
=
File
.
Exists
(
fileName
);
if
(!
pfxFileExists
)
{
return
null
;
}
try
{
return
new
X509Certificate2
(
fileName
,
PfxPassword
,
StorageFlag
);
return
certificateStorage
.
LoadRootCertificate
(
PfxFilePath
,
PfxPassword
,
X509KeyStorageFlags
.
Exportable
);
}
catch
(
Exception
e
)
{
...
...
@@ -861,7 +832,7 @@ namespace Titanium.Web.Proxy.Network
ErrorDialog
=
false
,
WindowStyle
=
ProcessWindowStyle
.
Hidden
},
// currentUser\Personal & currentMachine\Personal
new
ProcessStartInfo
{
...
...
@@ -904,6 +875,7 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
public
void
ClearRootCertificate
()
{
certificateStorage
.
Clear
();
certificateCache
.
Clear
();
rootCertificate
=
null
;
}
...
...
src/Titanium.Web.Proxy/Network/DefaultCertificateStorage.cs
0 → 100644
View file @
46926db7
using
System
;
using
System.IO
;
using
System.Reflection
;
using
System.Security.Cryptography.X509Certificates
;
namespace
Titanium.Web.Proxy.Network
{
internal
sealed
class
DefaultCertificateStorage
:
ICertificateStorage
{
private
const
string
defaultCertificateDirectoryName
=
"crts"
;
private
const
string
defaultCertificateFileExtension
=
".pfx"
;
private
const
string
defaultRootCertificateFileName
=
"rootCert"
+
defaultCertificateFileExtension
;
private
string
rootCertificatePath
;
private
string
certificatePath
;
public
X509Certificate2
LoadRootCertificate
(
string
name
,
string
password
,
X509KeyStorageFlags
storageFlags
)
{
string
filePath
=
getRootCertificatePath
(
name
);
return
loadCertificate
(
filePath
,
password
,
storageFlags
);
}
public
void
SaveRootCertificate
(
string
name
,
string
password
,
X509Certificate2
certificate
)
{
string
filePath
=
getRootCertificatePath
(
name
);
byte
[]
exported
=
certificate
.
Export
(
X509ContentType
.
Pkcs12
,
password
);
File
.
WriteAllBytes
(
filePath
,
exported
);
}
/// <inheritdoc />
public
X509Certificate2
LoadCertificate
(
string
subjectName
,
X509KeyStorageFlags
storageFlags
)
{
string
filePath
=
Path
.
Combine
(
getCertificatePath
(),
subjectName
+
defaultCertificateFileExtension
);
return
loadCertificate
(
filePath
,
string
.
Empty
,
storageFlags
);
}
/// <inheritdoc />
public
void
SaveCertificate
(
string
subjectName
,
X509Certificate2
certificate
)
{
string
filePath
=
Path
.
Combine
(
getCertificatePath
(),
subjectName
+
defaultCertificateFileExtension
);
byte
[]
exported
=
certificate
.
Export
(
X509ContentType
.
Pkcs12
);
File
.
WriteAllBytes
(
filePath
,
exported
);
}
public
void
Clear
()
{
if
(
Directory
.
Exists
(
getCertificatePath
()))
{
Directory
.
Delete
(
getCertificatePath
(),
true
);
}
certificatePath
=
null
;
}
private
X509Certificate2
loadCertificate
(
string
filePath
,
string
password
,
X509KeyStorageFlags
storageFlags
)
{
byte
[]
exported
;
try
{
exported
=
File
.
ReadAllBytes
(
filePath
);
}
catch
(
IOException
)
{
// file or directory not found
return
null
;
}
return
new
X509Certificate2
(
exported
,
password
,
storageFlags
);
}
private
string
getRootCertificatePath
(
string
filePath
)
{
if
(
Path
.
IsPathRooted
(
filePath
))
{
return
filePath
;
}
return
Path
.
Combine
(
getRootCertificateDirectory
(),
string
.
IsNullOrEmpty
(
filePath
)
?
defaultRootCertificateFileName
:
filePath
);
}
private
string
getCertificatePath
()
{
if
(
certificatePath
==
null
)
{
string
path
=
getRootCertificateDirectory
();
string
certPath
=
Path
.
Combine
(
path
,
defaultCertificateDirectoryName
);
if
(!
Directory
.
Exists
(
certPath
))
{
Directory
.
CreateDirectory
(
certPath
);
}
certificatePath
=
certPath
;
}
return
certificatePath
;
}
private
string
getRootCertificateDirectory
()
{
if
(
rootCertificatePath
==
null
)
{
string
assemblyLocation
=
GetType
().
Assembly
.
Location
;
// dynamically loaded assemblies returns string.Empty location
if
(
assemblyLocation
==
string
.
Empty
)
{
assemblyLocation
=
Assembly
.
GetEntryAssembly
().
Location
;
}
string
path
=
Path
.
GetDirectoryName
(
assemblyLocation
);
rootCertificatePath
=
path
??
throw
new
NullReferenceException
();
}
return
rootCertificatePath
;
}
}
}
src/Titanium.Web.Proxy/Network/ICertificateStorage.cs
0 → 100644
View file @
46926db7
using
System.Security.Cryptography.X509Certificates
;
namespace
Titanium.Web.Proxy.Network
{
public
interface
ICertificateStorage
{
/// <summary>
/// Loads the root certificate from the storage.
/// </summary>
X509Certificate2
LoadRootCertificate
(
string
name
,
string
password
,
X509KeyStorageFlags
storageFlags
);
/// <summary>
/// Saves the root certificate to the storage.
/// </summary>
void
SaveRootCertificate
(
string
name
,
string
password
,
X509Certificate2
certificate
);
/// <summary>
/// Loads certificate from the storage. Returns true if certificate does not exist.
/// </summary>
X509Certificate2
LoadCertificate
(
string
subjectName
,
X509KeyStorageFlags
storageFlags
);
/// <summary>
/// Stores certificate into the storage.
/// </summary>
void
SaveCertificate
(
string
subjectName
,
X509Certificate2
certificate
);
/// <summary>
/// Clears the storage.
/// </summary>
void
Clear
();
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment