private async Task <Certificate> GetClientCertificate() { //await InstallCertificate(@"Assets\BigsbyRootCert.pfx", "RootCert"); await InstallCertificate(@"Assets\BigsbyClientCert.pfx", "ClientCert"); var names = new List <string>(); var certList = await CertificateStores.FindAllAsync(); var certs = certList.ToList(); var query = new CertificateQuery { FriendlyName = "ClientCert", }; var queried = (await CertificateStores.FindAllAsync(query)).ToList(); Certificate clientCert = null; // Always choose first enumerated certificate. Works so long as there is only one cert // installed and it's the right one. if ((null != certList) && (certList.Count > 0)) { clientCert = certList.First(); } return(queried.First()); }
private async void DeleteCerts_Click(object sender, RoutedEventArgs e) { DeleteCertsCompleted.Visibility = Visibility.Collapsed; try { IReadOnlyCollection <Certificate> certs = await CertificateStores.FindAllAsync(); StringBuilder builder = new StringBuilder(); // Append count. builder.AppendFormat("Count: {0}\r\n", certs.Count); foreach (var cert in certs) { builder.Append("--------------------\r\n"); builder.AppendFormat("Issuer: {0}\r\n", cert.Issuer); builder.AppendFormat("Subject: {0}\r\n", cert.Subject); string thumbprint = CryptographicBuffer.EncodeToHexString(CryptographicBuffer.CreateFromByteArray(cert.GetHashValue())); builder.AppendFormat("Thumbprint: {0}\r\n", thumbprint); // Not working: CertificateStores.IntermediateCertificationAuthorities.Delete(cert); } CertsBlock.Text = builder.ToString(); } catch (Exception ex) { DisplayException(ex); } DeleteCertsCompleted.Visibility = Visibility.Visible; }
async Task <Certificate> GetCertificate(MailboxAddress mailbox, bool sign) { var secure = mailbox as SecureMailboxAddress; var query = new CertificateQuery(); var now = DateTimeOffset.UtcNow; if (secure != null) { query.Thumbprint = HexDecode(secure.Fingerprint); } else { query.FriendlyName = mailbox.Address; } // TODO: filter on key usages foreach (var certificate in await CertificateStores.FindAllAsync(query)) { if (certificate.ValidFrom > now || certificate.ValidTo < now) { continue; } return(certificate); } return(null); }
private async Task SaveCertKeyPair() { var settings = ApplicationData.Current.RoamingSettings; PemWriter certWriter = new PemWriter(new StringWriter()); PemWriter keyWriter = new PemWriter(new StringWriter()); keyWriter.WriteObject(keyPair); keyWriter.Writer.Flush(); // Add the cert to the cert store. This changes the fingerprint so we read it // back before we serialize it so we have the up to date cert. await AddToWinCertStore(); // Read the modified cert from the cert store IEnumerable <Certificate> certificates = await CertificateStores.FindAllAsync(new CertificateQuery { FriendlyName = "Limelight-Client" }); cert = new X509CertificateParser().ReadCertificate(certificates.Single().GetCertificateBlob().AsStream()); certWriter.WriteObject(cert); certWriter.Writer.Flush(); // Line endings MUST be UNIX for the PC to accept the cert properly string keyStr = keyWriter.Writer.ToString(); keyStr = keyStr.Replace("\r\n", "\n"); string certStr = certWriter.Writer.ToString(); certStr = certStr.Replace("\r\n", "\n"); settings.Values["cert"] = certStr; settings.Values["key"] = keyStr; }
/// <summary> /// Load the cert/key pair from memory /// </summary> /// <returns>Value indicating success</returns> private async Task <bool> LoadCertKeyPair() { var settings = ApplicationData.Current.RoamingSettings; IEnumerable <Certificate> certificates = await CertificateStores.FindAllAsync(new CertificateQuery { FriendlyName = "Limelight-Client" }); if (settings.Values.ContainsKey("cert") && settings.Values.ContainsKey("key") && certificates.Count() > 0) { string certStr = ApplicationData.Current.RoamingSettings.Values["cert"].ToString(); string keyStr = ApplicationData.Current.RoamingSettings.Values["key"].ToString(); PemReader certReader = new PemReader(new StringReader(certStr)); PemReader keyReader = new PemReader(new StringReader(keyStr)); keyPair = (AsymmetricCipherKeyPair)keyReader.ReadObject(); cert = (X509Certificate)certReader.ReadObject(); pemCertBytes = System.Text.Encoding.UTF8.GetBytes(certStr); return(true); } // We didn't load the cert/key pair properly else { return(false); } }
/// <summary> /// Opens the certificate store. /// </summary> private CertificateStore OpenStore(bool readOnly, bool createAlways, bool throwIfNotExist) { // check for a valid name. if (String.IsNullOrEmpty(m_symbolicName)) { if (throwIfNotExist) { throw ServiceResultException.Create( StatusCodes.BadConfigurationError, "WindowsCertificateStore object has not been initialized properly."); } return(null); } CertificateStore hStore = CertificateStores.GetStoreByName(m_symbolicName); if (hStore == null) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Could not open the certificate store.\r\nType={0}, Name={1}", m_storeType, m_symbolicName); } else { // return the handle. return(hStore); } }
/// <summary> /// Gets the Xml as a string from the URL provided /// </summary> /// <returns>The server info XML as a string</returns> private async Task GetXml() { HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter(); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); IEnumerable <Certificate> certificates = await CertificateStores.FindAllAsync(new CertificateQuery { FriendlyName = "Limelight-Client" }); filter.ClientCertificate = certificates.Single(); client = new Windows.Web.Http.HttpClient(filter); Debug.WriteLine(uri); if (rawXmlString == null) { try { rawXmlString = await client.GetStringAsync(uri); } catch (Exception e) { Debug.WriteLine(e.Message); } Debug.WriteLine(rawXmlString); // Up to the caller to deal with exceptions resulting here this.rawXml = XDocument.Parse(rawXmlString); } }
private IReadOnlyList <Certificate> GetCertificatesFromWinRTStore(X509Certificate2 dotNetCertificate) { var query = new CertificateQuery { Thumbprint = dotNetCertificate.GetCertHash(), IncludeDuplicates = false }; return(CertificateStores.FindAllAsync(query).AsTask().GetAwaiter().GetResult()); }
private async void ButtonClientCert_Click(object sender, RoutedEventArgs args) { using (new ControlDisabler(ButtonClientCert)) { var picker = new FileOpenPicker { ViewMode = PickerViewMode.List, SuggestedStartLocation = PickerLocationId.Downloads, }; picker.FileTypeFilter.Add(".pfx"); var file = await picker.PickSingleFileAsync(); if (file == null) { return; } var fileStream = await file.OpenReadAsync(); var certData = await new StreamReader(fileStream.AsStreamForRead()).ReadToEndAsync(); if (certData.IndexOf("-----BEGIN PKCS12-----") == -1) { await new ErrorContentDialog("Selected file is not a base64 PKCS12 certificate. Please use a certificate created using `certtool --load-certificate user.cert.pem --load-privkey user.key.pem --to-p12 --outfile user.pfx`") .ShowAsync(); return; } var passwordDialog = new CertPasswordContentDialog(); if (await passwordDialog.ShowAsync() != ContentDialogResult.Primary) { return; } var pass = passwordDialog.Password; try { // we're importing it with a temporary name to see if it's valid; "real" importing doesn't happen until save is pressed. await CertificateEnrollmentManager.ImportPfxDataAsync(certData, pass, ExportOption.NotExportable, KeyProtectionLevel.NoConsent, InstallOptions.None, Constants.ClientCertTempName); } catch (Exception e) { await new ErrorContentDialog(e) .ShowAsync(); return; } var certs = await CertificateStores.FindAllAsync(new CertificateQuery { FriendlyName = Constants.ClientCertTempName }); if (!certs.Where(c => c.HasPrivateKey).Any()) { await new ErrorContentDialog("Selected certificate contains no private key. Please use a certificate created using `certtool --load-certificate user.cert.pem --load-privkey user.key.pem --to-p12 --outfile user.pfx`") .ShowAsync(); return; } // everything looks good, remember values for saving clientCert = certData; clientCertPassphrase = pass; } }
private async void DoGet(bool ignoreCertErrors, string key, string value, string friendlyName) { ResponseHeadersBlock.Text = String.Empty; CertErrorsBlock.Text = String.Empty; ResponseContentBlock.Text = String.Empty; HttpRequestMessage request = null; try { request = new HttpRequestMessage( HttpMethod.Get, new Uri(UriBox2.Text)); HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter(); if (ignoreCertErrors) { filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationFailure); } if (!String.IsNullOrEmpty(friendlyName)) { CertificateQuery query = new CertificateQuery(); query.FriendlyName = friendlyName; IReadOnlyCollection <Certificate> certs = await CertificateStores.FindAllAsync(query); filter.ClientCertificate = certs.ElementAt(0); } HttpClient client = new HttpClient(filter); if (!String.IsNullOrEmpty(key)) { client.DefaultRequestHeaders.Add(key, value); } HttpResponseMessage response = await client.SendRequestAsync(request); ResponseHeadersBlock.Text = response.ReasonPhrase; ResponseContentBlock.Text = await response.Content.ReadAsStringAsync(); } catch (Exception ex) { ResponseHeadersBlock.Text = ex.ToString(); // Something like: 'Untrusted, InvalidName, RevocationFailure' CertErrorsBlock.Text = String.Join( ", ", request.TransportInformation.ServerCertificateErrors); } }
private async Task <Certificate> FindCertificate(IDictionary <string, string> challengeData) { CertificateQuery query = new CertificateQuery(); IReadOnlyList <Certificate> certificates = null; string errMessage = null; if (challengeData.ContainsKey("CertAuthorities")) { errMessage = "Cert Authorities:" + challengeData["CertAuthorities"]; PlatformPlugin.Logger.Verbose(null, "Looking up certificate matching authorities:" + challengeData["CertAuthorities"]); string[] certAuthorities = challengeData["CertAuthorities"].Split(';'); foreach (var certAuthority in certAuthorities) { //reverse the tokenized string and replace "," with " + " string[] dNames = certAuthority.Split(new[] { "," }, StringSplitOptions.None); string distinguishedIssuerName = dNames[dNames.Length - 1]; for (int i = dNames.Length - 2; i >= 0; i--) { distinguishedIssuerName += " + " + dNames[i].Trim(); } query.IssuerName = distinguishedIssuerName; certificates = await CertificateStores.FindAllAsync(query); if (certificates.Count > 0) { break; } } } else { errMessage = "Cert Thumbprint:" + challengeData["CertThumbprint"]; PlatformPlugin.Logger.Verbose(null, "Looking up certificate matching thumbprint:" + challengeData["CertThumbprint"]); query.Thumbprint = HexStringToByteArray(challengeData["CertThumbprint"]); certificates = await CertificateStores.FindAllAsync(query); } if (certificates == null || certificates.Count == 0) { throw new AdalException(AdalError.DeviceCertificateNotFound, string.Format(AdalErrorMessage.DeviceCertificateNotFoundTemplate, errMessage)); } return(certificates[0]); }
private static void ValidateClientCertificate(X509Certificate2 certificate) { #if FEATURE_NETNATIVE var query = new CertificateQuery { Thumbprint = certificate.GetCertHash(), IncludeDuplicates = false, StoreName = "MY" }; if (CertificateStores.FindAllAsync(query).AsTask().GetAwaiter().GetResult().Count == 0) { throw ExceptionHelper.PlatformNotSupported("Certificate could not be found in the MY store."); } ; #endif // FEATURE_NETNATIVE }
public async Task <Certificate> GetAsync() { var certUsername = await _storage.GetTextAsync(StorageKeyNames.RegisteredCertSubject); if (certUsername != null) { var certs = await CertificateStores.FindAllAsync(new CertificateQuery() { IssuerName = "ca_msso", }); var cert = certs.FirstOrDefault(c => c.Subject == certUsername); return(cert); } return(null); }
/// <summary> /// Queries the device certificates and tries to retrieve one suitable for authenticating with the NAM. /// /// Will look for issuer "Schaeffler Group Sub CA01" (for newer devices) or just "Schaeffler*" (for older devices) /// </summary> /// <returns>A certificate suitable for NAM authentication.</returns> private Certificate GetDeviceCertificate() { // get certificate by issuer name Debug.WriteLine("Finding certificates..."); var certificateTask = CertificateStores.FindAllAsync().AsTask(); certificateTask.Wait(); // get certificates (with private keys) issued by Schaeffler, prefer newer ones var possibleCertificates = from cert in certificateTask.Result where cert.HasPrivateKey && (cert.Issuer == "Schaeffler Group Sub CA01" || cert.Issuer.StartsWith("Schaeffler")) orderby cert.ValidFrom select cert; var certificate = possibleCertificates.FirstOrDefault(); return(certificate); }
private async Task <Certificate> FindCertificateFromStoreAsync() { // Find the client certificate for authentication. If not found, it means it has not been installed. CertificateQuery query = new CertificateQuery(); query.IssuerName = ClientCertIssuerName; query.FriendlyName = ClientCertFriendlyName; IReadOnlyList <Certificate> certs = await CertificateStores.FindAllAsync(query); if (certs.Count == 0) { return(null); } // This sample installs only one certificate, so if we find one, it must be ours. return(certs[0]); }
/// <summary> /// Returns a certificate with a given name /// </summary> /// <param name="certName">Name of cert we are going to look for</param> /// <returns>null if the cert isn't there, otherwise the cert that was found.</returns> public static async Task <Certificate> FindCert(string certName) { // Work around for the TplEventListener not working correctly. // https://social.msdn.microsoft.com/Forums/windowsapps/en-US/3e505e04-7f30-4313-aa47-275eaef333dd/systemargumentexception-use-of-undefined-keyword-value-1-for-event-taskscheduled-in-async?forum=wpdevelop await Task.Delay(1); // Do the CERT query var query = new CertificateQuery(); query.FriendlyName = certName; var certificates = await CertificateStores.FindAllAsync(query); if (certificates.Count != 1) { return(null); } return(certificates[0]); }
/// <summary> /// Gets the Xml as a string from the URL provided /// </summary> /// <returns>The server info XML as a string</returns> private async Task GetXml() { // Return if we've already been here if (ranQuery) { return; } else { ranQuery = true; } HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter(); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); // Allow the crypto provider to generate the cert if needed await new WindowsCryptoProvider().InitializeCryptoProviderKeys(); IEnumerable <Certificate> certificates = await CertificateStores.FindAllAsync(new CertificateQuery { FriendlyName = "Limelight-Client" }); filter.ClientCertificate = certificates.Single(); client = new Windows.Web.Http.HttpClient(filter); Debug.WriteLine(uri); try { rawXmlString = await client.GetStringAsync(uri); } catch (Exception e) { Debug.WriteLine(e.Message); return; } Debug.WriteLine(rawXmlString); // Up to the caller to deal with exceptions resulting here this.rawXml = XDocument.Parse(rawXmlString); }
/// <summary cref="ICertificateStore.Enumerate()" /> public async Task <X509Certificate2Collection> Enumerate() { X509Certificate2Collection certificates = new X509Certificate2Collection(); // get the certificates. IReadOnlyList <Certificate> list = await CertificateStores.FindAllAsync(); for (int ii = 0; ii < list.Count; ii++) { // add the certificate. IBuffer buffer = list[ii].GetCertificateBlob(); byte[] cert = new byte[buffer.Length]; CryptographicBuffer.CopyToByteArray(buffer, out cert); X509Certificate2 certificate = new X509Certificate2(cert); certificates.Add(certificate); } return(certificates); }
private async void Button_Click(object sender, RoutedEventArgs e) { try { CertificateQuery q = new CertificateQuery(); q.HardwareOnly = true; q.IssuerName = "Citizen CA"; IReadOnlyList <Certificate> certs = await CertificateStores.FindAllAsync(q); FileOpenPicker openPicker = new FileOpenPicker(); openPicker.ViewMode = PickerViewMode.Thumbnail; openPicker.SuggestedStartLocation = PickerLocationId.ComputerFolder; openPicker.FileTypeFilter.Add(".p12"); openPicker.FileTypeFilter.Add(".acc-p12"); StorageFile file = await openPicker.PickSingleFileAsync(); } catch { } }
private void loadCertificates() { certificatesListCB.Items.Clear(); var task = CertificateStores.FindAllAsync(); task.AsTask().Wait(); var certlist = task.GetResults(); LoadCertList(certlist); if (certificatesListCB.Items.Count == 0) { certificatesListCB.Items.Add(NOCERTS); certificatesListCB.IsEnabled = false; } else { certificatesListCB.IsEnabled = true; } certificatesListCB.SelectedIndex = 0; }
private async void QueryCerts_Click(object sender, RoutedEventArgs e) { QueryCertsCompleted.Visibility = Visibility.Collapsed; try { IReadOnlyCollection <Certificate> certs = await CertificateStores.FindAllAsync(); StringBuilder builder = new StringBuilder(); // Append count. builder.AppendFormat("Count: {0}\r\n", certs.Count); foreach (var cert in certs) { builder.Append("--------------------\r\n"); builder.AppendFormat("EnhancedKeyUsages: {0}\r\n", String.Join(", ", cert.EnhancedKeyUsages)); builder.AppendFormat("FriendlyName: {0}\r\n", cert.FriendlyName); builder.AppendFormat("HasPrivateKey: {0}\r\n", cert.HasPrivateKey); builder.AppendFormat("IsStronglyProtected: {0}\r\n", cert.IsStronglyProtected); builder.AppendFormat("Issuer: {0}\r\n", cert.Issuer); builder.AppendFormat("SerialNumber: {0}\r\n", BitConverter.ToString(cert.SerialNumber)); builder.AppendFormat("Subject: {0}\r\n", cert.Subject); builder.AppendFormat("ValidFrom: {0}\r\n", cert.ValidFrom); builder.AppendFormat("ValidTo: {0}\r\n", cert.ValidTo); string thumbprint = CryptographicBuffer.EncodeToHexString(CryptographicBuffer.CreateFromByteArray(cert.GetHashValue())); builder.AppendFormat("Thumbprint: {0}\r\n", thumbprint); } CertsBlock.Text = builder.ToString(); } catch (Exception ex) { DisplayException(ex); } QueryCertsCompleted.Visibility = Visibility.Visible; }
/// <summary> /// Gets the private key for the certificate matching the specified selector. /// </summary> /// <remarks> /// Gets the private key for the first certificate that matches the specified selector. /// </remarks> /// <returns>The private key on success; otherwise <c>null</c>.</returns> /// <param name="selector">The search criteria for the private key.</param> protected virtual async Task <AsymmetricKeyParameter> GetPrivateKeyAsync(IX509Selector selector) { // first we need to find the certificate... var match = selector as X509CertStoreSelector; var query = new CertificateQuery(); if (match == null) { return(null); } if (match.Certificate != null) { query.Thumbprint = HexDecode(match.Certificate.GetFingerprint()); } if (match.Issuer != null) { query.IssuerName = match.Issuer.ToString(); } var certificates = await CertificateStores.FindAllAsync(query); var certificate = certificates.FirstOrDefault(); if (certificate == null) { return(null); } // now get the key // TODO: what hash algo/padding do we want? does it matter? var key = await PersistedKeyProvider.OpenKeyPairFromCertificateAsync(certificate, HashAlgorithmNames.Sha256, CryptographicPadding.RsaPkcs1V15); var buffer = key.Export(CryptographicPrivateKeyBlobType.Pkcs1RsaPrivateKey); return(LoadPrivateKey(buffer)); }
/// <summary> /// Finds a certificate in the store. /// </summary> /// <param name="hStore">The handle for the store to search.</param> /// <param name="thumbprint">The thumbprint of the certificate to find.</param> /// <returns>The context for the matching certificate.</returns> private static async Task <IBuffer> FindCertificate(string thumbprint) { byte[] byteThumbprint = new byte[thumbprint.Length]; for (int i = 0; i < thumbprint.Length; i++) { byteThumbprint[i] = (byte)thumbprint[i]; } CertificateQuery query = new CertificateQuery(); query.Thumbprint = byteThumbprint; IReadOnlyList <Certificate> list = await CertificateStores.FindAllAsync(query); if (list.Count > 0) { return(list[0].GetCertificateBlob()); } else { return(null); } }
protected override async void OnNavigatedTo(NavigationEventArgs e) { IReadOnlyList <Certificate> certificates = await CertificateStores.FindAllAsync(); foreach (Certificate certificate in certificates) { var item = new ListViewItem { Content = $"Subject: {certificate.Subject}, Serial Number: {CryptographicBuffer.EncodeToHexString(CryptographicBuffer.CreateFromByteArray(certificate.SerialNumber))}", Tag = certificate }; if (certificate.IsPerUser) { UserCertificateListView.Items.Add(item); } else { AppCertificateListView.Items.Add(item); } } }
/// <summary cref="ICertificateStore.Add(X509Certificate2)" /> public async Task Add(X509Certificate2 certificate) { if (certificate == null) { throw new ArgumentNullException("certificate"); } // check for existing certificate. byte[] thumbprint = new byte[certificate.Thumbprint.Length]; for (int i = 0; i < certificate.Thumbprint.Length; i++) { thumbprint[i] = (byte)certificate.Thumbprint[i]; } CertificateQuery query = new CertificateQuery(); query.Thumbprint = thumbprint; IReadOnlyList <Certificate> pCertContext = await CertificateStores.FindAllAsync(query); if (pCertContext.Count != 0) { throw ServiceResultException.Create( StatusCodes.BadUnexpectedError, "Certificate is already in the store.\r\nType={0}, Name={1}, Subject={2}", m_storeType, m_symbolicName, certificate.Subject); } lock (m_lock) { // add certificate. CertificateFactory.AddCertificateToWindowsStore( m_storeType == WindowsStoreType.LocalMachine, m_symbolicName, certificate); } }
private async Task EnsureDeleteOldCertificates() { UserCertificateStore certificateStore = CertificateStores.GetUserStoreByName(StandardCertificateStoreNames.Personal); var certificateFinder = await CertificateStores.FindAllAsync(new CertificateQuery() { IncludeDuplicates = true, IncludeExpiredCertificates = true }); foreach (Certificate oldCertificate in certificateFinder) { if (oldCertificate.Issuer.StartsWith("Azure IoT Edison Intermediate")) { try { await certificateStore.RequestDeleteAsync(oldCertificate); } catch (Exception e) { DebugHelper.LogWarning($"An old certificate could not be deleted: {e.Message}"); } } } }
/// <summary cref="ICertificateStore.Delete(string)" /> public async Task <bool> Delete(string thumbprint) { // open store. CertificateStore hStore = OpenStore(false, false, false); if (hStore == null) { return(false); } // find certificate. byte[] byteThumbprint = new byte[thumbprint.Length]; for (int i = 0; i < thumbprint.Length; i++) { byteThumbprint[i] = (byte)thumbprint[i]; } CertificateQuery query = new CertificateQuery(); query.Thumbprint = byteThumbprint; IReadOnlyList <Certificate> list = await CertificateStores.FindAllAsync(query); // delete certificate. if (list.Count > 0) { lock (m_lock) { hStore.Delete(list[0]); } return(true); } else { return(false); } }
private void EnumerateCertificateList() { CertificateList.Visibility = Visibility.Visible; CertificateList.Items.Clear(); var task = CertificateStores.FindAllAsync(); task.AsTask().Wait(); var certlist = task.GetResults(); LoadCertList(certlist); if (CertificateList.Items.Count == 0) { VerifyCert.IsEnabled = false; RunSample.IsEnabled = false; CertificateList.Items.Add("No certificates found"); } else { VerifyCert.IsEnabled = true; RunSample.IsEnabled = true; } CertificateList.SelectedIndex = 0; }
public override async Task PairAsync(string ipAddress, TextBox outputTextBox) { // Create SHA256 hash digest. This is not supported by server version < 7 // (need to use SHA1 for those cases) but that doesn't really matter right now. IDigest hashAlgorithm = new Sha256Digest(); int hashDigestSize = hashAlgorithm.GetDigestSize(); // Create and salt pin byte[] salt = this.GenerateRandomBytes(16); string pin = GenerateRandomPin(); byte[] saltAndPin = SaltPin(salt, pin); // Asymmetric key pair RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator(); keyPairGenerator.Init(new KeyGenerationParameters(this.SecureRandom, 2048)); AsymmetricCipherKeyPair keyPair = keyPairGenerator.GenerateKeyPair(); // Certificate issuer and name X509Name name = new X509Name("CN=NVIDIA GameStream Client"); // Certificate serial number byte[] serialBytes = this.GenerateRandomBytes(8); BigInteger serial = new BigInteger(serialBytes).Abs(); // Expires in 20 years DateTime now = DateTime.UtcNow; DateTime expiration = now.AddYears(20); X509V3CertificateGenerator generator = new X509V3CertificateGenerator(); generator.SetSubjectDN(name); generator.SetIssuerDN(name); generator.SetSerialNumber(serial); generator.SetNotBefore(now); generator.SetNotAfter(expiration); generator.SetPublicKey(keyPair.Public); BouncyCastleX509Certificate certificate = generator.Generate( new Asn1SignatureFactory("SHA1WithRSA", keyPair.Private)); // Create PKCS12 certificate bytes. Pkcs12Store store = new Pkcs12Store(); X509CertificateEntry certificateEntry = new X509CertificateEntry(certificate); string friendlyName = "Moonlight Xbox"; string password = "******"; store.SetCertificateEntry(friendlyName, certificateEntry); store.SetKeyEntry( friendlyName, new AsymmetricKeyEntry(keyPair.Private), new X509CertificateEntry[] { certificateEntry }); string pfxData; using (MemoryStream memoryStream = new MemoryStream(512)) { store.Save(memoryStream, password.ToCharArray(), this.SecureRandom); pfxData = CryptographicBuffer.EncodeToBase64String(memoryStream.ToArray().AsBuffer()); } await CertificateEnrollmentManager.ImportPfxDataAsync( pfxData, password, ExportOption.NotExportable, KeyProtectionLevel.NoConsent, InstallOptions.DeleteExpired, friendlyName); // Read the UWP cert from the cert store Certificate uwpCertificate = (await CertificateStores.FindAllAsync( new CertificateQuery { FriendlyName = friendlyName }))[0]; string keyString; using (StringWriter keyWriter = new StringWriter()) { PemWriter pemWriter = new PemWriter(keyWriter); pemWriter.WriteObject(keyPair); keyString = keyWriter.ToString(); // Line endings must be UNIX style for GFE to accept the certificate. keyString = keyString.Replace(Environment.NewLine, "\n"); } string certString; using (StringWriter certWriter = new StringWriter()) { PemWriter pemWriter = new PemWriter(certWriter); pemWriter.WriteObject(certificate); certString = certWriter.ToString(); // Line endings must be UNIX style for GFE to accept the certificate. certString = certString.Replace(Environment.NewLine, "\n"); } byte[] pemCertBytes = Encoding.UTF8.GetBytes(certString); byte[] uniqueId = GenerateRandomBytes(8); // Create the HTTP client. HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter(); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.Untrusted); filter.IgnorableServerCertificateErrors.Add(ChainValidationResult.InvalidName); filter.ClientCertificate = uwpCertificate; HttpClient httpClient = new HttpClient(filter); // Unpair before doing anything else in this test app. string uriString = string.Format( "http://{0}:47989/unpair?uniqueid={1}&uuid={2}", ipAddress, BytesToHex(uniqueId), Guid.NewGuid().ToString("N")); using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(uriString))) { using (HttpResponseMessage response = await httpClient.SendRequestAsync(request)) { outputTextBox.Text = $"Unpair status code: {response.StatusCode}\n"; string responseContent = await response.Content.ReadAsStringAsync(); outputTextBox.Text += responseContent + "\n"; } } await Task.Delay(2000); outputTextBox.Text = $"Enter pin: {pin}"; // Get server certificate. // TODO: Call should have no timeout because it requires the user to enter a pin. PairResponse pairResponse = null; uriString = string.Format( "http://{0}:47989/pair?uniqueid={1}&uuid={2}&devicename=roth&updateState=1&phrase=getservercert&salt={3}&clientcert={4}", ipAddress, BytesToHex(uniqueId), Guid.NewGuid().ToString("N"), BytesToHex(salt), BytesToHex(pemCertBytes)); using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(uriString))) { using (HttpResponseMessage response = await httpClient.SendRequestAsync(request)) { outputTextBox.Text = $"Get server cert status code: {response.StatusCode}\n"; string responseContent = await response.Content.ReadAsStringAsync(); outputTextBox.Text += responseContent + "\n"; using (StringReader reader = new StringReader(responseContent)) { XmlSerializer serializer = new XmlSerializer(typeof(PairResponse)); pairResponse = serializer.Deserialize(new StringReader(responseContent)) as PairResponse; } } } if (pairResponse == null || pairResponse.Paired != 1) { outputTextBox.Text += "Pairing failed.\n"; return; } if (string.IsNullOrEmpty(pairResponse.PlainCert)) { outputTextBox.Text += "Pairing already in progress.\n"; return; } // Parse server certificate byte[] serverCertBytes = HexToBytes(pairResponse.PlainCert); BouncyCastleX509Certificate serverCertificate = new X509CertificateParser().ReadCertificate(serverCertBytes); // Hash the salt and pin and use it to generate an AES key. byte[] hashedSaltAndPin = HashData(hashAlgorithm, saltAndPin); ICipherParameters aesKey = GenerateCipherKey(hashedSaltAndPin); // Generate a random challenge and encrypt it using AES. byte[] challenge = GenerateRandomBytes(16); byte[] encryptedChallenge = DoAesCipher(true, aesKey, challenge); await Task.Delay(2000); // Send the encrypted challenge to the server. // TODO: Call should have a timeout. uriString = string.Format( "http://{0}:47989/pair?uniqueid={1}&uuid={2}&devicename=roth&updateState=1&clientchallenge={3}", ipAddress, BytesToHex(uniqueId), Guid.NewGuid().ToString("N"), BytesToHex(encryptedChallenge)); using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(uriString))) { using (HttpResponseMessage response = await httpClient.SendRequestAsync(request)) { outputTextBox.Text = $"Send challenge status code: {response.StatusCode}\n"; string responseContent = await response.Content.ReadAsStringAsync(); outputTextBox.Text += responseContent + "\n"; using (StringReader reader = new StringReader(responseContent)) { XmlSerializer serializer = new XmlSerializer(typeof(PairResponse)); pairResponse = serializer.Deserialize(new StringReader(responseContent)) as PairResponse; } } } if (pairResponse == null || pairResponse.Paired != 1) { outputTextBox.Text += "Pairing failed.\n"; return; } // Decode the server's response and subsequent challenge. byte[] encryptedServerChallengeResponse = HexToBytes(pairResponse.ChallengeResponse); byte[] decryptedServerChallengeResponse = DoAesCipher(false, aesKey, encryptedServerChallengeResponse); byte[] serverResponse = new byte[hashDigestSize]; byte[] serverChallenge = new byte[16]; Array.Copy(decryptedServerChallengeResponse, serverResponse, hashDigestSize); Array.Copy(decryptedServerChallengeResponse, hashDigestSize, serverChallenge, 0, 16); // Using another 16 byte secret, compute a challenge response hash using the secret, // our certificate signature, and the challenge. byte[] clientSecret = GenerateRandomBytes(16); byte[] challengeResponseHash = HashData( hashAlgorithm, ConcatenateByteArrays(serverChallenge, certificate.GetSignature(), clientSecret)); byte[] encryptedChallengeResponse = DoAesCipher(true, aesKey, challengeResponseHash); await Task.Delay(2000); // Send the challenge response to the server. // TODO: Call should have a timeout. uriString = string.Format( "http://{0}:47989/pair?uniqueid={1}&uuid={2}&devicename=roth&updateState=1&serverchallengeresp={3}", ipAddress, BytesToHex(uniqueId), Guid.NewGuid().ToString("N"), BytesToHex(encryptedChallengeResponse)); using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(uriString))) { using (HttpResponseMessage response = await httpClient.SendRequestAsync(request)) { outputTextBox.Text = $"Send challenge response status code: {response.StatusCode}\n"; string responseContent = await response.Content.ReadAsStringAsync(); outputTextBox.Text += responseContent + "\n"; using (StringReader reader = new StringReader(responseContent)) { XmlSerializer serializer = new XmlSerializer(typeof(PairResponse)); pairResponse = serializer.Deserialize(new StringReader(responseContent)) as PairResponse; } } } if (pairResponse == null || pairResponse.Paired != 1) { outputTextBox.Text += "Pairing failed.\n"; // TODO: Unpair here by calling http://<blah>/unpair?uniqueid={1}&uuid={2}. return; } // Get the server's signed secret. byte[] serverSecretResponse = HexToBytes(pairResponse.PairingSecret); byte[] serverSecret = new byte[16]; byte[] serverSignature = new byte[256]; Array.Copy(serverSecretResponse, serverSecret, serverSecret.Length); Array.Copy(serverSecretResponse, serverSecret.Length, serverSignature, 0, serverSignature.Length); if (!VerifySignature(serverSecret, serverSignature, serverCertificate.GetPublicKey())) { outputTextBox.Text += "Pairing failed.\n"; // TODO: Unpair as above. return; } // Ensure the server challenge matched what we expected (the PIN was correct). byte[] serverChallengeResponseHash = HashData( hashAlgorithm, ConcatenateByteArrays( challenge, serverCertificate.GetSignature(), serverSecret)); if (!serverChallengeResponseHash.SequenceEqual(serverResponse)) { outputTextBox.Text += "Pairing failed due to wrong pin.\n"; // TODO: Unpair as above. return; } await Task.Delay(2000); // Send the server our signed secret // TODO: Call should have a timeout. byte[] signedSecret = SignData(clientSecret, keyPair.Private); byte[] clientPairingSecret = ConcatenateByteArrays( clientSecret, signedSecret); uriString = string.Format( "http://{0}:47989/pair?uniqueid={1}&uuid={2}&devicename=roth&updateState=1&clientpairingsecret={3}", ipAddress, BytesToHex(uniqueId), Guid.NewGuid().ToString("N"), BytesToHex(clientPairingSecret)); using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(uriString))) { using (HttpResponseMessage response = await httpClient.SendRequestAsync(request)) { outputTextBox.Text = $"Send client pairing secret status code: {response.StatusCode}\n"; string responseContent = await response.Content.ReadAsStringAsync(); outputTextBox.Text += responseContent + "\n"; using (StringReader reader = new StringReader(responseContent)) { XmlSerializer serializer = new XmlSerializer(typeof(PairResponse)); pairResponse = serializer.Deserialize(new StringReader(responseContent)) as PairResponse; } } } if (pairResponse == null || pairResponse.Paired != 1) { outputTextBox.Text += "Pairing failed.\n"; // TODO: Unpair as above. return; } await Task.Delay(2000); // Do the initial challenge (seems neccessary for us to show as paired). // TODO: Call should have a timeout. uriString = string.Format( "https://{0}:47984/pair?uniqueid={1}&uuid={2}&devicename=roth&updateState=1&phrase=pairchallenge", ipAddress, BytesToHex(uniqueId), Guid.NewGuid().ToString("N")); using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, new Uri(uriString))) { using (HttpResponseMessage response = await httpClient.SendRequestAsync(request)) { outputTextBox.Text = $"Send pair challenge status code: {response.StatusCode}\n"; string responseContent = await response.Content.ReadAsStringAsync(); outputTextBox.Text += responseContent + "\n"; using (StringReader reader = new StringReader(responseContent)) { XmlSerializer serializer = new XmlSerializer(typeof(PairResponse)); pairResponse = serializer.Deserialize(new StringReader(responseContent)) as PairResponse; } } } if (pairResponse == null || pairResponse.Paired != 1) { outputTextBox.Text += "Pairing failed.\n"; // TODO: Unpair as above. return; } await Task.Delay(2000); outputTextBox.Text = "Pairing succeeded!\n"; }
private async void ButtonSync_Click(object sender, RoutedEventArgs args) { using (new ControlDisabler(ButtonSync)) { var settings = Storage.Settings.Load(); if (!settings.Valid) { await new ErrorContentDialog("Sync not configured, please adjust settings.").ShowAsync(); return; } var certs = await CertificateStores.FindAllAsync(new CertificateQuery { FriendlyName = Constants.ClientCertFriendlyName }); var cert = certs.Where(c => c.HasPrivateKey).FirstOrDefault(); if (cert == null) { await new ErrorContentDialog("No client certificate configured, please adjust settings.").ShowAsync(); return; } var syncBacklog = await Storage.SqliteStorage.GetSyncBacklog(); // FIXME: this whole initial sync special case is very ugly - surely there's an easier way to do it in an idempotent way? var isInitialSync = syncBacklog.SyncKey == null; var initialSyncPhase = 0; // TODO: Handle exceptions while (true) { Protocol.Message response; using (var stream = new StreamSocket()) { stream.Control.ClientCertificate = cert; try { await stream.ConnectAsync(settings.Endpoint, SocketProtectionLevel.Tls12).AsTask().WithTimeout(Constants.SyncTimeoutMillis); // TODO: listen for response in background, see https://docs.microsoft.com/en-us/windows/uwp/launch-resume/support-your-app-with-background-tasks & https://docs.microsoft.com/en-us/windows/uwp/networking/network-communications-in-the-background var request = new Protocol.Message(new Dictionary <string, string> { { "type", "sync" }, { "org", settings.Organization }, { "user", settings.User }, { "key", settings.Key }, { "client", "WPTaskClient 0.1.0" }, { "protocol", "v1" }, }, isInitialSync && initialSyncPhase == 0 ? "" : syncBacklog.ToString()); // must not send local changes on initial sync await request.ToStream(stream.OutputStream.AsStreamForWrite()).WithTimeout(Constants.SyncTimeoutMillis); response = await Protocol.Message.FromStream(stream.InputStream.AsStreamForRead()).WithTimeout(Constants.SyncTimeoutMillis); } catch (TimeoutException) { await new ErrorContentDialog("timeout during sync").ShowAsync(); return; } } if (!response.Header.TryGetValue("code", out string code)) { await new ErrorContentDialog("No status in sync response!").ShowAsync(); return; } if (code == "201") // no change { return; } if (code != "200") { if (response.Header.TryGetValue("status", out string status)) { await new ErrorContentDialog(string.Format("Sync response had status {0}: {1}!", code, status)).ShowAsync(); } else { await new ErrorContentDialog(string.Format("Sync response had status {0}!", code)).ShowAsync(); } return; } string syncKey = null; try { foreach (var line in response.Body.Split('\n')) // I wish there was an easy lazy way to do this (a lazy lazy way, if you will) { if (line.Length == 0) { continue; } if (line[0] == '{') { Data.Task task; try { task = Data.Task.FromJson(JsonObject.Parse(line)); } catch (Exception e) { await new ErrorContentDialog(e).ShowAsync(); return; } // TODO: Batch await Storage.SqliteStorage.UpsertTask(task, false); } else if (GUIDRegex.IsMatch(line)) { syncKey = line; } else { await new ErrorContentDialog(string.Format("unexpected line: {0}", line)).ShowAsync(); return; } } } finally { await UpdateTasks(); } if (syncKey == null) { await new ErrorContentDialog("Sync response contained no sync key!").ShowAsync(); return; } await Storage.SqliteStorage.SetSyncKey(syncKey); syncBacklog.SyncKey = syncKey; if (isInitialSync && initialSyncPhase < 1) { initialSyncPhase++; // run again, this time syncing our initial local changes } else { break; } } } }