RSA Encryption with Public Key - c#

I am trying to RSA Encrypt a string with a given key. (I can not change the key, since I request it from another system)
I get the key as a string and it looks like this:
-----BEGIN CERTIFICATE-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSehIDcXho52VvAQAFfVD2CzOFAYKSfnRsuKE/nqE1O4l/X6opYgjxj/mSNk1bvNobwiRBL4uWfFnsHoQSiv1Gqzl+JQ2QQ2rUVGuNZ7d7agjYcb1LCpKPE1Q0kqLvbGyDWQx8ULC7/FJ49mMwbzIE4C9ovfdOBa0er6IDNSW0IQIDAQAB-----END
CERTIFICATE-----
Now I tried to encrypt the string on many ways, but none of them worked.
The last solution I tried was the following:
public static string RSA(string payload, string publicKey)
{
byte[] toEncryptData = Encoding.ASCII.GetBytes(payload);
RSACryptoServiceProvider rsaPublic = new RSACryptoServiceProvider();
rsaPublic.FromXmlString(publicKey);
byte[] encryptedRSA = rsaPublic.Encrypt(toEncryptData, false);
string EncryptedResult = Encoding.Default.GetString(encryptedRSA);
return EncryptedResult;
}
It always throws an Exception when it comes to:
rsaPublic.FromXmlString(publicKey);
So what can I do to encrypt the string with RSA?
I need it because I want to implement an API: https://www.loxone.com/dede/wp-content/uploads/sites/2/2016/08/0903_Communicating-with-the-Miniserver.pdf
On Page 5 under 6. you can see the requirement!!

public static string ConvertToXmlPublicJavaKey(string publicJavaKey)
{
RsaKeyParameters publicKeyParam = (RsaKeyParameters)PublicKeyFactory.CreateKey(Convert.FromBase64String(publicJavaKey));
string xmlpublicKey = string.Format("<RSAKeyValue><Modulus>{0}</Modulus><Exponent>{1}</Exponent></RSAKeyValue>",
Convert.ToBase64String(publicKeyParam.Modulus.ToByteArrayUnsigned()),
Convert.ToBase64String(publicKeyParam.Exponent.ToByteArrayUnsigned()));
return xmlpublicKey;
}
The PublicKeyFactory class is from BouncyCastle.Crypto, you can Google it for more information.
Then your rsaPublic.FromXmlString(publicKey) will work fine.

Related

How do I sign with HashiCorp Vault

