Skip to main content
GameDev.net gamedev.net
🔒 Locked

Signing a message

Started by DvDmanDT May 22, 2012 at 12:16 PM 12 replies 3.4k views
Original Post
DvDmanDT
DvDmanDT
Hi, I want to use an asymmetric encryption algorithm (RSA?) to encrypt/sign a message on a server and decrypt/verify that message on a client. My idea is to have the private key on the server and the public key included hardcoded into the client. Sounds simple enough, but all my google attempts just result in examples where the keys are generated on the fly which is completely useless in my case.

Any pointers?
taby
taby
You have it a little bit backwards. As I'm sure you already realize, the public key is what encrypts the data, and the private key is what decrypts the data. So, according to your proposed plan, you'd need to store the private key on the client, not on the server. That said, you could give each client their own unique private key, or you could also just use symmetric-key encryption (again, where each client gets a unique key).

I used to have an RSA keygen / encryption / decryption code, but I lost it. The book Mastering Algorithms with C gives a basic rundown of the entire process. Writing the code for it by following their explanation is fairly easy. You'll need a big integer library to do anything useful with RSA, and I recommend MAPM mostly because it's not a pain to get used to and it's not excessively slow. Really, the hardest part about RSA is learning where and how to leverage the Chinese remainder theorem in order to avoid overcomplicated arithmetic.

Going beyond standalone RSA, you could consider using TLS, which uses asymmetric-key encryption (ie. RSA) for the initial handshake, and symmetric-key encryption (ie. AES) for the remainder. There are plenty of pre-written libraries for you to pick from.

Also, I see that there is a C# tag on this post. Microsoft has the CryptoAPI that you may want to consider taking a look through.
DvDmanDT
DvDmanDT
Depends on what one is trying to achieve. I want to verify that a message came from the right sender/is authentic, I'm not really interested in secure communications. .NET includes RSA functionality among other things which I intend to use to avoid more dependencies and potential licensing problems.

It's for product activation. Nothing too fancy, just keeping honest people honest. We are currently using a library/service for this but wish to implement our own due to licensing issues and technical problems.
alvaro
alvaro
Another library you may want to use for big integers is GMP. I don't have any experience with MAPM, but I've always found GMP easy to work with, and they already have functions to compute things that you'll need for RSA, like a probabilistic primality test (mpz_probab_prime_p), modular inverse(mpz_invert) and modular exponentiation (mpz_powm).

There are also handy C++ wrapper classes, but the last time I used the library they didn't expose all the functionality of the C interface.
taby
taby

Depends on what one is trying to achieve. I want to verify that a message came from the right sender/is authentic, I'm not really interested in secure communications. .NET includes RSA functionality among other things which I intend to use to avoid more dependencies and potential licensing problems.

It's for product activation. Nothing too fancy, just keeping honest people honest. We are currently using a library/service for this but wish to implement our own due to licensing issues and technical problems.


OK, then SSPI or CryptoAPI is something for you to look at. Best of luck. smile.png

I apologize for not being able read your mind on what you wanted, or what you've already looked into, or that your question mark was not really a question mark. I'll try harder next time. smile.png

You're welcome.
DvDmanDT
DvDmanDT

Another library you may want to use for big integers is GMP. I don't have any experience with MAPM, but I've always found GMP easy to work with, and they already have functions to compute things that you'll need for RSA, like a probabilistic primality test (mpz_probab_prime_p), modular inverse(mpz_invert) and modular exponentiation (mpz_powm).

There are also handy C++ wrapper classes, but the last time I used the library they didn't expose all the functionality of the C interface.


The G in GMP stands for GNU. For me that pretty much means "don't even consider looking at it or it'll ruin your life" as I'm writing closed source software..

I just noticed how to use key blobs. That was probably what I was looking for.
btower
btower
I've written this once for VB.NET. I hope you can read it and easily convert it to C#. It's not very difficult to do in .NET. I just use these two small helper classes for it.

As you mentioned, you found a lot of pointers on how to generate the certificate itself - so I won't explain that here.


Imports System.IO
Imports System.Security.Cryptography
Imports System.Security.Cryptography.X509Certificates

