1

I'm working on my first Azure SQL database project in csharp, where the code looks basically like the sample codes in their documentation:

https://learn.microsoft.com/en-us/azure/mysql/connect-csharp

I'm currently storing my username and password in a JSON config file, just in plain text. I'd like to be able to encrypt these, but not sure on how to design/implement that.

Ideally, the user would be able to do a one-time config (example.exe --config -username {username} -password {password}), the program would hash the password and store it in my config.json, and be able to decrypt it when the user wants to use the program to interact with their database.

Does that make sense/is secure from a design perspective? What encryption/decryption framework would be recommended for this use? Thanks!

MLE
  • 51
  • 1
  • 5
  • 1
    You don't encrypt passwords, you [hash them](https://stackoverflow.com/questions/326699/difference-between-hashing-a-password-and-encrypting-it). – DavidG Feb 26 '19 at 00:41
  • Also this is not a how-to site, we deal with programming problems and specific ones at that. there are oodles of ways to hash a password, and you haven't narrowed it down. also you have tried nothing. this is too broad... if you could address some of these issues, i am sure we can help you – TheGeneral Feb 26 '19 at 00:43
  • @DavidG Not when you want them to authenticate to the database, I suppose. – Maarten Bodewes Feb 26 '19 at 00:43
  • @DavidG Not really, "and be able to decrypt it when the user wants to use the program to interact with their database.". I believe the OP wants to secure their connection string – Camilo Terevinto Feb 26 '19 at 00:43
  • 1
    Ah maybe, it's a reflex action when someone asks how to encrypt passwords... – DavidG Feb 26 '19 at 00:45
  • You could use JSON Web Encryption on the password and store the secret key in the code. Of course, this isn't secure if an adversary has access to the runtime code, but generally you can keep the runtime code better protected than e.g. a configuration file. You may want to keep the key out of the source repository, of course. Another place would be a key store that is assigned to the user associated with the runtime. – Maarten Bodewes Feb 26 '19 at 00:45
  • @DavidG And it's a good one, 99% of the times probably – Camilo Terevinto Feb 26 '19 at 00:45
  • @CamiloTerevinto Exactly, the goal at the end of the day is to obfuscate the password so I don't need to store it as plaintext. – MLE Feb 26 '19 at 00:46
  • If you use RSA you can store the private key on the server that will decrypt the password. The problem is that the encrypted password becomes your plain text password because that is all you need to send to the server to gain access. – Jerry Feb 26 '19 at 01:00
  • Wait, are you trying to encrypt a password, like in a connection string? Or are you trying to hash a password, like to log on a server? Because those are two very different things. – Dour High Arch Feb 26 '19 at 01:01
  • @DourHighArch Sorry, I don't know what the difference is. The connection string looks like this in Azure: "Server=tcp:x.database.windows.net,1433;Initial Catalog=x;Persist Security Info=False;User ID={your_username};Password={your_password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" , which still includes the plaintext password as a field. – MLE Feb 26 '19 at 01:11
  • @EmilyL *don't* store the username and password in the connection string. In Azure you may *not* need any passwords. You could [create a managed service identity](https://learn.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-connect-msi) and use *that* to connect to the database, the same way a service application on premises would connect using its service AD account instead of any passwords. Amazon has similar mechanisms – Panagiotis Kanavos Feb 26 '19 at 10:54
  • @EmilyL all cloud providers offer secure credential stores, eg [Azure Key Vault](https://azure.microsoft.com/en-us/services/key-vault/). You can store any credentials there and the application retrieves them when it wants to use them. They are primarily useful for credentials to external services because all cloud platforms offer a way to authenticate specific services/functions/sites. Azure Key Vault is both easy and a lot more secure compared to attempting to encrypt passwords and store them somewhere – Panagiotis Kanavos Feb 26 '19 at 10:57

2 Answers2

2

You can use the System.Security.CryptoGraphy.ProtectedData class for this purpose, i.e. encrypt, then store encrypted user credentials and decrypt when needed. You can read up on it here:

https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection

https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.protecteddata?view=netframework-4.7.2

Here is an example:

using System;
using System.Text;
// Reference assembly 'System.Security'
using System.Security.Cryptography;

namespace TestProtectedData
{
    class Program
    {
        // Encrypt plainText and return a base-64 encoded cipher text
        static string Encrypt(string plainText)
        {
            byte[] plainBytes = UnicodeEncoding.UTF8.GetBytes(plainText);
            byte[] cipherBytes = ProtectedData.Protect(plainBytes, null, DataProtectionScope.CurrentUser);
            return Convert.ToBase64String(cipherBytes);
        }

        // Decrypt a base-64 encoded cipher text and return plain text
        static string Decrypt(string cipherBase64)
        {
            var cipherBytes = Convert.FromBase64String(cipherBase64);
            var plainBytes = ProtectedData.Unprotect(cipherBytes, null, DataProtectionScope.CurrentUser);
            return Encoding.UTF8.GetString(plainBytes, 0, plainBytes.Length);
        }

        static void Main(string[] args)
        {
            // plainTextToEncrypt can be a connection string, user credentials or similar
            var plainTextToEncrypt = "Hello, secret!";
            Console.WriteLine("Plain text: " + plainTextToEncrypt);
            // Getting a base64 encoded string as the encryption result for easy storage
            var cipherBase64 = Encrypt(plainTextToEncrypt);
            // Save the cipherBase64 string into a configuration file or similar
            Console.WriteLine("Encrypted text (base64): " + cipherBase64);
            // When needed, read the cipherBase64 string and decrypt the text
            var plainTextDecrypted = Decrypt(cipherBase64);
            Console.WriteLine("Decrypted text: " + plainTextDecrypted);
            Console.ReadKey();
        }
    }
}
J.R.
  • 1,880
  • 8
  • 16
  • Yup, this is how you do it. The secret is safe if the file is stolen. The only way it can be decrypted is if the attacker has the credentials of the Windows account that did the encryption. – Mark Waterman Feb 26 '19 at 01:32
0

I once worked on a project where in I did something like this. Here DecryptString will throw an exception if an original string is present. You can also use a flag in DecryptString to indicate if decryption was successful.

    #region Properties
    public string EmailAddress { get; set; }
    public string Password { get; set; }
    #endregion

    public void Initialize()
    {
        try
        {
            // Decrypt username & password
            // this will throw an error if original string is present
            EmailAddress = DecryptString(EmailAddress);
            Password = DecryptString(Password);
        }
        catch (Exception)
        {
            EncryptAppConfig(EmailAddress, Password);
            _log.Info("Encrypted Config file with email and password. ");
        }
    }

Then your EncryptAppConfig and DecryptAppConfig methods can use methods and properties from System.Security.Cryptography like RijndaelManaged, Rfc2898DeriveBytes.

Gauravsa
  • 6,330
  • 2
  • 21
  • 30