Reverse usage of public/private RSA encryption keys for licensing?

  • Thread starter charismatic_evangelist
  • Start date
C

charismatic_evangelist

The way that RSACryptoServiceProvider works is that we encrypt with a
public key and decrypt with a private key. This architecture works
great for people sending messages to me. However, I now have the
opposite application:

I want to put a license file on a destination computer and be sure
that it remains unaltered. It would be great if I could encrypt it
with a private key at our office and transmit the public key and the
encrypted file to the destination computer, where anybody can decrypt
it and read it, but not change it because we have the private key at
our office.

I've read posts to "simply reverse the public and private RSA keys"
but tried several variations of switching RsaParameters.D, DP, P, DQ,
Modulus and others, to no avail.

I'm sure that a solution exists, just that I do not know it.

How do I, in C# .NET, send a license file to a destination computer,
with a public key, anybody can read it, but only we can change it?

Thanks.
 
R

Rob Teixeira

Reversing the public and private key bytes won't work at all. They are
mathematically linked.

In order to use the private key to ensure data hasn't been tampered with and
originated from the sole person possessing the private key, you must use the
RSACryptoServiceProvider's SignData method. The clients can then use your
RSA public key and call the RSACryptoServiceProvider's VerifyData method.

If you want to get more formal, you can use the RSAPKCS1SignatureFormatter
and RSAPKCS1SignatureDeformatter classes in conjunction with the
RSACryptoServiceProvider, and this will create standard RSA PKCS#1 version
1.5 signatures.

Just as a note, this won't encrypt the original data, but will create a
signature. The client can verify the signature against the same data to
ensure the data hasn't been altered.

-Rob Teixeira
 
W

William Stacey [MVP]

It works both ways otherwise you would not be able to decrypt the signature
to get the clear hash to validate the new calculated hash against. Other
implementations allow this directly. .Net allows encrypting the hash only
with the private key. And only allows you to verify the signature
(encrypted hash) using the public key. That is probably a very good thing
as it would probably confuse the heck out of people if it allowed otherwise.
Keeping your private key private is the major idea. Hash your license using
SHA1 or something and encrypt the hash using private key.
RSACryptoServiceProvider.SignData and .VerifyData are easy methods to do
all this for you. The rest of your license could be clear text (i.e. xml)
with the signature in base64. The client gets the signature bytes and runs
VerifyData passing in the clear text, hash algo, and encrypted signature
bytes. The method computes the hash and compares to the decrypted hash and
return true if equal; otherwise false. Naturally, you don't include the
signature when calculating your client hash as that was not included in the
server side hash. That way you validate that only the owner of the private
key could have signed the license. However, the client's public key still
needs to be secured as good as possible in your code or other. Otherwise a
creative user could change the public key and sign the license with their
own key pair. Obfuscating code using encryption can help in this regard.
Some obfuscators can prevent (at least for now) using ildasm/ilasm
round-tripping. Naturally if you can use ildasm on your assembly, then you
could change the public key or just remove your license "if" test(s),
thereby thwarting your public key security all together (e.g. walking around
your 1000ft high wall). You can't really get 100% protection, but you can
make it much harder for most.
 
V

Valery Pryamikov

Hi,
Use RSA singing. RSA signature with strong hash (ie. SHA1) provides much
better security than encryption of data with RSA private key (ie. what you
asked about). In fact, RSA signature was proven to be secure in random
oracle model with non-tight reduction to RSA problem (and it's even possible
to achieve tight reduction simply by adding of one single extra radom bit as
it was shown in Katz-Wang signature).

-Valery.
http://www.harper.no/valery
 
W

William Stacey [MVP]

Use RSA singing. RSA signature with strong hash (ie. SHA1) provides much
better security than encryption of data with RSA private key (ie. what you
asked about).

Hi Valery. That seems to suggest that RSA signature entails more then just
encrypting the hash bytes? Is there more going on? TIA
 
V

Valery Pryamikov

