0

I have an application with a "secret" (eg password)

I don't want to store this secret locally in a user-accessible context file, so I'm planning to retrieve it on-demand from the server over HTTPS

I also don't want it visible in memory (eg from a crash log), for obvious reasons, so I'm storing the secret in a SecureString

However, when I serialise the SecureString, the result just shows the length of the plaintext string, eg {"Length":4}

If I transmit the password in plaintext, though, then it will be visible in the retrieved JSON in memory, even if subsequently store it in a SecureString

Is there any way to serialize a SecureString, or to receive the JSON and convert a plaintext string to a SecureString without needing an intermediate regular String that would be stored in memory?

In this scenario, I have to store/send the actual password, rather than, for example, a one-time-use key as I'd prefer: that's beyond my control here. I need the actual plaintext password to access another service, so the usual "Hash it then compare the hash" doesn't apply either

Jon Story
  • 2,881
  • 2
  • 25
  • 41
  • Simple: you cannot do that. SecureString is protected using DPAPI with User scope, so only the current Windows user in the current PC can decrypt it, and that's on purpose – Camilo Terevinto Apr 09 '19 at 12:07
  • Why not store an encrypted password on the server and decode it in the application locally? – Robert H Apr 09 '19 at 12:08
  • 1
    @RobertH Password should **never** be encrypted and should **never** be possible to be converted to plain text – Camilo Terevinto Apr 09 '19 at 12:08
  • RSA public/private key encryption seems like a possible way of doing it? Have client encrypt the password with the server's public key and transmit the encrypted string. The server can decrypt it with his private key. – Thomas N Apr 09 '19 at 12:09
  • @CamiloTerevinto What is your suggestion then? If encryption isn't an option and the password is not local, and can't be plain text I am intrigued as to the solution here – Robert H Apr 09 '19 at 12:13
  • @RobertH The actual answer is that there is no secure way of achieving this. The server needs plain text and that is wrong from the start. RSA could be used as ThomasN commented, but the server would still be just as insecure. We don't have enough information here as to know why it worries so much to the OP that the memory could be dumped and inspected – Camilo Terevinto Apr 09 '19 at 12:15
  • @CamiloTerevinto RSA is encryption right? – Robert H Apr 09 '19 at 12:18
  • @RobertH Exactly. It would harden security client-side, but not server-side. But we don't know if the OP cares about server-side either... – Camilo Terevinto Apr 09 '19 at 12:19
  • RSA is public key encryption. The client program generates an RSA key (a public/private pair) and sends its public key to the server. The server encrypts the password using this and sends the encrypted version back. Only the client program can decode it, as this requires the private part of the key to do. Secure over the link and in memory until you decode it. – Steve Todd Apr 09 '19 at 12:24
  • I don't particularly care about sever side here - if the server is compromised we have far bigger problems (the server itself holds a more secure system than the one accessed with the password). This question is entirely about avoiding exposing the password (as far as is practical) on the client side. This is an internal application at our organisation, so it wouldn't be the end of the world for the user to be able to access the password - but I'd still rather avoid it as far as possible – Jon Story Apr 09 '19 at 12:29

1 Answers1

0

Here is a worked example of using RSA encryption to send a string to someone whose public key you have. In your question you want the server to send a message (password) to the client and for the client to securely use that password without having to worry about logging etc in the middle. For this to work you need to have the client create the private key file and send the public key file to the server which can then communicate back securely.

There are probably libraries that make this way easier.

[Test]
public void TestEncryption()
{
    /////////////// Create Key Files ////////////////
    RSACryptoServiceProvider provider = new RSACryptoServiceProvider(4096);

    //Create the key files on disk and distribute them to sender / reciever
    var publicKey =  provider.ToXmlString(false);
    var privateKey =  provider.ToXmlString(true);

    /////////////// Actual Test ////////////////

    //send with the public key
    byte[] sent = Send("hey",publicKey);

    //cannot receive with public key
    var ex = Assert.Throws<CryptographicException>(()=>Receive(sent, publicKey));
    StringAssert.Contains("Key does not exist",ex.Message);

    //but can with private key
    Assert.AreEqual("hey", Receive(sent,privateKey));
}

private Byte[] Send(string send, string publicKey)
{
    using (RSACryptoServiceProvider rsaSender = new RSACryptoServiceProvider())
    {
        rsaSender.FromXmlString(publicKey);
        return rsaSender.Encrypt(Encoding.ASCII.GetBytes(send), false);
    }
}

private object Receive(byte[] sent, string privateKey)
{
    using (RSACryptoServiceProvider rsaReceiver = new RSACryptoServiceProvider())
    {
        rsaReceiver.FromXmlString(privateKey);
        return Encoding.ASCII.GetString(rsaReceiver.Decrypt(sent, false));
    }
}
Thomas N
  • 623
  • 1
  • 4
  • 14
  • 1
    Thanks for that, didn't notice that method. Nice. I think he is primarily concerned with the secure transmission? He can easily convert to/from SecureString at either end if needed. – Thomas N Apr 09 '19 at 13:16