/// <summary> /// Form loaded event /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ShowSecretKeyForm_Load(object sender, EventArgs e) { this.secretKeyField.SecretMode = true; string key = Base32.getInstance().Encode(CurrentAuthenticator.AuthenticatorData.SecretKey); this.secretKeyField.Text = Regex.Replace(key, ".{3}", "$0 ").Trim(); string type = CurrentAuthenticator.AuthenticatorData is HOTPAuthenticator ? "hotp" : "totp"; long counter = (CurrentAuthenticator.AuthenticatorData is HOTPAuthenticator ? ((HOTPAuthenticator)CurrentAuthenticator.AuthenticatorData).Counter : 0); string issuer = CurrentAuthenticator.AuthenticatorData.Issuer; string url = "otpauth://" + type + "/" + WinAuthHelper.HtmlEncode(CurrentAuthenticator.Name) + "?secret=" + key + "&digits=" + CurrentAuthenticator.AuthenticatorData.CodeDigits + (counter != 0 ? "&counter=" + counter : string.Empty) + (string.IsNullOrEmpty(issuer) == false ? "&issuer=" + WinAuthHelper.HtmlEncode(issuer) : string.Empty); BarcodeWriter writer = new BarcodeWriter(); writer.Format = BarcodeFormat.QR_CODE; writer.Options = new ZXing.Common.EncodingOptions { Width = qrImage.Width, Height = qrImage.Height }; qrImage.Image = writer.Write(url); }
/// <summary> /// Write a setting value into the Config /// </summary> /// <param name="name">name of setting value</param> /// <param name="value">setting value. If null, the setting is deleted.</param> public void WriteSetting(string name, string value) { if (this.IsPortable == true) { if (value == null) { if (_settings.ContainsKey(name) == true) { _settings.Remove(name); } } else { _settings[name] = value; } } else { if (value == null) { WinAuthHelper.DeleteRegistryKey(name); } else { WinAuthHelper.WriteRegistryValue(name, value); } } }
/// <summary> /// Load the error report form /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ErrorReportForm_Load(object sender, EventArgs e) { // build data try { dataText.Text = WinAuthHelper.PGPEncrypt(BuildDiagnostics(), WinAuthHelper.WINAUTH_PGP_PUBLICKEY); } catch (Exception ex) { dataText.Text = string.Format("{0}\n\n{1}", ex.Message, new StackTrace(ex)); } }
/// <summary> /// Load the error report form /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ErrorReportForm_Load(object sender, EventArgs e) { // build data try { dataText.Text = WinAuthHelper.PGPEncrypt(BuildDiagnostics(), WinAuthHelper.WINAUTH_PGP_PUBLICKEY); } catch (EmptyReportException) { dataText.Text = "No data available for error report."; } catch (Exception ex) { dataText.Text = string.Format("{0}\n\n{1}", ex.Message, new System.Diagnostics.StackTrace(ex).ToString()); } }
/// <summary> /// Get all the settings keys beneath the specified key /// </summary> /// <param name="name">name of parent key</param> /// <returns>string array of all child (recursively) setting names. Empty is none.</returns> public string[] ReadSettingKeys(string name) { if (this.IsPortable == true) { List <string> keys = new List <string>(); foreach (var entry in _settings) { if (entry.Key.StartsWith(name) == true) { keys.Add(entry.Key); } } return(keys.ToArray()); } else { return(WinAuthHelper.ReadRegistryKeys(name)); } }
/// <summary> /// Read a setting value. /// </summary> /// <param name="name">name of setting</param> /// <param name="defaultValue">default value if setting doesn't exist</param> /// <returns>setting value or default value</returns> public string ReadSetting(string name, string defaultValue = null) { if (this.IsPortable == true) { // read setting from _settings string value; if (_settings.TryGetValue(name, out value) == true) { return(value); } else { return(defaultValue); } } else { return(WinAuthHelper.ReadRegistryValue(name, defaultValue) as string); } }
/// <summary> /// Load the error report form /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void ExceptionForm_Load(object sender, EventArgs e) { errorIcon.Image = SystemIcons.Error.ToBitmap(); this.Height = detailsButton.Top + detailsButton.Height + 45; this.errorLabel.Text = string.Format(this.errorLabel.Text, (ErrorException != null ? ErrorException.Message : strings.UnknownError)); // build data #if DEBUG dataText.Text = string.Format("{0}\n\n{1}", this.ErrorException.Message, new System.Diagnostics.StackTrace(this.ErrorException).ToString()); #else try { dataText.Text = WinAuthHelper.PGPEncrypt(BuildDiagnostics(), WinAuthHelper.WINAUTH_PGP_PUBLICKEY); } catch (Exception ex) { dataText.Text = string.Format("{0}\n\n{1}", ex.Message, new System.Diagnostics.StackTrace(ex).ToString()); } #endif }
/// <summary> /// Verify and create the authenticator if needed /// </summary> /// <returns>true is successful</returns> private bool verifyAuthenticator(string privatekey) { if (string.IsNullOrEmpty(privatekey) == true) { return(false); } this.Authenticator.Name = nameField.Text; int digits = (this.Authenticator.AuthenticatorData != null ? this.Authenticator.AuthenticatorData.CodeDigits : GoogleAuthenticator.DEFAULT_CODE_DIGITS); if (string.IsNullOrEmpty(digitsField.Text) == true || int.TryParse(digitsField.Text, out digits) == false || digits <= 0) { return(false); } WinAuth.Authenticator.HMACTypes hmac = WinAuth.Authenticator.HMACTypes.SHA1; #if NETFX_3 try { hmac = (WinAuth.Authenticator.HMACTypes)Enum.Parse(typeof(WinAuth.Authenticator.HMACTypes), (string)hashField.SelectedItem, true); } catch (Exception) { } #else Enum.TryParse <WinAuth.Authenticator.HMACTypes>((string)hashField.SelectedItem, out hmac); #endif string authtype = timeBasedRadio.Checked == true ? TOTP : HOTP; int period = 0; if (string.IsNullOrEmpty(intervalField.Text) == true || int.TryParse(intervalField.Text, out period) == false || period <= 0) { return(false); } long counter = 0; // if this is a URL, pull it down Uri uri; Match match; if (Regex.IsMatch(privatekey, "https?://.*") == true && Uri.TryCreate(privatekey, UriKind.Absolute, out uri) == true) { try { var request = (HttpWebRequest)WebRequest.Create(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) == true) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(response.GetResponseStream())) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(this.Owner, "Cannot load QR code image from " + privatekey, ex); return(false); } } else if ((match = Regex.Match(privatekey, @"data:image/([^;]+);base64,(.*)", RegexOptions.IgnoreCase)).Success == true) { byte[] imagedata = Convert.FromBase64String(match.Groups[2].Value); using (MemoryStream ms = new MemoryStream(imagedata)) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(ms)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } else if (IsValidFile(privatekey) == true) { // assume this is the image file using (Bitmap bitmap = (Bitmap)Bitmap.FromFile(privatekey)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = result.Text; } } } string issuer = null; string serial = null; // check for otpauth://, e.g. "otpauth://totp/[email protected]?secret=IHZJDKAEEC774BMUK3GX6SA" match = Regex.Match(privatekey, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success == true) { authtype = match.Groups[1].Value.ToLower(); string label = match.Groups[2].Value; int p = label.IndexOf(":"); if (p != -1) { issuer = label.Substring(0, p); label = label.Substring(p + 1); } NameValueCollection qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); privatekey = qs["secret"] ?? privatekey; int querydigits; if (int.TryParse(qs["digits"], out querydigits) && querydigits != 0) { digits = querydigits; } if (qs["counter"] != null) { long.TryParse(qs["counter"], out counter); } issuer = qs["issuer"]; if (string.IsNullOrEmpty(issuer) == false) { label = issuer + (string.IsNullOrEmpty(label) == false ? " (" + label + ")" : string.Empty); } serial = qs["serial"]; if (string.IsNullOrEmpty(label) == false) { this.Authenticator.Name = this.nameField.Text = label; } string periods = qs["period"]; if (string.IsNullOrEmpty(periods) == false) { int.TryParse(periods, out period); } if (qs["algorithm"] != null) { #if NETFX_3 try { hmac = (WinAuth.Authenticator.HMACTypes)Enum.Parse(typeof(WinAuth.Authenticator.HMACTypes), qs["algorithm"], true); hashField.SelectedItem = hmac.ToString(); } catch (Exception) { } #else if (Enum.TryParse <WinAuth.Authenticator.HMACTypes>(qs["algorithm"], true, out hmac) == true) { hashField.SelectedItem = hmac.ToString(); } #endif } } // just get the hex chars privatekey = Regex.Replace(privatekey, @"[^0-9a-z]", "", RegexOptions.IgnoreCase); if (privatekey.Length == 0) { WinAuthForm.ErrorDialog(this.Owner, "The secret code is not valid"); return(false); } try { Authenticator auth; if (authtype == TOTP) { if (string.Compare(issuer, "BattleNet", true) == 0) { if (string.IsNullOrEmpty(serial) == true) { throw new ApplicationException("Battle.net Authenticator does not have a serial"); } serial = serial.ToUpper(); if (Regex.IsMatch(serial, @"^[A-Z]{2}-?[\d]{4}-?[\d]{4}-?[\d]{4}$") == false) { throw new ApplicationException("Invalid serial for Battle.net Authenticator"); } auth = new BattleNetAuthenticator(); ((BattleNetAuthenticator)auth).SecretKey = Base32.getInstance().Decode(privatekey); ((BattleNetAuthenticator)auth).Serial = serial; issuer = string.Empty; } else if (issuer == "Steam") { auth = new SteamAuthenticator(); ((SteamAuthenticator)auth).SecretKey = Base32.getInstance().Decode(privatekey); ((SteamAuthenticator)auth).Serial = string.Empty; ((SteamAuthenticator)auth).DeviceId = string.Empty; //((SteamAuthenticator)auth).RevocationCode = string.Empty; ((SteamAuthenticator)auth).SteamData = string.Empty; this.Authenticator.Skin = null; issuer = string.Empty; } else { auth = new GoogleAuthenticator(); ((GoogleAuthenticator)auth).Enroll(privatekey); } timer.Enabled = true; codeProgress.Visible = true; timeBasedRadio.Checked = true; } else if (authtype == HOTP) { auth = new HOTPAuthenticator(); if (counterField.Text.Trim().Length != 0) { long.TryParse(counterField.Text.Trim(), out counter); } ((HOTPAuthenticator)auth).Enroll(privatekey, counter); // start with the next code timer.Enabled = false; codeProgress.Visible = false; counterBasedRadio.Checked = true; } else { WinAuthForm.ErrorDialog(this.Owner, "Only TOTP or HOTP authenticators are supported"); return(false); } auth.HMACType = hmac; auth.CodeDigits = digits; auth.Period = period; this.Authenticator.AuthenticatorData = auth; if (digits > 5) { codeField.SpaceOut = digits / 2; } else { codeField.SpaceOut = 0; } //string key = Base32.getInstance().Encode(this.Authenticator.AuthenticatorData.SecretKey); this.codeField.Text = auth.CurrentCode; codeProgress.Maximum = period; if (!(auth is HOTPAuthenticator) && auth.ServerTimeDiff == 0L && SyncErrorWarned == false) { SyncErrorWarned = true; MessageBox.Show(this, string.Format(strings.AuthenticatorSyncError, "Google"), WinAuthMain.APPLICATION_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception irre) { WinAuthForm.ErrorDialog(this.Owner, "Unable to create the authenticator. The secret code is probably invalid.", irre); return(false); } return(true); }
/// <summary> /// Decode the secret field /// </summary> private void DecodeSecretCode() { Uri uri; Match match; if (Regex.IsMatch(secretCodeField.Text, "https?://.*") == true && Uri.TryCreate(secretCodeField.Text, UriKind.Absolute, out uri) == true) { try { var request = (HttpWebRequest)WebRequest.Create(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) == true) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(response.GetResponseStream())) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null && string.IsNullOrEmpty(result.Text) == false) { secretCodeField.Text = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(this.Owner, "Cannot load QR code image from " + secretCodeField.Text, ex); return; } } match = Regex.Match(secretCodeField.Text, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success == true) { string authtype = match.Groups[1].Value.ToLower(); string label = match.Groups[2].Value; if (authtype == HOTP) { counterBasedRadio.Checked = true; } else if (authtype == TOTP) { timeBasedRadio.Checked = true; counterField.Text = string.Empty; } NameValueCollection qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); if (qs["counter"] != null) { long counter; if (long.TryParse(qs["counter"], out counter) == true) { counterField.Text = counter.ToString(); } } string issuer = qs["issuer"]; if (string.IsNullOrEmpty(issuer) == false) { label = issuer + (string.IsNullOrEmpty(label) == false ? " (" + label + ")" : string.Empty); } this.nameField.Text = label; int period; if (int.TryParse(qs["period"], out period) == true && period > 0) { this.intervalField.Text = period.ToString(); } int digits; if (int.TryParse(qs["digits"], out digits) == true && digits > 0) { this.digitsField.Text = digits.ToString(); } WinAuth.Authenticator.HMACTypes hmac; #if NETFX_3 try { hmac = (WinAuth.Authenticator.HMACTypes)Enum.Parse(typeof(WinAuth.Authenticator.HMACTypes), qs["algorithm"], true); this.hashField.SelectedItem = hmac.ToString(); } catch (Exception) { } #else if (Enum.TryParse <WinAuth.Authenticator.HMACTypes>(qs["algorithm"], true, out hmac) == true) { this.hashField.SelectedItem = hmac.ToString(); } #endif } }
/// <summary> /// Verify and create the authenticator if needed /// </summary> /// <returns>true is successful</returns> private bool verifyAuthenticator(string privatekey) { if (string.IsNullOrEmpty(privatekey)) { return(false); } Authenticator.Name = nameField.Text; var authtype = "totp"; // if this is a URL, pull it down Uri uri; Match match; if (Regex.IsMatch(privatekey, "https?://.*") && Uri.TryCreate(privatekey, UriKind.Absolute, out uri)) { try { var request = (HttpWebRequest)WebRequest.Create(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { using (var bitmap = (Bitmap)Image.FromStream(response.GetResponseStream())) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(Owner, "Cannot load QR code image from " + privatekey, ex); return(false); } } else if ((match = Regex.Match(privatekey, @"data:image/([^;]+);base64,(.*)", RegexOptions.IgnoreCase)) .Success) { var imagedata = Convert.FromBase64String(match.Groups[2].Value); using (var ms = new MemoryStream(imagedata)) { using (var bitmap = (Bitmap)Image.FromStream(ms)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } else if (IsValidFile(privatekey)) { // assume this is the image file using (var bitmap = (Bitmap)Image.FromFile(privatekey)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = result.Text; } } } // check for otpauth://, e.g. "otpauth://totp/[email protected]?secret=IHZJDKAEEC774BMUK3GX6SA" match = Regex.Match(privatekey, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success) { authtype = match.Groups[1].Value; // @todo we only handle totp (not hotp) if (string.Compare(authtype, "totp", true) != 0) { WinAuthForm.ErrorDialog(Owner, "Only time-based (TOTP) authenticators are supported when adding a Google Authenticator. Use the general \"Add Authenticator\" for counter-based (HOTP) authenticators."); return(false); } var label = match.Groups[2].Value; if (string.IsNullOrEmpty(label) == false) { Authenticator.Name = nameField.Text = label; } var qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); privatekey = qs["secret"] ?? privatekey; } // just get the hex chars privatekey = Regex.Replace(privatekey, @"[^0-9a-z]", "", RegexOptions.IgnoreCase); if (privatekey.Length == 0) { WinAuthForm.ErrorDialog(Owner, "The secret code is not valid"); return(false); } try { var auth = new GoogleAuthenticator(); auth.Enroll(privatekey); Authenticator.AuthenticatorData = auth; codeProgress.Visible = true; var key = Base32.getInstance().Encode(Authenticator.AuthenticatorData.SecretKey); secretCodeField.Text = Regex.Replace(key, ".{3}", "$0 ").Trim(); codeField.Text = auth.CurrentCode; if (auth.ServerTimeDiff == 0L && SyncErrorWarned == false) { SyncErrorWarned = true; MessageBox.Show(this, string.Format(strings.AuthenticatorSyncError, "Google"), WinAuthMain.APPLICATION_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception irre) { WinAuthForm.ErrorDialog(Owner, "Unable to create the authenticator. The secret code is probably invalid.", irre); return(false); } return(true); }
/// <summary> /// Click the button to convert any secret code /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void secretCodeButton_Click(object sender, EventArgs e) { Uri uri; Match match; if (Regex.IsMatch(secretCodeField.Text, "https?://.*") == true && Uri.TryCreate(secretCodeField.Text, UriKind.Absolute, out uri) == true) { try { var request = (HttpWebRequest)WebRequest.Create(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) == true) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(response.GetResponseStream())) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null && string.IsNullOrEmpty(result.Text) == false) { secretCodeField.Text = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(this.Owner, "无法从中加载QR码图像 " + secretCodeField.Text, ex); return; } } match = Regex.Match(secretCodeField.Text, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success == true) { string authtype = match.Groups[1].Value.ToLower(); string label = match.Groups[2].Value; if (authtype == HOTP) { counterBasedRadio.Checked = true; NameValueCollection qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); if (qs["counter"] != null) { long counter; if (long.TryParse(qs["counter"], out counter) == true) { counterField.Text = counter.ToString(); } } string issuer = qs["issuer"]; if (string.IsNullOrEmpty(issuer) == false) { label = issuer + (string.IsNullOrEmpty(label) == false ? " (" + label + ")" : string.Empty); } this.nameField.Text = label; } else if (authtype == TOTP) { timeBasedRadio.Checked = true; counterField.Text = string.Empty; } } }
/// <summary> /// Build a diagnostics string for the current Config and any exception that had been thrown /// </summary> /// <returns>diagnostics information</returns> private string BuildDiagnostics() { var diag = new StringBuilder(); if (Config != null) { // clone the current config so we can extract key in case machine/user encrypted var clone = Config.Clone() as WinAuthConfig; clone.PasswordType = Authenticator.PasswordTypes.None; // add the config and authenticator try { var xml = new StringBuilder(); var settings = new XmlWriterSettings(); settings.Indent = true; using (var writer = XmlWriter.Create(xml, settings)) { clone.WriteXmlString(writer); } diag.Append("--CURRENT CONFIG--").Append(Environment.NewLine); diag.Append(xml).Append(Environment.NewLine).Append(Environment.NewLine); } catch (Exception ex) { diag.Append(ex.Message).Append(Environment.NewLine).Append(Environment.NewLine); } } // add each of the entries from the registry if (Config != null) { diag.Append("--REGISTRY--").Append(Environment.NewLine); diag.Append(WinAuthHelper.ReadBackupFromRegistry(Config)).Append(Environment.NewLine) .Append(Environment.NewLine); } // add current config file if (string.IsNullOrEmpty(ConfigFileContents) == false) { diag.Append("--CONFIGFILE--").Append(Environment.NewLine); diag.Append(ConfigFileContents).Append(Environment.NewLine).Append(Environment.NewLine); } // add winauth log var dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), WinAuthMain.APPLICATION_NAME); var winauthlog = Path.Combine(dir, "winauth.log"); if (File.Exists(winauthlog)) { diag.Append("--WINAUTH.LOG--").Append(Environment.NewLine); diag.Append(File.ReadAllText(winauthlog)).Append(Environment.NewLine).Append(Environment.NewLine); } // add the exception if (ErrorException != null) { diag.Append("--EXCEPTION--").Append(Environment.NewLine); var ex = ErrorException; while (ex != null) { diag.Append("Stack: ").Append(ex.Message).Append(Environment.NewLine).Append(new StackTrace(ex)) .Append(Environment.NewLine); ex = ex.InnerException; } if (ErrorException is InvalidEncryptionException) { diag.Append("Plain: " + ((InvalidEncryptionException)ErrorException).Plain) .Append(Environment.NewLine); diag.Append("Password: "******"Encrypted: " + ((InvalidEncryptionException)ErrorException).Encrypted) .Append(Environment.NewLine); diag.Append("Decrypted: " + ((InvalidEncryptionException)ErrorException).Decrypted) .Append(Environment.NewLine); } else if (ErrorException is InvalidSecretDataException) { diag.Append("EncType: " + ((InvalidSecretDataException)ErrorException).EncType) .Append(Environment.NewLine); diag.Append("Password: "******"Data: " + data).Append(Environment.NewLine); } } } return(diag.ToString()); }
/// <summary> /// Verify and create the authenticator if needed /// </summary> /// <returns>true is successful</returns> private bool verifyAuthenticator(string privatekey) { if (string.IsNullOrEmpty(privatekey) == true) { return(false); } this.Authenticator.Name = nameField.Text; string authtype = "totp"; // if this is a URL, pull it down Uri uri; Match match; if (Regex.IsMatch(privatekey, "https?://.*") == true && Uri.TryCreate(privatekey, UriKind.Absolute, out uri) == true) { try { var request = (HttpWebRequest)WebRequest.Create(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) == true) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(response.GetResponseStream())) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(this.Owner, "无法从中加载QR码图像 " + privatekey, ex); return(false); } } else if ((match = Regex.Match(privatekey, @"data:image/([^;]+);base64,(.*)", RegexOptions.IgnoreCase)).Success == true) { byte[] imagedata = Convert.FromBase64String(match.Groups[2].Value); using (MemoryStream ms = new MemoryStream(imagedata)) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(ms)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } else if (IsValidFile(privatekey) == true) { // assume this is the image file using (Bitmap bitmap = (Bitmap)Bitmap.FromFile(privatekey)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = result.Text; } } } // check for otpauth://, e.g. "otpauth://totp/[email protected]?secret=IHZJDKAEEC774BMUK3GX6SA" match = Regex.Match(privatekey, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success == true) { authtype = match.Groups[1].Value; // @todo we only handle totp (not hotp) if (string.Compare(authtype, "totp", true) != 0) { WinAuthForm.ErrorDialog(this.Owner, "添加Google Authenticator时,仅支持基于时间的(TOTP)验证器。使用普通 \"添加身份验证器\" 进行计数器(Hotp)验证器。"); return(false); } string label = match.Groups[2].Value; if (string.IsNullOrEmpty(label) == false) { this.Authenticator.Name = this.nameField.Text = label; } NameValueCollection qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); privatekey = qs["secret"] ?? privatekey; } // just get the hex chars privatekey = Regex.Replace(privatekey, @"[^0-9a-z]", "", RegexOptions.IgnoreCase); if (privatekey.Length == 0) { WinAuthForm.ErrorDialog(this.Owner, "密钥无效"); return(false); } try { MicrosoftAuthenticator authenticator = new MicrosoftAuthenticator(); authenticator.Enroll(privatekey); this.Authenticator.AuthenticatorData = authenticator; this.Authenticator.Name = this.nameField.Text; codeField.Text = authenticator.CurrentCode; newAuthenticatorProgress.Visible = true; newAuthenticatorTimer.Enabled = true; } catch (Exception ex) { WinAuthForm.ErrorDialog(this.Owner, "无法创建验证器: " + ex.Message, ex); return(false); } return(true); }
/// <summary> /// Verify and create the authenticator if needed /// </summary> /// <returns>true is successful</returns> private bool verifyAuthenticator(string privatekey) { if (string.IsNullOrEmpty(privatekey) == true) { return(false); } this.Authenticator.Name = nameField.Text; int digits = (this.Authenticator.AuthenticatorData != null ? this.Authenticator.AuthenticatorData.CodeDigits : GoogleAuthenticator.DEFAULT_CODE_DIGITS); string authtype = timeBasedRadio.Checked == true ? TOTP : HOTP; long counter = 0; // if this is a URL, pull it down Uri uri; Match match; if (Regex.IsMatch(privatekey, "https?://.*") == true && Uri.TryCreate(privatekey, UriKind.Absolute, out uri) == true) { try { var request = (HttpWebRequest)WebRequest.Create(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase) == true) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(response.GetResponseStream())) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(this.Owner, "Cannot load QR code image from " + privatekey, ex); return(false); } } else if ((match = Regex.Match(privatekey, @"data:image/([^;]+);base64,(.*)", RegexOptions.IgnoreCase)).Success == true) { byte[] imagedata = Convert.FromBase64String(match.Groups[2].Value); using (MemoryStream ms = new MemoryStream(imagedata)) { using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(ms)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = HttpUtility.UrlDecode(result.Text); } } } } else if (IsValidFile(privatekey) == true) { // assume this is the image file using (Bitmap bitmap = (Bitmap)Bitmap.FromFile(privatekey)) { IBarcodeReader reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null) { privatekey = result.Text; } } } // check for otpauth://, e.g. "otpauth://totp/[email protected]?secret=IHZJDKAEEC774BMUK3GX6SA" match = Regex.Match(privatekey, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success == true) { authtype = match.Groups[1].Value.ToLower(); string label = match.Groups[2].Value; NameValueCollection qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); privatekey = qs["secret"] ?? privatekey; int querydigits; if (int.TryParse(qs["digits"], out querydigits) && querydigits != 0) { digits = querydigits; } if (qs["counter"] != null) { long.TryParse(qs["counter"], out counter); } string issuer = qs["issuer"]; if (string.IsNullOrEmpty(issuer) == false) { label = issuer + (string.IsNullOrEmpty(label) == false ? " (" + label + ")" : string.Empty); } if (string.IsNullOrEmpty(label) == false) { this.Authenticator.Name = this.nameField.Text = label; } } // just get the hex chars privatekey = Regex.Replace(privatekey, @"[^0-9a-z]", "", RegexOptions.IgnoreCase); if (privatekey.Length == 0) { WinAuthForm.ErrorDialog(this.Owner, "The secret code is not valid"); return(false); } try { Authenticator auth; if (authtype == TOTP) { auth = new GoogleAuthenticator(); ((GoogleAuthenticator)auth).Enroll(privatekey); timer.Enabled = true; codeProgress.Visible = true; timeBasedRadio.Checked = true; } else if (authtype == HOTP) { auth = new HOTPAuthenticator(); if (counterField.Text.Trim().Length != 0) { long.TryParse(counterField.Text.Trim(), out counter); } ((HOTPAuthenticator)auth).Enroll(privatekey, counter); // start with the next code timer.Enabled = false; codeProgress.Visible = false; counterBasedRadio.Checked = true; } else { WinAuthForm.ErrorDialog(this.Owner, "Only TOTP or HOTP authenticators are supported"); return(false); } auth.CodeDigits = digits; this.Authenticator.AuthenticatorData = auth; codeField.SpaceOut = digits / 2; string key = Base32.getInstance().Encode(this.Authenticator.AuthenticatorData.SecretKey); //this.secretCodeField.Text = Regex.Replace(key, ".{3}", "$0 ").Trim(); this.codeField.Text = auth.CurrentCode; if (!(auth is HOTPAuthenticator) && auth.ServerTimeDiff == 0L && SyncErrorWarned == false) { SyncErrorWarned = true; MessageBox.Show(this, string.Format(strings.AuthenticatorSyncError, "Google"), WinAuthMain.APPLICATION_TITLE, MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception irre) { WinAuthForm.ErrorDialog(this.Owner, "Unable to create the authenticator. The secret code is probably invalid.", irre); return(false); } return(true); }
/// <summary> /// Decode the secret field /// </summary> private void DecodeSecretCode() { Match match; if (Regex.IsMatch(secretCodeField.Text, "https?://.*") && Uri.TryCreate(secretCodeField.Text, UriKind.Absolute, out var uri)) { try { var request = WebRequest.CreateHttp(uri); request.AllowAutoRedirect = true; request.Timeout = 20000; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0)"; using (var response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK && response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase)) { using (var bitmap = (Bitmap)Image.FromStream(response.GetResponseStream())) { var reader = new BarcodeReader(); var result = reader.Decode(bitmap); if (result != null && !string.IsNullOrEmpty(result.Text)) { secretCodeField.Text = HttpUtility.UrlDecode(result.Text); } } } } } catch (Exception ex) { WinAuthForm.ErrorDialog(Owner, "Cannot load QR code image from " + secretCodeField.Text, ex); return; } } match = Regex.Match(secretCodeField.Text, @"otpauth://([^/]+)/([^?]+)\?(.*)", RegexOptions.IgnoreCase); if (match.Success) { var authtype = match.Groups[1].Value.ToLower(); var label = match.Groups[2].Value; if (authtype == HOTP) { counterBasedRadio.Checked = true; } else if (authtype == TOTP) { timeBasedRadio.Checked = true; counterField.Text = string.Empty; } var qs = WinAuthHelper.ParseQueryString(match.Groups[3].Value); if (qs["counter"] != null) { if (long.TryParse(qs["counter"], out var counter)) { counterField.Text = counter.ToString(); } } var issuer = qs["issuer"]; if (!string.IsNullOrEmpty(issuer)) { label = issuer + (string.IsNullOrEmpty(label) ? string.Empty : " (" + label + ")"); } nameField.Text = label; if (int.TryParse(qs["period"], out var period) && period > 0) { intervalField.Text = period.ToString(); } if (int.TryParse(qs["digits"], out var digits) && digits > 0) { digitsField.Text = digits.ToString(); } if (Enum.TryParse <Authenticator.HMACTypes>(qs["algorithm"], true, out var hmac)) { hashField.SelectedItem = hmac.ToString(); } } }