It's just that there is a theorem that proves that if we use random oracle
as hash (highest level of collision resistance), than existence of an
algorithm to forge signature (RSA private key encryption of random oracle
hash) gives ability to solve RSA problem with sufficient amount of signing
requests (non-tight reduction means that amount of signing requests could be
quite high). Hash is essential part of this theorem - ie. no hash - no
security prove. SHA1 doesn't provide the same level of collision resistance,
but having really good collision resistances properties (none has managed to
find SHA1 collision so far) it presents good practical substitution to the
random oracle. Using private exponent for encrypting arbitrary data doesn't
have any security prove.

-Valery.
http://www.harper.no/valery
 
V

Valery Pryamikov

In other words, assuming special hash properties, there is a theorem that
proves that forging of RSA signature is at least as difficult as RSA problem
it-self. And without hash, no such proof exists - ie. without hash it may be
simpler to forger than to solve RSA problem.

-Valery.
http://www.harper.no/valery
 
C

charismatic_evangelist

Thanks everybody for all your expertise! I (believe that I) have a
solution:

#1. Here at our office, I generate a RSA private and public key via:

System.Security.Cryptography.RSACryptoServiceProvider RSA;
RSA = new RSACryptoServiceProvider();

and save the keys via:

string privateParameters = RSA.ToXmlString(true));
string publicParameters = RSA.ToXmlString(false));

I then hash my data that I want to ensure non-tampering via:

byte[] rawData, hash;
System.Security.Cryptography.HashAlgorithm HashMan;
HashMan = new System.Security.Cryptography.SHA1CryptoServiceProvider();
hash = HashMan.ComputeHash(rawData);

I then make a signature of this hash with the RSA private Crypto:

signatureFormatter = new RSAPKCS1SignatureFormatter(RSA);
signatureFormatter.SetHashAlgorithm("SHA1");
signature = signatureFormatter.CreateSignature(hash);

.... and prepend the 128-byte signature and the 20-byte hash in front
of the raw data and write back out to a new file, which becomes my
signed license file.

#2. At the destination computers, I instantiate a RSA Crypto provider:

RSAPublic = new RSACryptoServiceProvider();

and load it ONLY WITH THE PUBLIC KEY:

RSAPublic.FromXmlString(publicParameters);

I extract from the raw data the claimed signature and the claimed
hash:

byte[] rawSignature; // bytes 0 - 127 of raw data
byte[] rawHash; // bytes 128 - 147 of raw data

I skip over the first 148 (128 + 20) bytes of data to be validated and
compute a SHA1 hash of the bytes to be validated. If my hash equals
the hash stored in the file at location 128 for 20 bytes, then I
proceed, else error.

Then, I instantiate a signatureDeformatter with the public RSA Crypto:

signatureDeformatter = new RSAPKCS1SignatureDeformatter(RSAPublic);
signatureDeformatter.SetHashAlgorithm("SHA1");

and, finally, verify the signature with:

signatureDeformatter.VerifySignature(rawHash, rawSignature);

It seems to work with the few files I've tried. I thank all of you
for your valuabe input.
 
W

William Stacey [MVP]

Sound pretty good. However I would not transfer the hash twice (once
encrypted and once clear). Your hash is already in the signature, but
encrypted. Use VerifyData to verify signature using your data. Internally
it does a new hash of the data, decrypts the signature to get the orig hash
and compares the two.

--
William Stacey, MVP
http://mvp.support.microsoft.com

Thanks everybody for all your expertise! I (believe that I) have a
solution:

#1. Here at our office, I generate a RSA private and public key via:

System.Security.Cryptography.RSACryptoServiceProvider RSA;
RSA = new RSACryptoServiceProvider();

and save the keys via:

string privateParameters = RSA.ToXmlString(true));
string publicParameters = RSA.ToXmlString(false));

I then hash my data that I want to ensure non-tampering via:

byte[] rawData, hash;
System.Security.Cryptography.HashAlgorithm HashMan;
HashMan = new System.Security.Cryptography.SHA1CryptoServiceProvider();
hash = HashMan.ComputeHash(rawData);

I then make a signature of this hash with the RSA private Crypto:

