Imports System.IO Imports System.Net Imports System.Security.Cryptography.X509Certificates Imports System.Text Imports DigitalData.Modules.Logging Imports Newtonsoft.Json Public Class GraphQLInterface Private _logConfig As LogConfig Private _logger As Logger Private _baseUrl As String Private _userEmail As String Private _userPassword As String Private _certificate As X509Certificate2 Private _cookieJar As CookieContainer Private _Encoding As New UTF8Encoding Private Const MAX_COOKIE_SIZE As Integer = 32768 Private Const MAX_COOKIE_COUNT As Integer = 300 Private Const MAX_COOKIE_COUNT_PER_DOMAIN As Integer = 20 Public Property Proxy As WebProxy Public Property Credentials As NetworkCredential Public Event LogRequested As EventHandler(Of String) Public ReadOnly Property Certificate As X509Certificate2 Get Return _certificate End Get End Property Public Sub New(LogConfig As LogConfig, BaseUrl As String, Email As String, Password As String, CertificateFingerprint As String) Try _logConfig = LogConfig _logger = LogConfig.GetLogger() _baseUrl = BaseUrl _userEmail = Email _userPassword = Password Dim oStoreNames As New List(Of StoreName) From {StoreName.Root, StoreName.My} Dim oStoreLocations As New List(Of StoreLocation) From {StoreLocation.CurrentUser, StoreLocation.LocalMachine} Dim oCertificate As X509Certificate2 = Nothing For Each oStoreLocation In oStoreLocations _logger.Debug("Checking Stores in Location [{0}]", oStoreLocation.ToString) For Each oStoreName In oStoreNames oCertificate = FindCertificateByFingerprint(oStoreLocation, oStoreName, CertificateFingerprint, False) If oCertificate IsNot Nothing Then _logger.Info("Certificate found in Store [{0}]/[{1}]!", oStoreName.ToString, oStoreLocation.ToString) Exit For End If Next If oCertificate IsNot Nothing Then Exit For End If Next If oCertificate Is Nothing Then _logger.Warn("Certificate could not be found! Exiting.") Exit Sub End If _certificate = oCertificate Catch ex As Exception _logger.Error(ex) End Try End Sub Private Function FindCertificateByFingerprint(pLocation As StoreLocation, pStoreName As StoreName, pFingerprint As String, pValidOnly As Boolean) As X509Certificate2 Try Dim oStore As New X509Store(pStoreName, pLocation) Dim oLocation As String = pLocation.ToString _logger.Info("Opening Store [{0}]/[{1}]..", oLocation, oStore.Name) oStore.Open(OpenFlags.ReadOnly) _logger.Info("Available Certificates in Store [{0}]/[{1}]: [{2}]", oLocation, oStore.Name, oStore.Certificates.Count) For Each oCert In oStore.Certificates _logger.Debug("FriendlyName: {0}", oCert.FriendlyName) _logger.Debug("IssuerName: {0}", oCert.IssuerName.Name) _logger.Debug("SubjectName: {0}", oCert.SubjectName.Name) _logger.Debug("Fingerprint: {0}", oCert.Thumbprint) Next _logger.Debug("Looking for Certificate with Fingerprint [{0}]", pFingerprint) Dim oFoundCerts = oStore.Certificates.Find(X509FindType.FindByThumbprint, pFingerprint, pValidOnly) _logger.Debug("Closing store..") oStore.Close() If oFoundCerts.Count = 0 Then _logger.Debug("Certificate with Fingerprint [{0}] not found in Store [{1}]/[{2}]", pFingerprint, oLocation, oStore.Name) Return Nothing End If Return oFoundCerts.Item(0) Catch ex As Exception _logger.Warn("Unexpected error while searching for certificate with Fingerprint [{0}].", pFingerprint) _logger.Error(ex) Return Nothing End Try End Function Public Sub SaveCookies(Cookie As Cookie) GetCookies().Add(Cookie) End Sub Public Function Login() As HttpWebResponse Try RaiseEvent LogRequested(Me, $"Login started with user: [{_userEmail}]") Dim oLoginData As New LoginData() With {.email = _userEmail, .token = _userPassword} Dim oBytes As Byte() = ToBytes(JsonConvert.SerializeObject(oLoginData)) Dim oRequest As HttpWebRequest = GetRequest("/login", oBytes) RaiseEvent LogRequested(Me, $"Sending HTTP request to GraphQL") RaiseEvent LogRequested(Me, $"Request Host: [{oRequest.Host}]") RaiseEvent LogRequested(Me, $"Request Address: [{oRequest.Address}]") RaiseEvent LogRequested(Me, $"Request Method: [{oRequest.Method}]") RaiseEvent LogRequested(Me, $"Request ContentType: [{oRequest.ContentType}]") Using oStream = oRequest.GetRequestStream() oStream.Write(oBytes, 0, oBytes.Length) End Using Return oRequest.GetResponse() Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Public Function Logout() As HttpWebResponse Try Dim oLogoutData As New LogoutData() With {.email = _userEmail} Dim oBytes As Byte() = ToBytes(JsonConvert.SerializeObject(oLogoutData)) Dim oRequest As HttpWebRequest = GetRequest("/logout", oBytes) Using stream = oRequest.GetRequestStream() stream.Write(oBytes, 0, oBytes.Length) End Using Return oRequest.GetResponse() Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Public Function GetData(Query As String, OperationName As String) As HttpWebResponse Try Dim oQueryData As New QueryData() With { .operationName = OperationName, .query = Query, .variables = New Object } Dim oJson = JsonConvert.SerializeObject(oQueryData) Dim oBytes = ToBytes(oJson) Dim oRequest = GetRequest("/graphql", oBytes) Using stream = oRequest.GetRequestStream() stream.Write(oBytes, 0, oBytes.Length) End Using Return oRequest.GetResponse() Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Public Function ReadJSONPathFragmented(pObject As Linq.JObject, pJsonPath As String) Dim oSplitPath As List(Of String) = pJsonPath.Split(".").ToList() Dim oCurrentPath As String = String.Empty For Each oPart In oSplitPath If oCurrentPath = String.Empty Then oCurrentPath = oPart Else oCurrentPath &= "." & oPart End If _logger.Debug("Selecting Path Fragment [{0}]", oCurrentPath) Try pObject.SelectToken(oCurrentPath, errorWhenNoMatch:=True) Catch ex As Exception _logger.Warn("Path Fragment [{0}] did not return a valid token", oCurrentPath) Return False End Try Next Return True End Function Private Function GetRequest(Url As String, PostData As Byte()) As HttpWebRequest Try ' Set supported TLS versions for WebRequest ' Source: https://stackoverflow.com/questions/10822509/the-request-was-aborted-could-not-create-ssl-tls-secure-channel 'SetSecurityOptions() 'SetSecurityOptionsInsecure() 'SetSecurityOptionsModern() Dim oRequest As HttpWebRequest = WebRequest.Create($"{_baseUrl}{Url}") oRequest.Method = "POST" oRequest.ContentType = "application/json" oRequest.ContentLength = PostData.Length oRequest.ClientCertificates.Add(_certificate) oRequest.CookieContainer = GetCookies() oRequest.Proxy = Nothing If Proxy Is Nothing Then oRequest.Proxy = Nothing Else oRequest.Proxy = Proxy oRequest.Credentials = Credentials End If Return oRequest Catch ex As Exception _logger.Error(ex) Throw ex End Try End Function Private Sub SetSecurityOptions() ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12 End Sub Private Sub SetSecurityOptionsInsecure() ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls Or SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12 Or SecurityProtocolType.Ssl3 End Sub Private Sub SetSecurityOptionsModern() ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 End Sub Private Function GetCookies() As CookieContainer If _cookieJar Is Nothing Then _cookieJar = New CookieContainer(MAX_COOKIE_COUNT, MAX_COOKIE_COUNT_PER_DOMAIN, MAX_COOKIE_SIZE) End If Return _cookieJar End Function Private Function ToBytes(Str As String) As Byte() Return _Encoding.GetBytes(Str) End Function End Class