Unable to use existing keys in RSA Cryptoservice provider class - c#

I need to use RSA encryption in my wp8 app and send it to the server. But the trouble I am facing is that I know the public key of the server and I need to encrypt the data in app side using the key. But as far I infer from all the posting here, RSACryptoservice provider class doesn't support a key from another source(Am I wrong?). Is there any way to use the class in such a scenario? Or can this be done by using third party library only?
I tried the following function but still no use. What am I doing wrong here?
public static string RSAEncrypt(string data)
{
try
{
//initialze the byte arrays to the public key information.
string pk = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD0cNKUgLdLMpW5BWB+PAlIIIiqhSXk66PQVemUnRs3nowRcBETfUkMIfDcPDM1FXhh+/2FqsnFLveCYl980bylZlBghkjUleknV4dGLfQPuLE7oxk4tbQF6Zk9Fmc9ynxvZ7XDuLmdn/4mdxW7BmcSomLIxkkGHynKkkXk5QcKQIDAQAB";
byte[] PublicKey = Convert.FromBase64String(pk);
//byte[] Exponent = { 1, 0, 1 };
UnicodeEncoding pi = new UnicodeEncoding();
//Values to store encrypted symmetric keys.
byte[] dataBytes = pi.GetBytes(data);
//Create a new instance of RSACryptoServiceProvider.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Create a new instance of RSAParameters.
RSAParameters RSAKeyInfow = new RSAParameters();
//Set RSAKeyInfo to the public key values.
RSAKeyInfow.Modulus = PublicKey;
// RSAKeyInfow.Exponent = Exponent;
//Import key parameters into RSA.
RSA.ImportParameters(RSAKeyInfow);
var rslt = RSA.Encrypt(dataBytes, false);
System.Diagnostics.Debug.WriteLine(rslt);
return Convert.ToBase64String(rslt);
}
catch
{
return null;
}
}
Am calling this function in another page as:
private void Button_Click(object sender, RoutedEventArgs e)
{
var myString = "this is my string data";
var x = Class1.RSAEncrypt(myString);
MessageBox.Show(x);
}
The error I get is "Value cannot be null.
Parameter name: messageBoxText"
I think the problem here is not passing the exponent, but I don't know how.

Related

Using Azure Key Vault RSA Key to encrypt and decrypt strings

