/
Ingestor.cs
112 lines (103 loc) · 4.05 KB
/
Ingestor.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
using System;
using System.Collections.Generic;
using System.Text;
using CertGraph.CLI.Models;
using System.Runtime.Caching;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace CertGraph.CLI
{
class Ingestor
{
const string HttpsPrefix = "https://";
protected List<Cert> _chain;
/// <summary>
/// Makes a TLS connection, creates the chain and returns all the certificates in
/// the chain
/// </summary>
/// <param name="hostname"></param>
/// <param name="chain"></param>
/// <param name="timeout"></param>
/// <returns></returns>
public bool GetCert(string hostname, out List<Cert> chain, int timeout = 3000)
{
#region parse hostname, normalize it for cache lookup
if (!hostname.StartsWith(HttpsPrefix))
{
if (hostname.StartsWith("//"))
hostname = hostname.Substring(2);
hostname = HttpsPrefix + hostname;
}
#endregion
/// all ok, we can connect now and get the chainz, yo
try
{
if (Uri.TryCreate(hostname, UriKind.Absolute, out Uri tmp))
{
TcpClient client = new TcpClient();
if (client.ConnectAsync(tmp.Host, tmp.Port).Wait(timeout))
{
SslStream s = new SslStream(client.GetStream(),
false, // leave conn open
new RemoteCertificateValidationCallback(IngestCertChain),
null); // local cert callback
s.ReadTimeout = timeout;
s.WriteTimeout = timeout;
s.AuthenticateAsClient(tmp.Host);
client.Close();
chain = _chain;
return true;
}
else
{
System.Diagnostics.Trace.TraceError($"Could not connect to {hostname}, {tmp.Host} pot {tmp.Port}");
chain = null;
return false;
}
}
else
{
System.Diagnostics.Trace.TraceError($"Could not resolve url: {hostname}");
chain = null;
return false;
}
}
catch (Exception ex)
{
// Probably a wrong hostname or host is down
System.Diagnostics.Trace.TraceWarning($"Exception connecting to url: {hostname} + {ex.Message}");
chain = null;
return false;
}
}
/// <summary>
/// Take each cert from the chain, return them as a List of <Cert>'s
/// </summary>
/// <param name="sender"></param>
/// <param name="origcert"></param>
/// <param name="chain"></param>
/// <param name="sslPolicyErrors"></param>
/// <returns>true</returns>
private bool IngestCertChain(object sender, X509Certificate origcert, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
_chain = new List<Cert> { };
foreach (X509ChainElement ce in chain.ChainElements)
{
X509Certificate2 c = ce.Certificate;
_chain.Add(new Cert()
{
name = c.FriendlyName, // needed?
serial = c.SerialNumber,
subject = c.Subject,
expiry = c.GetExpirationDateString(),
thumbprint = c.Thumbprint,
issuer = c.Issuer
});
}
_chain.Reverse(); /// We want root -> int -> [int*N] -> leaf
/// always return true, we never fail. We just wanted the chain
return true;
}
}
}