signatureFormatter = new RSAPKCS1SignatureFormatter(RSA);
signatureFormatter.SetHashAlgorithm("SHA1");
signature = signatureFormatter.CreateSignature(hash);

... and prepend the 128-byte signature and the 20-byte hash in front
of the raw data and write back out to a new file, which becomes my
signed license file.

#2. At the destination computers, I instantiate a RSA Crypto provider:

RSAPublic = new RSACryptoServiceProvider();

and load it ONLY WITH THE PUBLIC KEY:

RSAPublic.FromXmlString(publicParameters);

I extract from the raw data the claimed signature and the claimed
hash:

byte[] rawSignature; // bytes 0 - 127 of raw data
byte[] rawHash; // bytes 128 - 147 of raw data

I skip over the first 148 (128 + 20) bytes of data to be validated and
compute a SHA1 hash of the bytes to be validated. If my hash equals
the hash stored in the file at location 128 for 20 bytes, then I
proceed, else error.

Then, I instantiate a signatureDeformatter with the public RSA Crypto:

signatureDeformatter = new RSAPKCS1SignatureDeformatter(RSAPublic);
signatureDeformatter.SetHashAlgorithm("SHA1");

and, finally, verify the signature with:

signatureDeformatter.VerifySignature(rawHash, rawSignature);

It seems to work with the few files I've tried. I thank all of you
for your valuabe input.


"Valery Pryamikov" <[email protected]> wrote in message
In other words, assuming special hash properties, there is a theorem that
proves that forging of RSA signature is at least as difficult as RSA problem
it-self. And without hash, no such proof exists - ie. without hash it may be
simpler to forger than to solve RSA problem.

-Valery.
http://www.harper.no/valery
 
M

Morten Dahl

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

(e-mail address removed) wrote:
| System.Security.Cryptography.RSACryptoServiceProvider RSA;
| RSA = new RSACryptoServiceProvider();

I don't know if this still applies, but I was taught to use

RSA rsa = RSA.Create(); // By default RSACryptoServiceProvider

instead of

RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

- - I've haven't tested if this breaks any constrains in the formatter
though. Anyway, it could be used for the hash algorithm as well:

SHA1 sha = SHA1.Create(); // By default SHA1CryptoServiceProvider

This way you can change the implementation of RSA and SHA1 without
recompiling the source.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (MingW32)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBpIJ42XTueCwB3DoRAgAEAJ4+Fb+dJc3xsaJxzkTPcVmlHr7JiQCeMAl8
kfq+xH4uoZyZqmqdO1k92EE=
=acJZ
-----END PGP SIGNATURE-----
 
W

William Stacey [MVP]

RSA rsa = RSA.Create(); // By default RSACryptoServiceProvider
instead of
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

TMK, it is the same thing as RSA is an abstract base class and
RSACryptoServiceProvider is currently the only concret class that derives
from RSA. Some other stuff goes on in Create, but you end up with an
RSACryptoServiceProvider:

RSA rsa = RSA.Create();
Console.WriteLine("Type:"+rsa.GetType().ToString());
--Output
Type:System.Security.Cryptography.RSACryptoServiceProvider
 
C

charismatic_evangelist

I thank William Stacey for teaching me that I do not need to store
hash twice, and Morten Dahl for: RSA rsa = RSA.Create();

I now have a solution (to verify that our license has not been
tampered with) that works.

Further, I have recently become aware of X509 Certificates. Because
Microsoft C# .NET has a whole namespace for X509Certificates:

1. Can I alternatively use X509 to verify that our license has
remained untouched?

2. Is it worth the additional effort?

3. Is the X509 buzzword (A) so prevalent and (B) so respected that our
marketeers can brag about our using it and make our software more
marketable? :)

Happy Thanksgiving!
 
V

Valery Pryamikov

Hi,
Certificates bind public key to identity and validity timespan.
Additionally, X509 standard defines series of protocols such as exchange,
validation and keys revocation. If you find that you need any of protocols
or features defined by X509 standard (and you most probably will) - then you
must definitely use the standard instead of attempting to invent your own
scheme.

-Valery.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top