Namespace Security.Cryptography
''' <summary>
''' Provides methods to sign and verify SHA1/RSA 1024 bits signatures.
''' </summary>
''' <remarks></remarks>
<DebuggerNonUserCode()> _
Public NotInheritable Class SHA1RSASigner
''' <summary>
''' Signs a string using SHA-1 hashing, then 1024 bits RSA signing (private key is necessairy) and returns the result base64 encoded.
''' </summary>
''' <param name="cert">A certificate containing a private key. This will be used to make an authentic signature.</param>
''' <param name="stringToSign">The string to make a signature for.</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function CreateSignatureBase64(ByVal cert As Certificate, ByVal stringToSign As String) As String
If (cert Is Nothing) Then Return String.Empty
If (Not cert.HasPrivateKey) Then Return String.Empty
If (String.IsNullOrEmpty(stringToSign)) Then Return String.Empty
Dim bytes() As Byte = Encoding.ASCII.GetBytes(stringToSign)
Dim sha As New SHA1Managed
sha.Initialize()
Dim shaDigest() As Byte = sha.ComputeHash(bytes)
Dim rsa As RSACryptoServiceProvider = cert.GetRSACryptoServiceProvider()
Dim rsaDigest() As Byte = rsa.SignHash(shaDigest, CryptoConfig.MapNameToOID("SHA1"))
Return Convert.ToBase64String(rsaDigest)
End Function
''' <summary>
''' Verifies a (plain-text) String based on a digital signature that is SHA-1 hashed, then RSA 1024 bits signed and finally base64 encoded.
''' </summary>
''' <param name="publicKey">The public key of the one who signed the message (the iDEAL acquirer's certificate).</param>
''' <param name="originalString">Plain-text string containing the signed text.</param>
''' <param name="base64TokenToVerify">The base64 encoded digital signature created by the sender (iDEAL acquirer).</param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function VerifySignatureBase64(ByVal publicKey As Certificate, ByVal originalString As String, ByVal base64TokenToVerify As String) As Boolean
If (publicKey Is Nothing) Then Return False
If (String.IsNullOrEmpty(originalString)) Then Return False
If (String.IsNullOrEmpty(base64TokenToVerify)) Then Return False
Dim bytes() As Byte = Encoding.ASCII.GetBytes(originalString)
Dim sha As New SHA1Managed
sha.Initialize()
Dim shaDigestOriginal() As Byte = sha.ComputeHash(bytes)
bytes = Convert.FromBase64String(base64TokenToVerify)
Dim rsa As RSACryptoServiceProvider = publicKey.GetRSACryptoServiceProvider()
Return rsa.VerifyHash(shaDigestOriginal, CryptoConfig.MapNameToOID("SHA1"), bytes)
End Function
End Class
End Namespace



Imports System.IO
Imports System.Security.Cryptography
Imports System.Security.Cryptography.X509Certificates

Namespace Security.Cryptography
''' <summary>
''' Represents a X509 certificate (PKCS #12).
''' </summary>
''' <remarks></remarks>
<DebuggerNonUserCode()> _
Public NotInheritable Class Certificate
Private _certificate As X509Certificate2
#Region " Properties "
''' <summary>
''' Gets the fingerprint of the certificate.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property FingerPrint() As String
Get
If (_certificate Is Nothing) Then Return String.Empty
Return _certificate.Thumbprint
End Get
End Property
''' <summary>
''' Gets wether this certificate contains a private key.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property HasPrivateKey() As Boolean
Get
If (_certificate Is Nothing) Then Return False
Return _certificate.HasPrivateKey
End Get
End Property
''' <summary>
''' Gets the private key of this certificate, if available.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property PrivateKey() As String
Get
If ((_certificate Is Nothing) OrElse (_certificate.PrivateKey Is Nothing)) Then Throw New CertificateException("This certificate does not contain a private key.")
Try
Return _certificate.PrivateKey.ToXmlString(True)
Catch ex As Exception
Throw New CertificateException(String.Concat(ex.Message, " See InnerException for more details."), ex)
End Try
End Get
End Property
''' <summary>
''' Gets the public key of this certificate, if available.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property PublicKey() As String
Get
If ((_certificate Is Nothing) OrElse (_certificate.PublicKey Is Nothing) OrElse (_certificate.PublicKey.Key Is Nothing)) Then Throw New CertificateException("This certificate does not contain a public key.")
Try
Return _certificate.PublicKey.Key.ToXmlString(False)
Catch ex As Exception
Throw New CertificateException(String.Concat(ex.Message, " See InnerException for more details."), ex)
End Try
End Get
End Property
#End Region
''' <summary>
''' Loads an existing certificate based on a PKCS #12 file.
''' </summary>
''' <param name="fileName">Virtual path to a PKCS #12 certificate file. Supports CER (publickey) and P12 (privatekey) files.</param>
''' <param name="password">Optional password if the file requires one.</param>
Public Sub New(ByVal fileName As String, Optional ByVal password As String = Nothing)
Try
Dim certificateBytes() As Byte
Dim context As HttpContext = HttpContext.Current
If (context Is Nothing) Then
certificateBytes = File.ReadAllBytes(fileName.Replace("/"c, "\"c).Replace("~", Environment.CurrentDirectory))
Else
certificateBytes = File.ReadAllBytes(context.Request.MapPath(fileName))
End If
If ((certificateBytes IsNot Nothing) AndAlso (certificateBytes.Length > 0)) Then
_certificate = New X509Certificate2(certificateBytes, password, X509KeyStorageFlags.Exportable Or X509KeyStorageFlags.MachineKeySet Or X509KeyStorageFlags.PersistKeySet)
End If
Catch ex As Exception
Throw New CertificateException(String.Concat("Failed to load certificate file: ", ex.Message, " See InnerException for more details."), ex)
End Try
End Sub
''' <summary>
''' Creates an RSA Crypto Service provider that is initialized with this certificate's key. The private key is used, if possible. Keysize is set to 1024 bits.
''' </summary>
Public Function GetRSACryptoServiceProvider() As RSACryptoServiceProvider
RSACryptoServiceProvider.UseMachineKeyStore = True
Dim result As New RSACryptoServiceProvider
result.KeySize = 1024
If (Me.HasPrivateKey) Then
result.FromXmlString(Me.PrivateKey)
Else
result.FromXmlString(Me.PublicKey)
End If
Return result
End Function
End Class
End Namespace
taby
taby

I've written this once for VB.NET. I hope you can read it and easily convert it to C#. It's not very difficult to do in .NET. I just use these two small helper classes for it.
...


That's handy code to have. Thanks for that.
alvaro
alvaro

The G in GMP stands for GNU. For me that pretty much means "don't even consider looking at it or it'll ruin your life" as I'm writing closed source software..


So you don't understand what the LGPL is... Your loss.
DvDmanDT
DvDmanDT
Doesn't the LGPL require me to allow people to replace that library with their own modified version and that I do not restrict reverse engineering the portion of my application which uses that library (for debugging purposes)? That is in this case to allow people to reverse engineer my product activation mechanism.
Washu
Washu
Product activation is a tricky area, and nothing you do (literally nothing) will prevent someone from being able to "steal" your software. You can make it a tinny bit harder though, which is what product activation mainly aims to do. But at the end of the day, if your product is valuable, someone will write a small little executable that will bypass or otherwise disable your activation code. Making it trivial to steal your software.

That being said, you should really be looking into open source frameworks for this, as there are plenty of them. One example would be AquaticPrime, which also links to a very nice article on the subject.

As for the case of the keys being generated on the fly, how are the examples useless? I mean, its just a matter of changing a tiny bit of code, to use a pre-generated key instead of a runtime-generated one.
In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.
alvaro
alvaro

Doesn't the LGPL require me to allow people to replace that library with their own modified version and that I do not restrict reverse engineering the portion of my application which uses that library (for debugging purposes)? That is in this case to allow people to reverse engineer my product activation mechanism.


I think as long as you link with the library dynamically, you are OK. If you think that makes debugging too easy... Well, if someone is determined to break your registration scheme, I don't think using a different bignum library would make things particularly harder. But I see your point.
JohnnyCode
JohnnyCode
in fact,
-there is an authority of encryption that nows how data was encrypt
-you recieve entropic data and ask authority for decryption key
- you do not ask authority if you have its certificate,( through network for decryption key)
- you have the private key extracted from certificate you have
- you decrypt message encrypted under that certifiacate
- client knows you have used valid certificate to encrypt the data
- exchange encrypeted data in the manner of protocol
DvDmanDT
DvDmanDT
Washu: The problem with the examples was just that, I couldn't find how to pregenerate those keys. As in, I didn't see any reasonable way to export/import them. Key blobs appear to be exactly what I was looking for though.

As I said in my second post, it's about keeping honest people honest. I want to protect against keygens, but won't bother trying to prevent cracks etc. We are much more interested in cross-plattformness, flexibility in licensing models and so on. We need to support node-locked licenses, volume licenses, academic licenses, different feature levels, time-limited licenses and so on.


I think as long as you link with the library dynamically, you are OK. If you think that makes debugging too easy... Well, if someone is determined to break your registration scheme, I don't think using a different bignum library would make things particularly harder. But I see your point.

Well, using a built-in .NET library or statically linked library would make it somewhat harder, but difficulty isn't the concern, it's more the fact that I must allow them to reverse engineer (portions of) my application.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.