public async Task Simple_Create_Win_Certificate_Test() { var tasks = new List <Task>(); var mgr = new CertificateManager(null, null, false, false, false, new Lazy <Action <Exception> >(() => (e => { //Console.WriteLine(e.ToString() + e.InnerException != null ? e.InnerException.ToString() : string.Empty); })).Value); mgr.CertificateEngine = CertificateEngine.DefaultWindows; mgr.CreateRootCertificate(true); mgr.TrustRootCertificate(true); mgr.ClearIdleCertificates(); for (int i = 0; i < 5; i++) { foreach (string host in hostNames) { tasks.Add(Task.Run(() => { //get the connection var certificate = mgr.CreateCertificate(host, false); Assert.IsNotNull(certificate); })); } } await Task.WhenAll(tasks.ToArray()); mgr.RemoveTrustedRootCertificate(true); mgr.StopClearIdleCertificates(); }
// uncomment this to compare WinCert maker performance with BC (BC takes more time for same test above) //[TestMethod] public async Task Simple_Create_Win_Certificate_Test() { var tasks = new List <Task>(); var mgr = new CertificateManager(null, null, false, false, false, new Lazy <ExceptionHandler>(() => (e => { Debug.WriteLine(e.ToString()); Debug.WriteLine(e.InnerException?.ToString()); })).Value) { CertificateEngine = CertificateEngine.DefaultWindows }; mgr.CreateRootCertificate(); mgr.TrustRootCertificate(true); mgr.ClearIdleCertificates(); for (int i = 0; i < 5; i++) { tasks.AddRange(hostNames.Select(host => Task.Run(() => { // get the connection var certificate = mgr.CreateCertificate(host, false); Assert.IsNotNull(certificate); }))); } await Task.WhenAll(tasks.ToArray()); mgr.RemoveTrustedRootCertificate(true); mgr.StopClearIdleCertificates(); }
public async Task Simple_Create_Certificate_Stress_Test() { var tasks = new List <Task>(); var mgr = new CertificateManager("Titanium", "Titanium Root Certificate Authority", new Lazy <Action <Exception> >(() => (e => { })).Value); mgr.ClearIdleCertificates(1); for (int i = 0; i < 1000; i++) { foreach (var host in hostNames) { tasks.Add(Task.Run(async() => { await Task.Delay(random.Next(0, 10) * 1000); //get the connection var certificate = mgr.CreateCertificate(host, false); Assert.IsNotNull(certificate); })); } } await Task.WhenAll(tasks.ToArray()); mgr.StopClearIdleCertificates(); }
/// <summary> /// This is called when this proxy acts as a reverse proxy (like a real http server) /// So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client /// </summary> /// <param name="endPoint"></param> /// <param name="tcpClient"></param> /// <returns></returns> private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) { bool disposed = false; var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); CustomBinaryReader clientStreamReader = null; HttpResponseWriter clientStreamWriter = null; try { if (endPoint.EnableSsl) { var clientHelloInfo = await SslTools.PeekClientHello(clientStream); if (clientHelloInfo != null) { var sslStream = new SslStream(clientStream); clientStream = new CustomBufferedStream(sslStream, BufferSize); string sniHostName = clientHelloInfo.GetServerName(); string certName = HttpHelper.GetWildCardDomainName(sniHostName ?? endPoint.GenericCertificateName); var certificate = CertificateManager.CreateCertificate(certName, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false); } //HTTPS server created - we can now decrypt the client's traffic } clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); //now read the request line string httpCmd = await clientStreamReader.ReadLineAsync(); //Now create the request disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, endPoint.EnableSsl?endPoint.GenericCertificateName : null, endPoint, null, true); } finally { if (!disposed) { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); } } }
/// <summary> /// This is called when this proxy acts as a reverse proxy (like a real http server) /// So for HTTPS requests we would start SSL negotiation right away without expecting a CONNECT request from client /// </summary> /// <param name="endPoint"></param> /// <param name="tcpClient"></param> /// <returns></returns> private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcpClient) { bool disposed = false; var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); CustomBinaryReader clientStreamReader = null; StreamWriter clientStreamWriter = null; try { if (endPoint.EnableSsl) { var sslStream = new SslStream(clientStream); clientStream = new CustomBufferedStream(sslStream, BufferSize); //implement in future once SNI supported by SSL stream, for now use the same certificate var certificate = CertificateManager.CreateCertificate(endPoint.GenericCertificateName, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SslProtocols.Tls, false); //HTTPS server created - we can now decrypt the client's traffic } clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; //now read the request line var httpCmd = await clientStreamReader.ReadLineAsync(); //Now create the request disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, endPoint.EnableSsl?endPoint.GenericCertificateName : null, endPoint, null); } finally { if (!disposed) { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); } } }
/// <summary> /// This is called when client is aware of proxy /// So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy /// </summary> /// <param name="endPoint"></param> /// <param name="tcpClient"></param> /// <returns></returns> private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) { bool disposed = false; var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); var clientStreamWriter = new HttpResponseWriter(clientStream); Uri httpRemoteUri; try { //read the first line HTTP command string httpCmd = await clientStreamReader.ReadLineAsync(); if (string.IsNullOrEmpty(httpCmd)) { return; } string httpMethod; string httpUrl; Version version; Request.ParseRequestLine(httpCmd, out httpMethod, out httpUrl, out version); httpRemoteUri = httpMethod == "CONNECT" ? new Uri("http://" + httpUrl) : new Uri(httpUrl); //filter out excluded host names bool excluded = false; if (endPoint.ExcludedHttpsHostNameRegex != null) { excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host)); } if (endPoint.IncludedHttpsHostNameRegex != null) { excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host)); } ConnectRequest connectRequest = null; //Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request) if (httpMethod == "CONNECT") { connectRequest = new ConnectRequest { RequestUri = httpRemoteUri, OriginalRequestUrl = httpUrl, HttpVersion = version, Method = httpMethod, }; await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.RequestHeaders); var connectArgs = new TunnelConnectSessionEventArgs(endPoint); connectArgs.WebSession.Request = connectRequest; connectArgs.ProxyClient.TcpClient = tcpClient; connectArgs.ProxyClient.ClientStream = clientStream; if (TunnelConnectRequest != null) { await TunnelConnectRequest.InvokeParallelAsync(this, connectArgs, ExceptionFunc); } if (!excluded && await CheckAuthorization(clientStreamWriter, connectArgs) == false) { if (TunnelConnectResponse != null) { await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc); } return; } //write back successfull CONNECT response connectArgs.WebSession.Response = ConnectResponse.CreateSuccessfullConnectResponse(version); await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response); var clientHelloInfo = await SslTools.GetClientHelloInfo(clientStream); bool isClientHello = clientHelloInfo != null; if (isClientHello) { connectRequest.ClientHelloInfo = clientHelloInfo; } if (TunnelConnectResponse != null) { connectArgs.IsHttpsConnect = isClientHello; await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs, ExceptionFunc); } if (!excluded && isClientHello) { httpRemoteUri = new Uri("https://" + httpUrl); connectRequest.RequestUri = httpRemoteUri; SslStream sslStream = null; try { var alpnStream = AlpnEnabled ? (Stream) new ServerHelloAlpnAdderStream(clientStream) : clientStream; sslStream = new SslStream(alpnStream); string certName = HttpHelper.GetWildCardDomainName(httpRemoteUri.Host); var certificate = endPoint.GenericCertificate ?? CertificateManager.CreateCertificate(certName, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false); //HTTPS server created - we can now decrypt the client's traffic clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStreamReader.Dispose(); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamWriter = new HttpResponseWriter(clientStream); } catch { sslStream?.Dispose(); return; } //Now read the actual HTTPS request line httpCmd = await clientStreamReader.ReadLineAsync(); } //Hostname is excluded or it is not an HTTPS connect else { //create new connection using (var connection = await GetServerConnection(connectArgs, true)) { if (isClientHello) { if (clientStream.Available > 0) { //send the buffered data var data = new byte[clientStream.Available]; await clientStream.ReadAsync(data, 0, data.Length); await connection.Stream.WriteAsync(data, 0, data.Length); await connection.Stream.FlushAsync(); } var serverHelloInfo = await SslTools.GetServerHelloInfo(connection.Stream); ((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo; } await TcpHelper.SendRaw(clientStream, connection.Stream, (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }); UpdateServerConnectionCount(false); } return; } } //Now create the request disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, httpRemoteUri.Scheme == UriSchemeHttps?httpRemoteUri.Host : null, endPoint, connectRequest); } catch (Exception e) { ExceptionFunc(new Exception("Error whilst authorizing request", e)); } finally { if (!disposed) { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); } } }
/// <summary> /// This is called when client is aware of proxy /// So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy /// </summary> /// <param name="endPoint"></param> /// <param name="tcpClient"></param> /// <returns></returns> private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) { var disposed = false; var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); var clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; Uri httpRemoteUri; try { //read the first line HTTP command var httpCmd = await clientStreamReader.ReadLineAsync(); if (string.IsNullOrEmpty(httpCmd)) { return; } //break up the line into three components (method, remote URL & Http Version) var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3); //Find the request Verb var httpVerb = httpCmdSplit[0].ToUpper(); httpRemoteUri = httpVerb == "CONNECT" ? new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]); //parse the HTTP version var version = HttpHeader.Version11; if (httpCmdSplit.Length == 3) { var httpVersion = httpCmdSplit[2].Trim(); if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase)) { version = HttpHeader.Version10; } } //filter out excluded host names bool excluded = false; if (endPoint.ExcludedHttpsHostNameRegex != null) { excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host)); } if (endPoint.IncludedHttpsHostNameRegex != null) { excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host)); } List <HttpHeader> connectRequestHeaders = null; //Client wants to create a secure tcp tunnel (its a HTTPS request) if (httpVerb == "CONNECT" && !excluded && endPoint.RemoteHttpsPorts.Contains(httpRemoteUri.Port)) { httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); connectRequestHeaders = new List <HttpHeader>(); string tmpLine; while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync())) { var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); var newHeader = new HttpHeader(header[0], header[1]); connectRequestHeaders.Add(newHeader); } if (await CheckAuthorization(clientStreamWriter, connectRequestHeaders) == false) { return; } await WriteConnectResponse(clientStreamWriter, version); SslStream sslStream = null; try { sslStream = new SslStream(clientStream); var certName = HttpHelper.GetWildCardDomainName(httpRemoteUri.Host); var certificate = endPoint.GenericCertificate ?? CertificateManager.CreateCertificate(certName, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false); //HTTPS server created - we can now decrypt the client's traffic clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStreamReader.Dispose(); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamWriter = new StreamWriter(clientStream) { NewLine = ProxyConstants.NewLine }; } catch { sslStream?.Dispose(); return; } //Now read the actual HTTPS request line httpCmd = await clientStreamReader.ReadLineAsync(); } //Sorry cannot do a HTTPS request decrypt to port 80 at this time else if (httpVerb == "CONNECT") { //Siphon out CONNECT request headers await clientStreamReader.ReadAndIgnoreAllLinesAsync(); //write back successfull CONNECT response await WriteConnectResponse(clientStreamWriter, version); await TcpHelper.SendRaw(this, httpRemoteUri.Host, httpRemoteUri.Port, null, version, null, false, clientStream, tcpConnectionFactory); return; } //Now create the request disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, httpRemoteUri.Scheme == Uri.UriSchemeHttps?httpRemoteUri.Host : null, endPoint, connectRequestHeaders); } catch (Exception e) { ExceptionFunc(new Exception("Error whilst authorizing request", e)); } finally { if (!disposed) { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); } } }
private string CreateCertificate(long userid, string doc1, string doc2, string doc3, string isrcCode, string trackName, CoopArtistList coopArtists) { ClientInfo ci = null; using (Database db = new MySqlDatabase()) { ci = db.GetClientInfo(Util.UserId); } string ret = string.Empty; using (CertificateManager mgr = new CertificateManager(userid, string.Empty)) { mgr.AddTrackName(trackName); if (!string.IsNullOrEmpty(doc1)) mgr.AddDocument(doc1); if (!string.IsNullOrEmpty(doc2)) mgr.AddDocument(doc2); if (!string.IsNullOrEmpty(doc3)) mgr.AddDocument(doc3); if (!string.IsNullOrEmpty(isrcCode)) mgr.AddIsrcCode(isrcCode); foreach (CoopArtist coop in coopArtists) mgr.AddCoopArtist(coop.Artist, coop.Role); if (ci != null) mgr.Agent = ci.GetFullName(); mgr.CreateCertificate(string.Format("{0}.cer", DateTime.UtcNow.ToString("yyyyMMddHHmmss"))); ret = mgr.CertificateFilename; } return ret; }
/// <summary> /// This is called when client is aware of proxy /// So for HTTPS requests client would send CONNECT header to negotiate a secure tcp tunnel via proxy /// </summary> /// <param name="endPoint"></param> /// <param name="tcpClient"></param> /// <returns></returns> private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpClient) { bool disposed = false; var clientStream = new CustomBufferedStream(tcpClient.GetStream(), BufferSize); var clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); var clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); Uri httpRemoteUri; try { //read the first line HTTP command string httpCmd = await clientStreamReader.ReadLineAsync(); if (string.IsNullOrEmpty(httpCmd)) { return; } Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, out var version); httpRemoteUri = httpMethod == "CONNECT" ? new Uri("http://" + httpUrl) : new Uri(httpUrl); //filter out excluded host names bool excluded = false; if (endPoint.ExcludedHttpsHostNameRegex != null) { excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host)); } if (endPoint.IncludedHttpsHostNameRegex != null) { excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(httpRemoteUri.Host)); } ConnectRequest connectRequest = null; //Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request) if (httpMethod == "CONNECT") { connectRequest = new ConnectRequest { RequestUri = httpRemoteUri, OriginalUrl = httpUrl, HttpVersion = version, }; await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.Headers); var connectArgs = new TunnelConnectSessionEventArgs(BufferSize, endPoint, connectRequest, ExceptionFunc); connectArgs.ProxyClient.TcpClient = tcpClient; connectArgs.ProxyClient.ClientStream = clientStream; if (TunnelConnectRequest != null) { await TunnelConnectRequest.InvokeAsync(this, connectArgs, ExceptionFunc); } if (await CheckAuthorization(clientStreamWriter, connectArgs) == false) { if (TunnelConnectResponse != null) { await TunnelConnectResponse.InvokeAsync(this, connectArgs, ExceptionFunc); } return; } //write back successfull CONNECT response connectArgs.WebSession.Response = ConnectResponse.CreateSuccessfullConnectResponse(version); await clientStreamWriter.WriteResponseAsync(connectArgs.WebSession.Response); var clientHelloInfo = await SslTools.PeekClientHello(clientStream); bool isClientHello = clientHelloInfo != null; if (isClientHello) { connectRequest.ClientHelloInfo = clientHelloInfo; } if (TunnelConnectResponse != null) { connectArgs.IsHttpsConnect = isClientHello; await TunnelConnectResponse.InvokeAsync(this, connectArgs, ExceptionFunc); } if (!excluded && isClientHello) { httpRemoteUri = new Uri("https://" + httpUrl); connectRequest.RequestUri = httpRemoteUri; SslStream sslStream = null; try { sslStream = new SslStream(clientStream); string certName = HttpHelper.GetWildCardDomainName(httpRemoteUri.Host); var certificate = endPoint.GenericCertificate ?? CertificateManager.CreateCertificate(certName, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, SupportedSslProtocols, false); //HTTPS server created - we can now decrypt the client's traffic clientStream = new CustomBufferedStream(sslStream, BufferSize); clientStreamReader.Dispose(); clientStreamReader = new CustomBinaryReader(clientStream, BufferSize); clientStreamWriter = new HttpResponseWriter(clientStream, BufferSize); } catch { sslStream?.Dispose(); return; } if (await CanBeHttpMethod(clientStream)) { //Now read the actual HTTPS request line httpCmd = await clientStreamReader.ReadLineAsync(); } else { // It can be for example some Google (Cloude Messaging for Chrome) magic excluded = true; } } //Hostname is excluded or it is not an HTTPS connect if (excluded || !isClientHello) { //create new connection using (var connection = await GetServerConnection(connectArgs, true)) { if (isClientHello) { int available = clientStream.Available; if (available > 0) { //send the buffered data var data = BufferPool.GetBuffer(BufferSize); try { // clientStream.Available sbould be at most BufferSize because it is using the same buffer size await clientStream.ReadAsync(data, 0, available); await connection.StreamWriter.WriteAsync(data, 0, available, true); } finally { BufferPool.ReturnBuffer(data); } } var serverHelloInfo = await SslTools.PeekServerHello(connection.Stream); ((ConnectResponse)connectArgs.WebSession.Response).ServerHelloInfo = serverHelloInfo; } await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize, (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }, ExceptionFunc); } return; } } //Now create the request disposed = await HandleHttpSessionRequest(tcpClient, httpCmd, clientStream, clientStreamReader, clientStreamWriter, httpRemoteUri.Scheme == UriSchemeHttps?httpRemoteUri.Host : null, endPoint, connectRequest); } catch (IOException e) { ExceptionFunc(new Exception("Connection was aborted", e)); } catch (SocketException e) { ExceptionFunc(new Exception("Could not connect", e)); } catch (Exception e) { // is this the correct error message? ExceptionFunc(new Exception("Error whilst authorizing request", e)); } finally { if (!disposed) { Dispose(clientStream, clientStreamReader, clientStreamWriter, null); } } }
private string CreateCertificate(long userid, string password, string doc1, string doc2, string doc3, string isrcCode, string trackName, CoopArtistList coopArtists) { string ret = string.Empty; using (CertificateManager mgr = new CertificateManager(userid, password)) { mgr.AddTrackName(trackName); if (!string.IsNullOrEmpty(doc1)) mgr.AddDocument(doc1); if (!string.IsNullOrEmpty(doc2)) mgr.AddDocument(doc2); if (!string.IsNullOrEmpty(doc3)) mgr.AddDocument(doc3); if (!string.IsNullOrEmpty(isrcCode)) mgr.AddIsrcCode(isrcCode); foreach (CoopArtist coop in coopArtists) mgr.AddCoopArtist(coop.Artist, coop.Role); mgr.CreateCertificate(string.Format("{0}.cer", DateTime.UtcNow.ToString("yyyyMMddHHmmss"))); ret = mgr.CertificateFilename; } return ret; }
private string CreateCertificate(long userid, string password, string doc1, string doc2, string doc3) { string ret = string.Empty; using (CertificateManager mgr = new CertificateManager(userid, password)) { if (!string.IsNullOrEmpty(doc1)) mgr.AddDocument(doc1); if (!string.IsNullOrEmpty(doc2)) mgr.AddDocument(doc2); if (!string.IsNullOrEmpty(doc3)) mgr.AddDocument(doc3); mgr.CreateCertificate(string.Format("ID{0:D10}.cer", userid)); ret = mgr.CertificateFilename; } return ret; }