This repository has been archived by the owner on Jan 30, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Utilities.cs
146 lines (131 loc) · 5.04 KB
/
Utilities.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
using System;
using System.Configuration;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Text;
namespace FlyingPie
{
static class Utilities
{
private const string EmailPasswordConfigKey = "EmailPassword";
private static readonly byte[] Entropy;
static Utilities()
{
Entropy = Encoding.Unicode.GetBytes(ConfigurationManager.AppSettings["SaltyString"]);
}
public static void SetAppConfig(string key, string value)
{
var fullExePath = Assembly.GetEntryAssembly().Location;
Configuration configuration = ConfigurationManager.OpenExeConfiguration(fullExePath);
configuration.AppSettings.Settings.Remove(key);
configuration.AppSettings.Settings.Add(key, value);
configuration.Save();
ConfigurationManager.RefreshSection("appSettings");
}
/// <summary>
/// Returns the email password if we have it stored. Optionally prompts the user for the password if we don't already have it stored.
/// Returns null if
/// </summary>
/// <param name="promptForPassword"></param>
/// <returns></returns>
public static SecureString GetEmailPassword(bool promptForPassword)
{
//First see if we've already saved the password
var encryptedPassword = ConfigurationManager.AppSettings[EmailPasswordConfigKey];
if (encryptedPassword != null)
{
var decryptedPassword = DecryptString(encryptedPassword);
if (decryptedPassword != null && decryptedPassword.Length > 0)
{
return decryptedPassword;
}
}
//If we didn't already have it saved, or decrypting failed, we'll need to prompt the user (if allowed).
if (!promptForPassword) return null;
var passwordFromUser = PromptForEmailPassword();
//Encrypt the password and save it for later, then return it.
SetAppConfig(EmailPasswordConfigKey, EncryptString(passwordFromUser));
return passwordFromUser;
}
public static SecureString PromptForEmailPassword()
{
bool done = false;
Console.Write("First-time setup: what is the **app-specific** password for sending email? ");
var password = new SecureString();
while (!done)
{
var key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.Enter:
done = true;
continue;
case ConsoleKey.Backspace:
if (password.Length > 0)
{
Console.Write("\b \b");
password.RemoveAt(password.Length - 1);
}
continue;
}
//Ignore the keypress if the character is 0 - that means it was something special we can ignore.
var c = key.KeyChar;
if (c == 0) continue;
//If we get here, the keypress should be part of the password. Show a * for the user and save it.
Console.Write("*");
password.AppendChar(key.KeyChar);
}
password.MakeReadOnly();
return password;
}
private static string EncryptString(SecureString input)
{
byte[] encryptedData = ProtectedData.Protect(
Encoding.Unicode.GetBytes(ToInsecureString(input)),
Entropy,
DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encryptedData);
}
private static SecureString DecryptString(string encryptedData)
{
try
{
byte[] decryptedData = ProtectedData.Unprotect(
Convert.FromBase64String(encryptedData),
Entropy,
DataProtectionScope.CurrentUser);
return ToSecureString(Encoding.Unicode.GetString(decryptedData));
}
catch
{
return null;
}
}
private static SecureString ToSecureString(this string input)
{
SecureString secure = new SecureString();
foreach (char c in input)
{
secure.AppendChar(c);
}
secure.MakeReadOnly();
return secure;
}
private static string ToInsecureString(this SecureString input)
{
string returnValue = string.Empty;
IntPtr ptr = Marshal.SecureStringToBSTR(input);
try
{
returnValue = Marshal.PtrToStringBSTR(ptr);
}
finally
{
Marshal.ZeroFreeBSTR(ptr);
}
return returnValue;
}
}
}