I have setup Azure Key Vault to retrieve RSA Keys for encryption. Azure send me an object of type KeyBundle. This object contains a JsonWebKey of type RSA of size 2048. Looking at my RSA Key, it has 2 methods called Encrypt(byte[] data, RSAEncryptionPadding padding) and Decrypt(byte[] data, RSAEncryptionPadding padding). Now I am trying to encrypt and decrypt a simple string like this:
public EncryptionManager(KeyBundle encryptionKey)
{
string test = "Hello World!";
var key = encryptionKey.Key.ToRSA();
var encryptedString = key.Encrypt(Encoding.UTF8.GetBytes(test), RSAEncryptionPadding.OaepSHA256);
var decryptedString = key.Decrypt(encryptedString, RSAEncryptionPadding.OaepSHA256);
}
Encryption works, but decryption throws an exception with message:
Key does not exist.
Here is the StackTrace
at System.Security.Cryptography.RSAImplementation.RSACng.EncryptOrDecrypt(SafeNCryptKeyHandle
key, ReadOnlySpan`1 input, AsymmetricPaddingMode paddingMode, Void*
paddingInfo, Boolean encrypt) at
System.Security.Cryptography.RSAImplementation.RSACng.EncryptOrDecrypt(Byte[]
data, RSAEncryptionPadding padding, Boolean encrypt) at
System.Security.Cryptography.RSAImplementation.RSACng.Decrypt(Byte[]
data, RSAEncryptionPadding padding) at
NxtUtils.Security.EncryptionManager..ctor(KeyBundle encryptionKey) in
C:\Repos\Enigma\EnigmaPrototype\SharedLibaries\NxtUtils\Security\EncryptionManager.cs:line
26
I am really not familiar with cryptographic algorithms. My question is: How can I encrypt and decrypt a simple strig using this RSA Key provided by Azure?
Thanks!
I got the same issue, what I did is here although I searched from internet and got this from the Microsoft docs
so this is my working code below
public static class KeyVaultEncryptorDecryptor
{
public static string KeyDecryptText(this string textToDecrypt , KeyVaultClient keyVaultClient, string keyidentifier)
{
var kv = keyVaultClient;
try
{
var key = kv.GetKeyAsync(keyidentifier).Result;
var publicKey = Convert.ToBase64String(key.Key.N);
using var rsa = new RSACryptoServiceProvider();
var p = new RSAParameters() {
Modulus = key.Key.N, Exponent = key.Key.E
};
rsa.ImportParameters(p);
var encryptedTextNew = Convert.FromBase64String(textToDecrypt);
var decryptedData = kv.DecryptAsync(key.KeyIdentifier.Identifier.ToString(), JsonWebKeyEncryptionAlgorithm.RSAOAEP, encryptedTextNew).GetAwaiter().GetResult();
var decryptedText = Encoding.Unicode.GetString(decryptedData.Result);
return decryptedText;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return default;
}
}
public static string KeyEncryptText(this string textToEncrypt , KeyVaultClient keyVaultClient, string keyidentifier)
{
var kv = keyVaultClient;
try
{
var key = kv.GetKeyAsync(keyidentifier).GetAwaiter().GetResult();
var publicKey = Convert.ToBase64String(key.Key.N);
using var rsa = new RSACryptoServiceProvider();
var p = new RSAParameters() {
Modulus = key.Key.N, Exponent = key.Key.E
};
rsa.ImportParameters(p);
var byteData = Encoding.Unicode.GetBytes(textToEncrypt);
var encryptedText = rsa.Encrypt(byteData, true);
string encText = Convert.ToBase64String(encryptedText);
return encText;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return default;
}
}
}
ToRSA has a default boolean parameter indicating if the private key should be available, or not.
Since you didn't explicitly say true it is implicitly false, therefore your key object is public-only. With a public RSA key you can encrypt data or verify a signature, but you cannot sign or decrypt.

RSA signing in C# for licensing

(Approach #2)
I need to make a software activation mechanism. So i ended up with this scheme: App creates a unique id, based on computer's hardware. Buyer emails this id to me. I sign it with my private key and i send back the signed string. App verifies string (decodes it with contained public key and compares it with hardware id).
So far i am done with hardware id and i have created the keys (1024bit) with openssl, that's two files private.pem and public.pem.
I tried to apply the solution described in http://www.christian-etter.de/?p=771
but verification always fails.
The code:
private void Generate_Click(object sender, EventArgs e)
{
byte[] SignedData;
byte[] UnsignedData = Encoding.UTF8.GetBytes(CompID.Text);
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.PersistKeyInCsp = false;
rsa.LoadPrivateKeyPEM(PrivateKey.Text);
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
SignedData = rsa.SignData(UnsignedData, sha1);
ActCode.Text = Encoding.UTF8.GetString(SignedData, 0, SignedData.Length);
}
}
//--------------------------------------------------------------------
private void Verify_Click(object sender, EventArgs e)
{
byte[] UnsignedData = Encoding.UTF8.GetBytes(CompID.Text);
byte[] SignedData = Encoding.UTF8.GetBytes(ActCode.Text);
bool VerifOK;
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.PersistKeyInCsp = false;
rsa.LoadPublicKeyPEM(PublicKey.Text);
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
VerifOK = rsa.VerifyData(UnsignedData, sha1, SignedData);
}
if (VerifOK) verif.Text = "verification ok";
else verif.Text = "verification error";
}
You are treating some arbitrary bytes as a UTF-8 encoded string (SignedData) This is incorrect because not all byte sequences are valid. I suspect the encoder is throwing out the invalid bytes, causing verification to fail. You should use Base64 encoding to preserve the binary data in a string format without losing anything. So this:
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
SignedData = rsa.SignData(UnsignedData, sha1);
ActCode.Text = Encoding.UTF8.GetString(SignedData, 0, SignedData.Length);
becomes
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
SignedData = rsa.SignData(UnsignedData, sha1);
ActCode.Text = Convert.ToBase64String(SignedData);
Likewise during verification you need to use Convert.FromBase64String to get the SignedData. It is OK to use UTF-8 for the CompID because it is string data that you need converted to a binary form for signing.

phpseclib sign and verify in C#

I need to sign the string with private Key using RSA phpseclib and then verify it in C# . I have seen many examples of how to encrypt in C# and decrypt in php, but none of how to sign string in php and verify in .NET.
here is php code:
include('Crypt/RSA.php');
$info = "Something";
$PrivateKey= "<RSAKeyValue><Modulus>3C5QWo4H+............"; //long string
$unsignedString = base64_encode($info);
$signedString = HashAndSignBytes($info, $PrivateKey);
file_put_contents('file.txt', $unsignedString."\n".$signedString);
function HashAndSignBytes($stringToSign, $Key) {
$rsa = new Crypt_RSA();
$rsa->loadKey($Key); // private key
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($stringToSign);
return base64_encode($signature);
}
and here is my attempt to read the file and verify it in C#:
const string publicKey = #"<RSAKeyValue><Modulus>3C5QWo4H.....";
TextReader reader = new StreamReader(path, Encoding.ASCII);
var unsignedString = reader.ReadLine();
var signedString = reader.ReadLine();
reader.Close();
if (VerifySignedHash(unsignedString,signedString, publicKey)) {
//some code
}
private bool VerifySignedHash(string stringToVerify, string signedString, string publicKey)
{
var byteConverter = new ASCIIEncoding();
var dataToVerify = Convert.FromBase64String(stringToVerify);
var signedData = Convert.FromBase64String(signedString);
try
{
// Create a new instance of RSACryptoServiceProvider using the
// key from RSAParameters.
var rsaAlg = new RSACryptoServiceProvider();
rsaAlg.FromXmlString(publicKey);
// Verify the data using the signature. Pass a new instance of SHA1CryptoServiceProvider
// to specify the use of SHA1 for hashing.
return rsaAlg.VerifyData(dataToVerify, new SHA1CryptoServiceProvider(), signedData);
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return false;
}
}
verfication fails...
In your "signing" code, you base64encode the original string, then write that string to the output file. However, on the C# side, you read that value into unsignedString, but never reverse the base64 encoding.
The end result is that you're trying to verify the bytes of the base64Encoded string, not the data itself, so the VerifyData step fails.
Think that's your problem.
Modifying the following line might solve the problem:
var dataToVerify = Convert.FromBase64String(stringToVerify);

Portable encryption algorithm

I need to implement a new, or already existing, encryption algorithm that encrypt and decrypt a string using another string as key. The problem is that this algorithm have to work independently from a computer on which it is used.
So the methods signature are:
public static string Encrypt(this string source, string key);
public static string Decrypt(this string source, string key);
I tried these algorithms, but they don't work the way I want:
public static string Encrypt(this string source, string key)
{
if (String.IsNullOrEmpty(source) || String.IsNullOrEmpty(key))
throw new ArgumentException();
CspParameters cspp = new CspParameters { KeyContainerName = key };
using (var rsa = new RSACryptoServiceProvider(cspp) { PersistKeyInCsp = true })
return BitConverter.ToString(rsa.Encrypt(UTF8Encoding.UTF8.GetBytes(source), true));
}
public static string Decrypt(this string source, string key)
{
if (String.IsNullOrEmpty(source) || String.IsNullOrEmpty(key))
throw new ArgumentException();
try
{
CspParameters cspp = new CspParameters { KeyContainerName = key };
using (var rsa = new RSACryptoServiceProvider(cspp) { PersistKeyInCsp = true })
{
string[] decryptArray = source.Split(new char[] { '-' }, StringSplitOptions.None);
byte[] bytes = Array.ConvertAll<string, byte>(decryptArray, (s => Convert.ToByte(Byte.Parse(s, NumberStyles.HexNumber))));
return UTF8Encoding.UTF8.GetString(rsa.Decrypt(bytes, true));
}
}
catch
{ return null; }
}
How can I do?
The KeyContainerName is NOT the key. In your example above, by passing the key as the store name, you'll create a NEW RSA keypair on each machine with a store name of the key you passed in (rather than a storename of something like "MyRSAKeyPair" or whatever). This will mean both the public and private keys will be completely different and your routines won't seem to work.
ALSO: You're using asymmetric encryption, this has a maximum block size limit of the key length. This means you'll either need to create a chunking mechanism (slow as asymmetric encryption is expensive) OR use something symmetric like AES with the AES key being sent using asymmetric encryption (such as RSA) on a per conversation basis.
You need to export the RSA public key and then import it into the remote machine's keystore. Easier still is generating an X509 certificate (you can self sign if you're just going between a couple of machines, exporting the public part of it into a .CER file, then you can use the X509 Certificate Store API to get the RSA Provider, meaning you have a nice transportable key.
public static RSACryptoServiceProvider GetRsaProviderFromCertificate()
{
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection certCollection = (X509Certificate2Collection)store.Certificates;
foreach(X509Certificate2 cert in certCollection)
{
if (cert.SubjectName.Name.IndexOf("TheCertIWantToUse") > 0)
{
return cert.PrivateKey as RSACryptoServiceProvider;
}
}
I hope that's explicit enough...
If you want to do it without certs
// Export public key (on the encrypting end)
publicKey = rsaProvider.ToXmlString(false);
// Write public key to file
publicKeyFile = File.CreateText(publicKeyFileName);
publicKeyFile.Write(publicKey);
Then on the other machine
// Select target CSP
cspParams = new CspParameters();
cspParams.ProviderType = 1; // PROV_RSA_FULL
rsaProvider = new RSACryptoServiceProvider(cspParams);
// Read public key from file
publicKeyFile = File.OpenText(publicKeyFileName);
publicKeyText = publicKeyFile.ReadToEnd();
// Import public key
rsaProvider.FromXmlString(publicKeyText);

C# RSA Decrypt Using Private Key

I've been searching but I can't seem to find a simple way of decrypting using RSA.
I have generated a public and private key, they are stored in two separate files and are in the XML format. I have no problem associating the public key to the RSACryptoServiceProvider object using FromXmlString, and then encrypting a string. My confusion comes when trying to decrypt an encrypted string. I'm not sure how I associate the private key data with the RSACryptoServiceProvider so that I can use the Decrypt function.
Any help would be appreciated.
EDIT:
The format of the public and private key is XML generated by the RSACryptoServiceProvider object, which I just put into a file:
<RSAKeyValue><Modulus>vS7Y5up+6kHMx7hQjKA6sKlIVASaw ... etc ...
I load the public key using this code:
StreamReader sr = new StreamReader(HttpContext.Current.Server.MapPath("public.key"));
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(sr.ReadToEnd().ToString());
I currently haven't tried anything with the private key yet, since I'm not sure where to start.
I don't know your situation but I would suggest that you store you key information in a KeyContainer. If you do this you can access the keyContainer by name and can do something like this.
// retrieves the maximum number of characters that can be decrypted at once
private int getMaxBlockSize(int keySize){
int max = ((int)(keysize/8/3) )* 4
if (keySize / 8 mod 3 != 0){
max += 4
}
return max;
}
public string decrypt(string msg, string containerName){
CspParameters params = new CspParameters();
params.KeyContainerName = containerName;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(params);
StringBuilder decryptedMsg = new StringBuilder();
int maxDecryptSize = getMaxBlockSize(rsa.KeySize);
int iterationCount = Math.Floor(msg.length / maxDecryptSize)
for(int i=0; i<iterationCount; i++){
int start = i * maxDecryptSize;
int blkSize = Math.min(start + maxDecryptSize, msg.Length);
Byte[] msgBytes = System.Convert.FromBase64String(msg.Substring(start, blkSize));
decryptedMsg.Append(System.Text.Encoding.Unicode.GetString(RSAProvider.Decrypt(msgBytes, false));
}
return decryptedMsg.ToString();
}
I haven't tested this out so there might be a bug in here but the you get the idea.
if you have private key in text format
like given below
-----BEGIN RSA PRIVATE KEY-----
text....
-----END RSA PRIVATE KEY-----
public string RsaDecryptWithPrivate(string base64Input, string privateKey)
{
var bytesToDecrypt = Convert.FromBase64String(base64Input);
AsymmetricCipherKeyPair keyPair;
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(privateKey))
{
keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();//fetch key pair from text file
decryptEngine.Init(false, keyPair.Private);
}
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
return decrypted;
}

Categories

Resources