0

I'm attempting to write a simple check on my server for version data. The result is going to be a JSON response containing the current version #, whether the version requires an update for the program to keep functioning or not, and some HTML data with a feature list for the new release.

What I'm trying to do is send this data from my webserver to my C# application in a secure way that the end user cannot spoof.

I had initially looked at using RSA to encrypt it with a private key on the server side and decode it on the client using a public key, but apparently RSA (at least native .NET RSA) cannot handle this situation, as it is expecting to decode using the private key.

I'm looking for any tips or suggestions on how to do this. I did see that RSA has a verifyhash or verifydata method, but I can't seem to find any decent examples of how to do this securely.. what is this point of using RSA if you're just sending a hash anyway? And if the client is .NET, they could easily decode how the hash is being created and spoof it themselves...

I'm also looking for a .NET (3.5) solution for this, as the application is currently standalone and I would rather not add on and dependencies.

I'm not looking for any code, just some helpful topics to Google or something, as what I have been searching is not returning anything that seems to be useful.

Thanks,

Smitty

Edit to add some of the links I found saying you cannot decode using the public key

Error occurred while decoding OAEP padding

C# RSA encrypt/decrypt throws exception

Travis
  • 76
  • 1
  • 11
  • I don't know much, well hardly anything, about encryption, but I find it hard to believe that the .Net RSA implementation does not allow use of public key decryption. That's the whole idea behind RSA. – RenniePet Dec 13 '15 at 03:14
  • That's what I thought too, but everything I found says it is not meant for that. It is meant to be encrypted with the public key and decoded with the private key. I am trying to find the links that said that, and when I do I will modify my question to include them. – Travis Dec 13 '15 at 03:18
  • Hmmm, yes, I have indeed displayed my lack of knowledge about encryption. OK, can you maybe send info from the server to the client unencrypted, and the client assumes it may be suspect. The client makes a hash of the info plus a random number and sends an encrypted message to the server (public key encrypted) saying, "are you really who you claim to be?" Your server then replies, in clear text, "yes, you can trust me, your hash was correct and your random number is x!" – RenniePet Dec 13 '15 at 03:29
  • That's actually a pretty ingenious way to handle it, however it does mean a second call to the server... which most likely will not be a problem whatsoever.... hmm. I am interested in seeing what other people think, and if there is a best practice in this situation, but your solution definitely solves my problem, so you have my thanks! – Travis Dec 13 '15 at 03:43

1 Answers1

0

The accepted answer in the first link you posted is not entirely correct. The .NET framework does support this operation, but under a different name.

In asymmetric cryptosystems like RSA, non-repudiation can be established by "encrypting" with the private key and "decrypting" with the public key. Because this cryptographic operation is intended to prevent tampering and not for secrecy, it is known as (and referred to in the framework APIs) as data signing, not encryption.

The correct methods to use are, as you suggested, SignData/SignHash to sign the data with the private key, and VerifyData/VerifyHash to verify the data with the public key.

The mathematical operations involved with encrypting with the private key place a practical limitation on the size of the data that can be signed. As a result, it is customary to hash the data being signed with a cryptographic hash (e.g., SHA1) and then sign the hash. The .NET framework API enforces this restriction.

As a simple example, you could use the following code to sign the data on the server.

public byte[] SignData(RSAParameters privateParameters, byte[] data)
    {
        using (var csp = new RSACryptoServiceProvider())
        using (var sha1 = new SHA1CryptoServiceProvider())
        {
            csp.ImportParameters(privateParameters);
            return csp.SignData(data, sha1);
        }
    }

The client can verify the data is signed with the private key by providing (a) the public RSA parameters, (b) the signature, and (c) the original data. In addition, the client must know the hash algorithm used to generate the signature:

public bool VerifySignature(RSAParameters publicParameters, byte[] data, byte[] signature)
{
    using (var csp = new RSACryptoServiceProvider())
    using (var sha1 = new SHA1CryptoServiceProvider())
    {
        csp.ImportParameters(publicParameters);
        return csp.VerifyData(data, sha1, signature);
    }
}

You can test that VerifySignature returns false if the signature or data is tampered with.

As a final note, you write that the intent is to provide "secure way that the end user cannot spoof". With a digital signature, the user can have high confidence that the data being provided to it is from a valid source. It will not protect against attacks in which the end user is complicit, such as causing the check to be bypassed or replacing the public key with one the end user controls.

drf
  • 8,461
  • 32
  • 50
  • Thank you for the explanation of signing and verifying the data. Your code was essentially what I tried before but it wasn't verifying the data and always returning false. With your explanation I pushed forward and eventually nailed down the issue to my incorrect import of my public key. Thanks again! – Travis Dec 13 '15 at 05:34