Verifying a RSA signature made by Crypto Node.JS in C# - c#

I'm trying to build a web service using Express/NodeJS which signs a piece of information. The signed data is received and verified by a client written in C#. You'll have to forgive my inexperience in cryptography and its associated technologies.
First off, I generate a certificate for the C# client and a private key for the NodeJS application using OpenSSL;
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days
365
In the NodeJS application, I have the following code;
const crypto = require('crypto')
const fs = require('fs')
var pem = fs.readFileSync('./keys/key.pem');
var key = pem.toString('ascii');
var privateKey = crypto.createPrivateKey({
'key': key,
'format': 'pem',
'passphrase': '<PASSPHRASE>',
});
function sign(identifier){
var sign = crypto.createSign('RSA-SHA256');
sign.update(identifier);
var sig = sign.sign(privateKey, 'base64');
return sig;
}
exports.sign = sign;
In this case, the parameter identifier is the data to be signed. The client will receive this, and the signature generated, sig.
In the C# client I have the following snippet;
X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(pub));
using (var sha256 = SHA256.Create())
{
using (var rsa = cert.GetRSAPublicKey())
{
bool results = rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Console.WriteLine(results.ToString());
}
}
The pub is the generated certificate in Base64, it is stored in a const string. The data contains the same information as identifier in the NodeJS application, but it's converted to bytes using Convert.FromBase64String(...), and likewise the signature is the data returned from sig in the NodeJS application, only converted from Base64 to byte data.
When all information is inserted, VerifyData() returns false, this leads me to believe that there's some kind of missmatch between the cryptographic configurations of the web service and the client.
Any ideas?

As pointed out in the comments, the problem was that data in the C# client was converted to from Base64 when the data in the NodeJS application read from UTF-8.
The solution was to convert the string using Encoding.UTF8.GetBytes()
Thanks for the quick response!

Related

Encrypt the content using AES and mimekit

I looked at several questions on stackoverflow and I'm not able to find answer pertaining to my requirement. So I'm posting this question:
My requirement says:
The content must be encrypted using AES in FIPS Publication 197 (incorporated by reference in provision 6 of this Standard), and RFC 3565 (incorporated by reference in provision 6 of this Standard).
And it should be signed with RSA algorithm specified in RFC 4056.
I saw this post: MimeKit encrypt Message with AES
CmsRecipient CmsRecipient = new CmsRecipient("mail.cer");
CmsRecipient.EncryptionAlgorithms = new EncryptionAlgorithm[] { EncryptionAlgorithm.AES256 };
to1.Add(CmsRecipient);
var signed = MultipartSigned.Create(ctx, signer, multipart);
var encrypted = ApplicationPkcs7Mime.Encrypt(ctx, to1, signed);
message.Body = MultipartSigned.Create(ctx, signer, encrypted);
It seems like in the above code, we expect the AES key to be part of the "mail.cer" certificate?
How do we encrypt the mail content using AES key and mimekit libraries, provided I've a secret key in the form of a string?

How to sign using new keyvault client from azure.security.keyvault

I recently started to replace the old Azure KeyVault client from the Microsoft.Azure.KeyVaultnamespace with the newer one in Azure.Security.KeyVault.
This works without any issues when getting secrets and certificates, but I am not sure how to sign anymore
How do I sign using the new keyvault client?
Did you check Azure SDK Azure.Security.KeyVault.Keys package on github?
Example code from that source:
SignResult rsaSignDataResult = rsaCryptoClient.SignData(SignatureAlgorithm.RS256, data);
Debug.WriteLine($"Signed data using the algorithm {rsaSignDataResult.Algorithm}, with key {rsaSignDataResult.KeyId}. The resulting signature is {Convert.ToBase64String(rsaSignDataResult.Signature)}");
For azure key vault, we need to create a signature from a digest using the specified key.
So, you could refer to the below code to sign some arbitrary data and verify the signatures using the CryptographyClient with both the EC and RSA keys we created.
byte[] digest = null;
using (HashAlgorithm hashAlgo = SHA256.Create())
{
digest = hashAlgo.ComputeHash(data);
}
SignResult rsaSignResult = rsaCryptoClient.Sign(SignatureAlgorithm.RS256, digest);
If you want to sign certificate you could refer to this article.

Encrypt with RSACryptoServiceProvider and public key string on Windows Mobile 6