i don't know if this question is very easy and I just didn't figure it out how to sign with HashiCorp-VaultĀ“s Api VaultSharp, but I am despairing.
The entire Documentation with examples can be found here: https://github.com/rajanadar/VaultSharp
Encryption and Decryption works fine. Only Signing is a problem.
Code for Encryption:
public byte[] EncryptData(byte[] data, string keyName)
{
SecretsEngine transitSecretsEngine = new SecretsEngine
{
Type = SecretsEngineType.Transit,
Path = path
};
Client.V1.System.MountSecretBackendAsync(transitSecretsEngine).Wait();
Client.V1.Secrets.Transit.CreateEncryptionKeyAsync(keyName, new CreateKeyRequestOptions()
{
Exportable = true
}, path).Wait();
EncryptRequestOptions encryptOptions = new EncryptRequestOptions
{
Base64EncodedPlainText = Convert.ToBase64String(data),
ConvergentEncryption = true,
};
Secret<EncryptionResponse> encryptionResponse = Client.V1.Secrets.Transit.EncryptAsync(keyName,
encryptOptions, path).Result;
string cipherText = encryptionResponse.Data.CipherText;
return Encoding.Unicode.GetBytes(cipherText);
}
Code for Decryption:
public byte[] DecryptData(string ciphertext, string keyName)
{
DecryptRequestOptions decryptOptions = new DecryptRequestOptions
{
CipherText = ciphertext,
};
Secret<DecryptionResponse> decryptionResponse = Client.V1.Secrets.Transit.DecryptAsync(keyName,
decryptOptions, path).Result;
return Convert.FromBase64String(decryptionResponse.Data.Base64EncodedPlainText);
}
Here is my Code Trial for signing:
public byte[] Sign(byte[] plaintextBytes, string keyName)
{
byte[] hash = ComputeHash(plaintextBytes,SHA256.Create());
GCKMS.SignatureOptions options = new GCKMS.SignatureOptions()
{
Digest = Convert.ToBase64String(hash),
};
Secret<GCKMS.SignatureResponse> result = Client.V1.Secrets.GoogleCloudKMS.SignAsync(keyName,
options).Result;
return Encoding.Unicode.GetBytes(result.Data.Signature);
}
The Error is:
VaultSharp.Core.VaultApiException: {"errors":["no handler for route
'gcpkms/sign/Manuel'"]}
Last but not least my Code for validating the signature:
public bool ValidateSignature(byte[] plaintextByte, byte[] signature, string keyName)
{
GCKMS.VerificationOptions option = new GCKMS.VerificationOptions
{
Digest = Encoding.Unicode.GetString(ComputeHash(plaintextByte)),
Signature = Encoding.Unicode.GetString(signature)
};
Secret<GCKMS.VerificationResponse> result =
Client.V1.Secrets.GoogleCloudKMS.VerifyAsync(keyName, option).Result;
return result.Data.Valid;
}
I am not sure but this could be because I don't use a SecretsEngine with a Path. I could not find any SecretsEngine for GoogleCloudKms.
Useful information:
I generate the Path with Guid.NewGuid().ToString();.
ComputeHash is a self written Function that computes the Hash with a give Algorithm. The
default algorithm is SHA256.
GCMS is a short version of the Namespace VaultSharp.V1.SecretsEngines.GoogleCloudKMS
Any ideas and suggestions are very welcome.
Thanks in advance!
Although Vault offers convenient signature with Transit, the C# wrapper you are using does not support it.
Google KMS does offer signature, but its interface is more complex: you have to do the hash yourself and keep track of the key versions.
What I suggest is that you play a trick on your API wrapper:
Leave your encryption and decryption code as-is
Write to the the Transit backend as if it was a KV store version 1
Get your signature by sending your payload as the input parameter
You still have to base64 your data before sending it to Vault, to avoid binary encoding issues.
So assuming that:
You want to sign the text StackOverflow
The transit back-end is mounted under transit
Your signature key is named my-key
This should get you started:
var value = new Dictionary<string, object> { "input", Convert.ToBase64String(Encoding.UTF8.GetBytes("StackOverflow")) } };
var writtenValue = await vaultClient.V1.Secrets.KeyValue.V1.WriteSecretAsync("sign/my-key", value, "transit");

How does public and primary keys look like in rsa c#?

I am trying to use Asymmetric key algorithm rsa to secure my APIs.
suppose i create a Key pair for a user Who is using my APIs
as
RSA = new RSACryptoServiceProvider();
string PublicKey = RSA.ToXmlString(false);
string PrivateKey = RSA.ToXmlString(true);
now my private key look like
<RSAKeyValue>
<Modulus>tMvvUeHhggKAVex8JzYKXYQ32HVmr05PdtT1KV3kTkKE26jO/9IVmg+bWwxR1vuMzmY7spwguSJQsnjutJXamH0 mblNgYHmWwhyhJMSTtnZp57VDNNedjCFQnLOn211yk/PpCQHEiDDvt84hnmcdXNBlfZfkQzQ+UO/elhP5NH0=</Modulus>
<Exponent>AQAB</Exponent>
<P>22SSx3JpSqYVUWuxiSKwmh/8RDDcgDvq8l+4dMlQ/F+BJhthTQ3UJGupWaxiJyXX95AYAJIJJvWVvmvI7tqbxw==</P><Q>0va2WFy0oUwX4eJtZElRbot9TOrwGcqI64rMAYjvxl/mayCRXf0ypwKofKWOsmjK/pX0xWaqnFWB/NdLFt4Fmw==</Q><DP>VbIYPz2qcRUkmJQnWbiqINnDkONBDfnZkOjgxQVp09p+OONTA2UWa09+a9+Qy1fV3wZyya5BUu10m1fAucO8Ow==</DP>
<DQ>Lm8hOZfGJk6SXySwgUdmBhfrz3dSu8qJkpatSpUyeY54MBIuDOsDMCF0pmLmYryQGbM1+hEb8mcbwmQ84d6iiw==</DQ>
<InverseQ>L67OLIIK+M3OF1nxSHTjZ+Kv/hwOHJPvdHHSuh9VEmw93kPGn6Qt6GudEreFb6xlFsuR6UM19LUIweapgaUagQ==</InverseQ>
<D>YbaGlZ6bHoTzj3zMbPTMDVbUR+zLnpuYXwUhq0XPimxxGbbWiXSlsCoXMNIruSEjLLocMaAoH2bobkzl1jvXdAI30NdZ/rpG5SRl dpaeIundLfHkSnZfHwHuP9OGXJSXmbmLCrO8dq7BjLauS4JiTRgjiXoq8c995MEF+vhw9hE=</D>
</RSAKeyValue>
And this is my public key
<RSAKeyValue>
<Modulus>lF1Yof6FXaY9M/StHwPC1voW5K5JOqSu8BXndS4WH06L9mOfrNI9Et0cRaHjiYd2bQNFZB9+ AZmN4ODc36DgVPqQqYY2tbH+9UY0hcdi+WnDuUZe7sgvWKfGwBKTcy7g8uUAzpTWEP1W0FqeMunmXljD8Iouoqp5oHtTW1GHIlE=</Modulus>
<Exponent>AQAB</Exponent>
</RSAKeyValue>
Now my doubt is here Private key string also contain public key
what part of private or public key string should i send to user as it is in
XmlFormat
and there are multiple tags like modulus ,D,Q in the string what is actually a key in the string which i send to a user as his public , pvt keys
as my user will be on android platform
will he be able to use those keys in xml format ?
like in c# when we have to use keys
i do rsa.FromXmlString(Key);
where public or private key is in XmlFormat
Can that user will able to do this on android as well

Verify Private Key Protection before signing with RSACryptoServiceProvider

When signing data with RSACryptoServiceProvider in C#, I have a requirement to ensure the certificate was imported with strong key protection and a high security level to require the user enters the password every time they sign with the key. Here's a quick simplified sample of the signing code:
X509Store myCurrentUserStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
myCurrentUserStore.Open(OpenFlags.MaxAllowed);
X509Certificate2 currentCertificate = myCurrentUserStore.Certificates[4];
RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(currentCertificate.PrivateKey.ToXmlString(true));
byte[] signedData = Encoding.UTF8.GetBytes(originalFileContent);
byte[] signature = key.SignData(signedData, CryptoConfig2.CreateFromName("SHA256CryptoServiceProvider") as HashAlgorithm);
So what's the best way to go about checking how the certificate was installed so I can display an error message if it was not installed with strong private key protection with a high security level?
There are a couple things in your snippet that I don't understand.
Why you're opening with MaxAllowed. If you just want to read, use ReadOnly.
Why you're reading store.Certificates[4]. But presumably this is just a placeholder for "read a cert".
Why you're exporting and re-importing the key. (Especially since that would have had to prompt, which would defeat your "it needs to prompt" goal).
For #3 I'm assuming you are just looking to have a unique instance, in which case: Good news! .NET 4.6 added a GetRSAPrivateKey (extension) method to X509Certificate2 which always returns a unique instance. (And you might be excited to know about the new overload to SignData which doesn't encourage sending objects to the finalizer queue: https://msdn.microsoft.com/en-us/library/mt132675(v=vs.110).aspx)
Anyways, what I wrote here works for medium (consent) or high (password) protection. The CngKey based approach can distinguish medium from high, but the classic CAPI fallback can't tell which is which. (The classic CAPI fallback will only happen with obscure HSMs which don't have a CNG-compatible driver).
private static bool HasProtectedKey(X509Certificate2 cert)
{
if (!cert.HasPrivateKey)
{
return false;
}
using (RSA rsa = cert.GetRSAPrivateKey())
{
return HasProtectedKey(rsa);
}
}
private static bool HasProtectedKey(RSA rsa)
{
RSACng rsaCng = rsa as RSACng;
if (rsaCng != null)
{
return rsaCng.Key.UIPolicy.ProtectionLevel != CngUIProtectionLevels.None;
}
RSACryptoServiceProvider rsaCsp = rsa as RSACryptoServiceProvider;
if (rsaCsp != null)
{
CspKeyContainerInfo info = rsaCsp.CspKeyContainerInfo;
// First, try with the CNG API, it can answer the question directly:
try
{
var openOptions = info.MachineKeyStore
? CngKeyOpenOptions.MachineKey
: CngKeyOpenOptions.UserKey;
var cngProvider = new CngProvider(info.ProviderName);
using (CngKey cngKey =
CngKey.Open(info.KeyContainerName, cngProvider, openOptions))
{
return cngKey.UIPolicy.ProtectionLevel != CngUIProtectionLevels.None;
}
}
catch (CryptographicException)
{
}
// Fallback for CSP modules which CNG cannot load:
try
{
CspParameters silentParams = new CspParameters
{
KeyContainerName = info.KeyContainerName,
KeyNumber = (int)info.KeyNumber,
ProviderType = info.ProviderType,
ProviderName = info.ProviderName,
Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.NoPrompt,
};
if (info.MachineKeyStore)
{
silentParams.Flags |= CspProviderFlags.UseMachineKeyStore;
}
using (new RSACryptoServiceProvider(silentParams))
{
}
return false;
}
catch (CryptographicException e)
{
const int NTE_SILENT_CONTEXT = unchecked((int)0x80090022);
if (e.HResult == NTE_SILENT_CONTEXT)
{
return true;
}
throw;
}
}
// Some sort of RSA we don't know about, assume false.
return false;
}

