When I am trying to use Windows.Security.Cryptography.Core in UWP, I always get an error on importing the public key from AsymmetricKeyAlgorithmProvider. I tried every combination without success. I always checked the input string to be in UTF8 mode.
try {
var bytes = Encoding.UTF8.GetBytes(publicKeyString);
publicKeyString = Encoding.UTF8.GetString(bytes);
Debug.WriteLine(publicKeyString);
IBuffer keyBuffer = CryptographicBuffer.DecodeFromBase64String(publicKeyString);
byte[] bytes = Convert.FromBase64String(publicKeyString);
string hex = "2D2D2D2D2D424547494E205055424C4943204B45592D2D2D2D2D0A4D494942496A414E42676B71686B6947397730424151454641414F43415138414D49494243674B434151454172336767514744726E5645562B786A4F484F2B390A4B72595541547166756935666F364D65736E53466C6B797937482F4775327135667273724B305246383933507A584F664371414A6753534D58673330793463720A594E3742617838467979314C58744E2B5A35576B43436D644F597438704F636D7A75494D6A636E4E733063486B767859576B336658516D513255535A685063700A51595761382F4469392B344462745464587A643263346F717A4E4A4A4A4B66437A647731796E72776F4A755A4245563547747363396F48782F6D7231725434420A386F377635473553706D543368687661396762617034436C4231745677434B584F41636A2F71782F49416A505A35566E4B7A363669325534706A6F43764A79590A564E53497A3557346276726A6E622B76775848626B506D62316E6655503864526D33683767644F314D7330766B72715A4567714746416953343475686974576B0A65514944415141420A2D2D2D2D2D454E44205055424C4943204B45592D2D2D2D2D0A";
Debug.WriteLine(hex);
AsymmetricKeyAlgorithmProvider provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey publicKey = provider.ImportPublicKey(CryptographicBuffer.DecodeFromHexString(hex), CryptographicPublicKeyBlobType.BCryptPublicKey);
IBuffer dataBuffer = CryptographicBuffer.CreateFromByteArray(Encoding.UTF8.GetBytes(plainText));
var encryptedData = CryptographicEngine.Encrypt(publicKey, dataBuffer, null);
return CryptographicBuffer.EncodeToBase64String(encryptedData);
}
catch (Exception e)
{
throw;
return "Error in Encryption:With RSA ";
}
I have found a solution to my problem the problem was that the public key i was trying to import was not in ASN1 form and therefore the uwp Cryptography Core couldn't do the conversion from DER to ASN1 the new code is :
byte[] bytes = Encoding.UTF8.GetBytes(publicKeyString);
IBuffer keyBuffer = CryptographicBuffer.DecodeFromBase64String(publicKeyString);
AsymmetricKeyAlgorithmProvider provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey publicKey = provider.ImportPublicKey(keyBuffer,CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);`
I meet the same problem:ASN1 bad tag value met.
I found that the ImportKeyPair(IBuffer) is the correct for my situation.
Sample Code:
var loginPBK = "";//your public key,such as "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCp0wHYbg......."
var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
var publicKey = provider.ImportPublicKey(CryptographicBuffer.DecodeFromBase64String(loginPBK));
var encryptData = CryptographicEngine.Encrypt(publicKey, CryptographicBuffer.ConvertStringToBinary(password, BinaryStringEncoding.Utf8), null);
var pwd2 = CryptographicBuffer.EncodeToBase64String(encryptData);
Related
I have gone through many posts here but did not find the right solution. I want to decrypt a value encrypted in c# .net from Android.
I have successfully decrypted in .net platform using the following code snippet
public static void Main()
{
string _privateKey = Base64Decode("myprivatekey");
var rsa = new RSACryptoServiceProvider();
string data = "198,47,144,175,154,47,194,175,242,41,212,150,220,177,198,161,236,36,197,62,18,111,21,244,232,245,90,234,195,169,141,195,139,199,131,163,26,124,246,50,102,229,73,148,18,110,170,145,112,237,23,123,226,135,158,206,71,116,9,219,56,96,140,19,180,192,80,29,63,160,43,127,204,135,155,67,46,160,225,12,85,161,107,214,104,218,6,220,252,73,252,92,152,235,214,126,245,126,129,150,49,68,162,120,237,246,27,25,45,225,106,201,251,128,243,213,250,172,26,28,176,219,198,194,7,202,34,210";
var dataArray = data.Split(new char[] { ',' });
byte[] dataByte = new byte[dataArray.Length];
for (int i = 0; i < dataArray.Length; i++)
{
dataByte[i] = Convert.ToByte(dataArray[i]);
}
rsa.FromXmlString(_privateKey);
var decryptedByte = rsa.Decrypt(dataByte, false);
Console.WriteLine(_encoder.GetString(decryptedByte));
}
Now I want to do the same process in Android app. Please can somebody guide me through this?
I have tried the following code but its throwing javax.crypto.IllegalBlockSizeException: input must be under 128 bytes exception
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decode(modulusString, DEFAULT);
byte[] exponentBytes = Base64.decode(publicExponentString, DEFAULT);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPrivateKeySpec rsaPubKey = new RSAPrivateKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey pubKey = fact.generatePrivate(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = result.getBytes("UTF-16LE");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.decode(cipherData, DEFAULT).toString();
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.
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);
I'm trying to port this method from .NET 4.5 desktop app to a WinRT app:
static byte[] DotNetRsaEncrypt(string modulus, string exponent, byte[] data)
{
var modulusBytes = Convert.FromBase64String(modulus);
var exponentBytes = Convert.FromBase64String(exponent);
var rsaParameters = new RSAParameters { Modulus = modulusBytes, Exponent = exponentBytes };
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
var encrypted = rsa.Encrypt(data, true);
return encrypted;
}
After reading this RSA Encryption in metro style Application
I tried the following:
static byte[] WinRtRsaEncrypt(string modulus, string exponent, byte[] data)
{
var modulusBytes = Convert.FromBase64String(modulus);
var exponentBytes = Convert.FromBase64String(exponent);
var keyBlob = modulusBytes.Concat(exponentBytes).ToArray().AsBuffer();
var rsa = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaOaepSha1);
var key = rsa.ImportPublicKey(keyBlob, CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
var encrypted = CryptographicEngine.Encrypt(key, data.AsBuffer(), null);
return encrypted;
}
But it does not work.
In order to get the same functionality as my desktop app...
What AsymmetricAlgorithmNames should I pass to OpenAlgorithm()?
What CryptographicPublicKeyBlobType should I pass to ImportPublicKey()?
Following up on user1968335's hint, this worked for me.
First, in a C# application, use the following code to obtain a CspBlob from your modulus/exponent:
var exponent = Encoding.Default.GetBytes(exponentStr);
var modulus = Encoding.Default.GetBytes(modulusStr);
var rsaParameters = new RSAParameters { Modulus = modulus, Exponent = exponent };
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
var cspBlobString = Convert.ToBase64String(rsa.ExportCspBlob(false));
Then, in a WinRT application you can use that CspBlob to sign a piece of data like this:
private static string SignString(string data)
{
string cspBlobString = //cspBlob
var keyBlob = CryptographicBuffer.DecodeFromBase64String(cspBlobString);
AsymmetricKeyAlgorithmProvider rsa = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey key = rsa.ImportPublicKey(keyBlob, CryptographicPublicKeyBlobType.Capi1PublicKey);
IBuffer plainBuffer = CryptographicBuffer.ConvertStringToBinary(data, BinaryStringEncoding.Utf8);
IBuffer encryptedBuffer = CryptographicEngine.Encrypt(key, plainBuffer, null);
byte[] encryptedBytes;
CryptographicBuffer.CopyToByteArray(encryptedBuffer, out encryptedBytes);
return Convert.ToBase64String(encryptedBytes);
}
If it matters, this is how I generated my asymmetric keys: http://43n141e.blogspot.co.uk/2008/08/rsa-encryption-openssl-to-ruby-to-c-and_27.html
According to CryptoWinRT sample, OpenAlgorithm(...) method takes these values.
RSA_PKCS1
RSA_OAEP_SHA1
RSA_OAEP_SHA256
RSA_OAEP_SHA384
See also : RSA cryptography between a WinRT and a .Net app
I recently posted about issues with encrypting large data with RSA, I am finally done with that and now I am moving on to implementing signing with a user's private key and verifying with the corresponding public key. However, whenever I compare the signed data and the original message I basically just get false returned. I am hoping some of your could see what I am doing wrong.
Here is the code:
public static string SignData(string message, RSAParameters privateKey)
{
//// The array to store the signed message in bytes
byte[] signedBytes;
using (var rsa = new RSACryptoServiceProvider())
{
//// Write the message to a byte array using UTF8 as the encoding.
var encoder = new UTF8Encoding();
byte[] originalData = encoder.GetBytes(message);
try
{
//// Import the private key used for signing the message
rsa.ImportParameters(privateKey);
//// Sign the data, using SHA512 as the hashing algorithm
signedBytes = rsa.SignData(originalData, CryptoConfig.MapNameToOID("SHA512"));
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
finally
{
//// Set the keycontainer to be cleared when rsa is garbage collected.
rsa.PersistKeyInCsp = false;
}
}
//// Convert the a base64 string before returning
return Convert.ToBase64String(signedBytes);
}
So that is the first step, to sign the data, next I move on to verifying the data:
public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)
{
bool success = false;
using (var rsa = new RSACryptoServiceProvider())
{
byte[] bytesToVerify = Convert.FromBase64String(originalMessage);
byte[] signedBytes = Convert.FromBase64String(signedMessage);
try
{
rsa.ImportParameters(publicKey);
SHA512Managed Hash = new SHA512Managed();
byte[] hashedData = Hash.ComputeHash(signedBytes);
success = rsa.VerifyData(bytesToVerify, CryptoConfig.MapNameToOID("SHA512"), signedBytes);
}
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
return success;
}
And here is the test client:
public static void Main(string[] args)
{
PublicKeyInfrastructure pki = new PublicKeyInfrastructure();
Cryptograph crypto = new Cryptograph();
RSAParameters privateKey = crypto.GenerateKeys("email#email.com");
const string PlainText = "This is really sent by me, really!";
RSAParameters publicKey = crypto.GetPublicKey("email#email.com");
string encryptedText = Cryptograph.Encrypt(PlainText, publicKey);
Console.WriteLine("This is the encrypted Text:" + "\n " + encryptedText);
string decryptedText = Cryptograph.Decrypt(encryptedText, privateKey);
Console.WriteLine("This is the decrypted text: " + decryptedText);
string messageToSign = encryptedText;
string signedMessage = Cryptograph.SignData(messageToSign, privateKey);
//// Is this message really, really, REALLY sent by me?
bool success = Cryptograph.VerifyData(messageToSign, signedMessage, publicKey);
Console.WriteLine("Is this message really, really, REALLY sent by me? " + success);
}
Am I missing a step here? According to the Cryptography API and the examples there, I shouldn't manually compute any hashes, since I supply the algorithm within the method call itself.
Any help will be greatly appreciated.
Your problem is at the beginning of the VerifyData method:
public static bool VerifyData(string originalMessage, string signedMessage, RSAParameters publicKey)
{
bool success = false;
using (var rsa = new RSACryptoServiceProvider())
{
//Don't do this, do the same as you did in SignData:
//byte[] bytesToVerify = Convert.FromBase64String(originalMessage);
var encoder = new UTF8Encoding();
byte[] bytesToVerify = encoder.GetBytes(originalMessage);
byte[] signedBytes = Convert.FromBase64String(signedMessage);
try
...
For some reason you switched to FromBase64String instead of UTF8Encoding.GetBytes.