Using Base64 encoded Public Key to verify RSA signature
Asked Answered
L

4

21

In a nutshell, this is my problem:

private string publicKeyString = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVGUzbydMZS+fnkGTsUkDKEyFOGwghR234d5GjPnMIC0RFtXtw2tdcNM8I9Qk+h6fnPHiA7r27iHBfdxTP3oegQJWpbY2RMwSmOs02eQqpKx4QtIjWqkKk2Gmck5cll9GCoI8AUAA5e0D02T0ZgINDmo5yGPhGAAmqYrm8YiupwQIDAQAB";

/* Some transformation required, using publicKeyString to initiate a new RSACryptoServiceProvider object
*/

//for now:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();

byte[] selfComputedHash = new byte[]; //left out of the example
byte[] signature = new byte[];

bool result = rsa.VerifyHash(selfComputedHash, CryptoConfig.MapNameToOID("SHA1"), signature);

As you can see, the problem is initiating a new RSACryptoServiceProvider with the given Base64 encoded public key string. I've been able to do the instantiation using an object RSAParameters, loaded with the byte[]'s for Modulus and Exponent derived from this public key string using an OpenSSL shell command. But since this public key may change in the future I want to be able to store it in its original form in a database. There must be a more straightforward way of dealing with this.

A lot of the examples I've read so far avoid this problem by exporting and importing the generated private and public keys to and from a key-container object and use it in the same piece of code and thus not 'transferring' the key in some string form out of memory. Some people have expressed the same problem, both here on StackOverflow and on other sites, but I have not been able to find a satisfying answer yet.

Any idea's are more than welcome.

Background info: My communication partner computes a 20-byte SHA1-hash from an input string of variable length, composed of the information contained in several fields of an ASCII encoded message. This hash is then RSA-signed with my partner's private key and sent along with the ASCII message to me. Upon arrival, I compute the SHA1 hash myself, using the same fields from the ASCII message and then try to verify if these fields were not altered by calling VerifyHash.

The key is provided in 2 forms: regular and 'noNL'. The noNL version is included in the code above, the regular version is this:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVGUzbydMZS+fnkGTsUkDKEyFO
GwghR234d5GjPnMIC0RFtXtw2tdcNM8I9Qk+h6fnPHiA7r27iHBfdxTP3oegQJWp
bY2RMwSmOs02eQqpKx4QtIjWqkKk2Gmck5cll9GCoI8AUAA5e0D02T0ZgINDmo5y
GPhGAAmqYrm8YiupwQIDAQAB
-----END PUBLIC KEY-----
Lorylose answered 14/2, 2012 at 20:28 Comment(0)
G
18

Your string is the base64 encoding of a SubjectPublicKeyInfo. You can use Bouncycastle.net to decode it like this:

byte[] publicKeyBytes = Convert.FromBase64String(publicKeyString);
AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(publicKeyBytes);
RsaKeyParameters rsaKeyParameters = (RsaKeyParameters) asymmetricKeyParameter;
RSAParameters rsaParameters = new RSAParameters();
rsaParameters.Modulus = rsaKeyParameters.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = rsaKeyParameters.Exponent.ToByteArrayUnsigned();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
Gainful answered 15/2, 2012 at 8:33 Comment(3)
Thank you, that's pretty easy. I'll look into the BouncyCastle library, but are you sure there is no way to accomplish this using only the Cryptography library in .NET itself?Lorylose
@jscheppers: You can do it manually as poupou suggests, but there is no direct support for the format in .NET. A quick search reveals this example for decoding manually: jensign.com/JavaScience/dotnet/pempublic/pempublic.cs , but that one looks a bit messy.Gainful
I think I prefer your BouncyCastle-solution then ;) I've already tried it in my current implementation and it works. I'll mark your post as the answer!Lorylose
D
4

First base64 is only an encoding of some binary data. There's more than one way to encode, in binary, an RSA public key. However if you got this from OpenSSL it's likely a DER-encoded RSAPublicKey structure.

 RSAPublicKey ::= SEQUENCE {
     modulus            INTEGER,    -- n
     publicExponent     INTEGER  }  -- e

In general you would need a ASN.1 decoder, Mono.Security.dll provides one, but for such a simple structure you might want to do it by hand since ASN.1 is basically a Tag, Length and a Value.

Deonnadeonne answered 14/2, 2012 at 21:14 Comment(2)
The organisation that provides the public keys does not mention the format of the key, but since I'm able to 'read' them with OpenSSL and since the key contains both the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- tags (I'll add this to my original post) I'm fairly certain it's a DER encodes key. How would you go about converting this string into an ASN.1 structure by hand? When bytes come into play, I'm not that sure of myself :)Lorylose
Note that it is actually a SubjectPublicKeyInfo containing an RSAPublicKey.Gainful
M
1

Without an external library, you can get exponent and modulus from a DER-encoded X.509 public key as follows:

byte[] publicKeyBytes = Convert.FromBase64String(publicKeyString);
var rsa = new RSACryptoServiceProvider();
rsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _);
var rsaParams = rsa.ExportParameters(false);
var exponent = rsaParams.Exponent;
var modulus = rsaParams.Modulus;

see https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.rsa.importsubjectpublickeyinfo?view=net-7.0

Magdaleno answered 26/6, 2023 at 11:30 Comment(2)
I am unable to verify your answer in my use case, but this seems to be the most applicable solution to my original problem. Thank you for taking the time to answer it more than 11 years later, I hope this will help others.Lorylose
The ImportSubjectPublicKeyInfo() method only exists on .NET Core 3.0 and above. If anyone needs it on .NET framework, look hereSugden
M
0

If your partner also uses .NET, he/she can derive from Mono's makecert https://github.com/mono/mono/blob/bf55818da11240bd108dc51b374fae3d3b5482ce/mcs/tools/security/makecert.cs to generate certificate files and send to you directly.

In that case, you can easily load certificates instead of raw bytes,

http://www.lextm.com/2012/02/simple-publicprivate-key-signing-sample-code/

Melanosis answered 15/2, 2012 at 4:4 Comment(1)
That would be a good possibility, but I have no control over the way the keys are generated. The keys are provided as-is, no certificate is available where this key is present in.Lorylose

© 2022 - 2024 — McMap. All rights reserved.