RSA Encryption causes errors

I'm hoping you can help me with a weird error I've been getting while trying to implement a QueryString encryption module using RSA for my encryption type. My question is 2 fold:
Can you help me resolve the errors I'm getting?
Do you recommend something other than RSA to encrypt a QueryString?
Background/Important info:
I created an object called QueryString, which i store in the session (and which uses the SessionID to generate keys/salt). I instantiate it on Session Start, which generates the keys, and it naturally dies on Session.Abandon... I retrieve it in my BasePage and use it in my pages afterword much like i would a normal querystring (QueryString[key] for gets and stuff)... I store my Public and Private keys in the object itself, as internal properties to the object.
Another important thing is that my web site has a lot of grids, whithin which a record rows that contain links, and so they all have to be encrypted before they are set (href=...)... so the QueryString object i created can get taxed quite a bit and quite quickly (while looping with OnRowCreated or something to encrypt the hrefs).
The error(s):
i am currently getting intermittent errors, that cannot be reproduced (they happen at random... trust me... very random), of the following types when i try to either Encrypt or Decrypt:
Error type 1: CreateProvHandle
Error type 2: The specified file could not be found.
Error type 3: Attempted to perform an unauthorized operation.
For errors 1 and 2, i managed to deal with it so far by simply recursively calling the method (encrypt or decrypt) that caused it and they usually only recurse once (max i've had is 3 using my metrics) and the error magically disappears... so i blamed it on too many calls too fast to the object itself or something... but if someone has any clue as to why this would happen or how to solve this, i would love to take the recursing out of my methods and truly throw when a major exception occurs instead. On top of that i told my RSA params not to persist anything in the CSP store and so i thought the file thing didn't matter but apparently not...
For error 3, i simply cannot get my head around it! My RSA parameters say not to persist anything in the CSP so i don't know how, when or why it would even try to access files (yes, i am repeating myself!), let alone files that are restricted or that the user wouldn't have access to? Please help me!!
Here's some code for my RSA params... maybe you'll find something there that doesn't jive with what i'm trying to do (generate the keys once on object instantiation, store the object in the session, and use that from that point on/disconnect from anything remote/calls to server processes that are not part of the site or .NET)?
public static void AssignParameter()
{
const int PROVIDER_RSA_FULL = 1;
const string CONTAINER_NAME = "ICareContainer";
CspParameters cspParams;
cspParams = new CspParameters(PROVIDER_RSA_FULL);
cspParams.KeyContainerName = CONTAINER_NAME;
cspParams.Flags = CspProviderFlags.UseMachineKeyStore;
cspParams.ProviderName = "Microsoft Strong Cryptographic Provider";
CryptoKeyAccessRule rule = new CryptoKeyAccessRule("everyone", CryptoKeyRights.FullControl, AccessControlType.Allow);
cspParams.CryptoKeySecurity = new CryptoKeySecurity();
cspParams.CryptoKeySecurity.SetAccessRule(rule);
rsa = new RSACryptoServiceProvider(cspParams);
rsa.PersistKeyInCsp = false;
rsa.KeySize = 1024;
}
public static string[] GetKeys()
{
AssignParameter();
string[] keys = new string[2];
//privatekey
keys[0] = rsa.ToXmlString(true);
//publickey
keys[1] = rsa.ToXmlString(false);
return keys;
}
public static string EncryptData(string data2Encrypt, string key)
{
AssignParameter();
string publicOnlyKeyXML = key;
rsa.FromXmlString(publicOnlyKeyXML);
//read plaintext, encrypt it to ciphertext
byte[] plainbytes = System.Text.Encoding.Default.GetBytes(data2Encrypt);
byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
return Convert.ToBase64String(cipherbytes);
}
public static string DecryptData(string data2Decrypt, string key)
{
AssignParameter();
byte[] getpassword = Convert.FromBase64String(data2Decrypt);
string publicPrivateKeyXML = key;
rsa.FromXmlString(publicPrivateKeyXML);
//read ciphertext, decrypt it to plaintext
byte[] plain = rsa.Decrypt(getpassword, false);
return System.Text.Encoding.Default.GetString(plain);
}
Ah yes... stupid mistake (aren't they always?):
Normal Class
Static Crypto Class
End Static
End Normal
Can you find the problem and why i was getting collision errors? I've changed the Static to be Normal and all is well in my neck of the woods.
Cheers!

Check signature for x509 certificate

I have:
x509 certificate (Base64);
String data;
Signature of string data (Base64).
Is it possible to check signature?
My code:
bool valid = false;
var signature = Convert.FromBase64String(base64Signature);
var data = Encoding.UTF8.GetBytes(stringData);
var x509 = new X509Certificate2(Convert.FromBase64String(certificate));
var dsa = x509.PublicKey.Key as DSACryptoServiceProvider;
if (dsa!=null)
valid = dsa.VerifySignature(data, signature);
else {
var rsa = x509.PublicKey.Key as RSACryptoServiceProvider;
if (rsa!=null)
valid = rsa.VerifyHash(data, ???, signature);
}
I don't know what I should use instead of ???. It is possible to get hash algorithm from certificate?
The sender of the original message may use whatever algorithm he likes to sign his message, using the private key that corresponds to the certificate. While you can get the OID of the algorithm used to sign the certificate from its the SignatureAlgorithm property, nothing prevents the sender to use a different signing or hashing algorithm.
According to the documentation, the only valid hashing algorithms for the RSA provider are SHA1 and MD5. Perhaps you should try VerifyHash with both algorithms and check which one succeeds. You can get the proper OID for each one using the CryptoConfig.MapNameToOID method like this:
string sha1Oid = CryptoConfig.MapNameToOID("SHA1");
string md5Oid = CryptoConfig.MapNameToOID("MD5");
bool sha1Valid = rsa.VerifyHash(data, sha1Oid, signature);
bool md5Valid = rsa.VerifyHash(data, md5Oid, signature);
valid = sha1Valid || md5Valid;

Categories