my Windows Mobile 6 application needs to send data to a php REST web service of a company.
that WS has a method that returns the public key to use to encrypt the username and password of the user of the mobile application.
They give me a sample code written in php which simply calls the WS to obtain the public key and then calls openssl_public_encrypt with, as public key parameter, the value returned by the web service's call. This is an excerpt
function CallAPI($url, $data = false)
{
$curl = curl_init();
$url = sprintf("%s?%s", $url, http_build_query($data));
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
return curl_exec($curl);
}
$public_key=CallAPI(, "https://***.***.***/index.php/rest/getPKey");
$json = json_encode(array("username"=>"********","psw"=>"*******"));
openssl_public_encrypt($json, $encrypted, $public_key);
From Windows Mobile point of view seems to be more complicated than this, also I am not really into RSA encryption.
the first thing I do is to call the WS to obtain the public key and I save it into a string. The following is the code I use to encrypt data.
ASCIIEncoding ByteConverter = new ASCIIEncoding();
byte[] dataToEncrypt = ByteConverter.GetBytes(data_string);
byte[] public_key = ByteConverter.GetBytes(public_key_string);
byte[] encryptedData;
RSACryptoServiceProvider RSA_provider = new RSACryptoServiceProvider();
RSAParameters key_info = RSA_provider.ExportParameters(false);
key_info.Modulus = public_key;
RSA_provider.ImportParameters(key_info);
encryptedData = RSA_provider.Encrypt(dataToEncrypt, false);
string encrypted_string = ByteConverter.GetString(encryptedData, 0, encryptedData.Length);
return encrypted_string;
If I try to send data to the web service it fails due to an authentication failure, also I noted that from php code that the encrypted string is always of 256 chars, while the .NET encrypted string has completely different length.
What I'm doing wrong?
I have seen a lot of questions on StackOverflow about working with .NET and RSA Encryption, but the features that are used are not contained in the Compact Framework.
Thank you in advance.
I've solved using BouncyCastle API for ecnryption/decryption on .NET side and sending data between Windows Mobile client and PHP server using UrlEncode and replacing all '+'s in the UrlEncoded string with a custom string known from both the client and the server, I decided for "####".

Implement secure communication using RSA encryption in C#

I want to implement a scenario where two endpoints can securely communicate with each other using public/private key encryption. The scenario is following:
For A to send a message to B:
A encrypts the message using A's private key.
A encrypts the message using B's public key.
A sends the message.
B receives the message.
B decrypts the message using A's public key.
B decrypts the message using B's private key.
B reads the message.
Here is what I have in C# using RSA encryption:
// Alice wants to send a message to Bob:
String plainText = "Hello, World!";
Byte[] plainData = Encoding.Default.GetBytes(plainText);
Byte[] cipherData = null;
RSACryptoServiceProvider alice = new RSACryptoServiceProvider();
RSACryptoServiceProvider bob = new RSACryptoServiceProvider();
var alicePrivateKey = alice.ExportParameters(true);
var alicePublicKey = alice.ExportParameters(false);
var bobPrivateKey = bob.ExportParameters(true);
var bobPublicKey = bob.ExportParameters(false);
RSACryptoServiceProvider messenger = new RSACryptoServiceProvider();
messenger.ImportParameters(alicePrivateKey);
cipherData = messenger.Encrypt(plainData, true);
messenger.ImportParameters(bobPublicKey);
cipherData = messenger.Encrypt(cipherData, true);
messenger.ImportParameters(alicePublicKey);
cipherData = messenger.Decrypt(cipherData, true);
messenger.ImportParameters(bobPrivateKey);
cipherData = messenger.Decrypt(cipherData, true);
String result = Encoding.Default.GetString(alice.Decrypt(cipherData, true));
Clearly, there is something wrong with the following lines:
messenger.ImportParameters(bobPublicKey);
cipherData = messenger.Encrypt(cipherData, true);
Which throws System.Security.Cryptography.CryptographyException with message { "Bad Length" }.
As I can see it is not able to encrypt the data using just the public part of bob's key.
Can someone throw some light on how to properly accomplish what I want to do in C#?
Two problems here:
A) Your protocol design is wrong. If you want to use RSA to exchange messages, the algorithm is this:
A encrypts message using B's public key
A sends the message
B decrypts the message using B's private key
(B does processing)
B encrypts message using A's public key
B sends the message
A decrypts the message using A's private key
and so on. Notice how A does not know B's private key, and vice versa. The public and private keys are related in such a way that a message encrypted with a public key (known to everyone) can only be decrypted with the corresponding private key (known only to the intendent recipient of the encrypted message). This is the whole point of RSA, actually.
As for implementation in C#, it is quite trivial to do with the Crypto classes once you really understand the underlying concepts. See for example here and here.
B) RSA is good for exchanging small amounts of data. It is meant for key exchange over an insecure channel without the need for a shared secret. For exchanging "normal" data, a symmetric algorithm such as AES is used. So the idea would be generating a random passphrase and IV from A, and sending that to B via RSA as discussed in A; after both parties know the passphrase and IV, they can just encrypt data using AES with the shared key.
This is what SSL does, and you should have a really good reason to roll your own instead of using a standard SSL stream.
RSA is used to encrypt data which are smaller than the key. You use symmetric key to encrypt large amount of data and then use the RSA to share the symmetric key.
For further details you might refer to this question : how to use RSA to encrypt files (huge data) in C#

Get Apple Keychain to recognize Bouncy Castle .NET created PKCS12 (.p12) store

Our organization manages a stable of iOS applications for multiple clients, which means dealing with a lot of different developer identity certificates and push notification certificates.
I have had success with the Bouncy Castle C# Crypto API in simplifying management of the certificates and private keys for push notifications, essentially eliminating the need for the Keychain for all our push notification certificates.
I would like to extend this to the developer identity certificates. The goal would be to store all the private key and certificate information in the database for each developer identity. Then when a new developer or build machine needs to be provisioned, server side code could wrap all of the certificates and private keys into one p12 archive with one password that could be imported into the target Mac's Keychain.
Unfortunately, the Mac Keychain doesn't like the p12 files I'm generating. This is annoying since I can successfully import these files into the Windows certificate manager just fine.
The code I'm using (the important parts) looks like this:
private byte[] GetP12Bytes(List<DevIdentity> identities, string password)
{
Pkcs12Store store = new Pkcs12Store();
foreach(DevIdentity ident in identities)
{
// Easiest to create a Bouncy Castle cert by converting from .NET
var dotNetCert = new X509Certificate2(ident.CertificateBytes);
// This method (not shown) parses the CN= attribute out of the cert's distinguished name
string friendlyName = GetFriendlyName(dotNetCert.Subject);
// Now reconstitute the private key from saved value strings
BigInteger modulus = new BigInteger(ident.PrivateKey.Modulus);
BigInteger publicExponent = new BigInteger(ident.PrivateKey.PublicExponent);
BigInteger privateExponent = new BigInteger(ident.PrivateKey.Exponent);
BigInteger p = new BigInteger(ident.PrivateKey.P);
BigInteger q = new BigInteger(ident.PrivateKey.Q);
BigInteger dP = new BigInteger(ident.PrivateKey.DP);
BigInteger dQ = new BigInteger(ident.PrivateKey.DQ);
BigInteger qInv = new BigInteger(ident.PrivateKey.QInv);
RsaKeyParameters kp = new RsaPrivateCrtKeyParameters(modulus, publicExponent, privateExponent, p, q, dP, dQ, qInv);
AsymmetricKeyEntry privateKey = new AsymmetricKeyEntry(kp);
// Now let's convert to a Bouncy Castle cert and wrap it for packaging
Org.BouncyCastle.X509.X509Certificate cert = DotNetUtilities.FromX509Certificate(dotNetCert);
X509CertificateEntry certEntry = new X509CertificateEntry(cert);
// Set the private key and certificate into the store
store.SetCertificateEntry(friendlyName, certEntry);
store.SetKeyEntry(ident.PrivateKeyName, privateKey, new X509CertificateEntry[] { certEntry });
}
using (MemoryStream ms = new MemoryStream())
{
store.Save(ms, password.ToCharArray(), new SecureRandom());
ms.Flush();
byte[] p12Bytes = ms.ToArray();
return p12Bytes;
}
}
Like I said, this works great for import on Windows, but fails with a very generic error when importing into the Mac Keychain.
There is one major difference I can see when loading a Keychain-generated p12 and my own generated p12 file, but I do not know if this is the cause.
If I load the Mac Keychain generated p12 into a Bouncy Castle PKCS12Store, and then examine the keys, on the Keychain p12, both the certificate and the private key have an attribute with the key "1.2.840.113549.1.9.21" with equivalent values (a DerOctetString with value #af8a1d6891efeb32756c12b7bdd96b5ec673e11e).
If I do the same to my generated p12 file, the private key contains the "1.2.840.113549.1.9.21" attribute, but the Certificate does not.
If I Google "1.2.840.113549.1.9.21", I find out that this OID means PKCS_12_LOCAL_KEY_ID . My only theory is that the Keychain relies on this to match up the certificate and private key, and that my generated file does not have this, so it fails.
However, I've tried adding these values to a Hashtable and then using the CertificateEntry constructor that takes the attribute hashtable. If I do that, and then save the bytes, and then reload the bytes, that attribute is again missing.
So I'm flummoxed. Maybe this attribute is a glitch in the Bouncy Castle API? Maybe there's something I'm doing wrong. Maybe the Keychain has ridiculous non-standard requirements for incoming p12 files. In any case, any help that could be provided would be greatly appreciated.
BouncyCastle's Pkcs12Store takes care of setting both the Friendly Name and Local Key ID attributes for you (or at least it does so in the 1.7 release, circa April 2011). My guess is that you must have used an older version where this didn't work.
Here's how I'm saving an iPhone Developer identity to a Pkcs12Store instance (extra stuff and security omitted):
var store = new Pkcs12Store();
// pairs is IEnumerable<Tuple<X509Certificate, AsymmetricKeyParameter>>
foreach (var pair in pairs)
{
var cn = pair.Item1.SubjectDN
.GetValueList(X509Name.CN).OfType<string>().Single();
var certEntry = new X509CertificateEntry(pair.Item1);
store.SetCertificateEntry(cn, certEntry);
var keyEntry = new AsymmetricKeyEntry(pair.Item2);
store.SetKeyEntry("Developer Name", keyEntry, new[] { certEntry });
}
store.Save(stream, string.Empty.ToArray(), new SecureRandom());
Importing the store in Keychain Access.app on OS X 10.7 correctly places the certificate and private key in the keychain and places the certificate within the private key in the UI, as with a certificate and key generated by Keychain Access itself.
On a side note, it seems that Pkcs12Store uses the public key of the certificate to generate the value of the LocalKeyId attribute shared by the certificate and key entries.
You can see the relevant section of the Pkcs12Store source here.

Categories