ASP.Net equivalent of openssl_private_decrypt and openssl_pkey_get_private - c#

I want to decrypt text in ASP.Net.
The working code to decrypt the text in PHP is:
function rsaDecryption($dataEncrypted, &$dataDecrypted)
{
// Decrypt argument
$key = openssl_pkey_get_private(RsaKeyPrivate, RsaKeyPassphrase);
if (FALSE == $key)
{
echo("Failed to get the private key<br />\n");
return false;
}
if(!openssl_private_decrypt($dataEncrypted, $dataDecrypted, $key))
{
echo("Failed to decrypt message.<br />\n");
return false;
}
return TRUE;
}
I saw several articles on the web, but none was clear to me. This article (16319559) is the opposite than what I asked, but that was not really clear either.
The private key looks something like this
String myPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\n<whole bunch of characters>==\n-----END RSA PRIVATE KEY-----
What are the equivalent C# methods?
UPDATE:
I created a method using a couple of answers.
Decode string-based key using PEM function, see here.
OpenSSL code, DecodeRsaPrivateKey() and GetIntegerSize(), see here.
I also needed the following:
String strArgHtmlDecoded = HttpUtility.UrlDecode(dataEncrypted);
Byte[] byArgNo64 = Convert.FromBase64String(strArgHtmlDecoded);
That results in
Byte[] byArgDecrypted = oProviderRsa.Decrypt(byArgNo64, false);
strDecrypted = Encoding.Default.GetString(byArgDecrypted);

I figured it out.
The answer was a collection of code from various places.
The link in the comment was part of it. I saw that same article once before, but without the other code, I did not see the relevance.
See my update for the final answer.

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 to use my own public/private key to encrypt string C# Asp.net core

I have spent several hours trying to have a valid Encrypt/Decrypt function that uses Asymmetric encryption, and the best option seems to be RSA.
But The thing is that I want to be able to provide my own public/private key to the function as a string myself in the form : "769de1f1a9dd6e114f81b9490ea42a2967840353edd358a35c84e2c831dd40a2"
something very very similar to the 'eth-crypto' npm library for javascript.
but I haven't found any article or documentation that explains that.
so has anyone implemented it before or have an article that explains it.
keep in mind that when using asp.net core the FromXmlString doesn't work even when using 3.0
Here is my Encrypt function so far :
public static string EncryptAsymmetric(string textToEncrypt, string publicKeyString)
{
var bytesToEncrypt = Encoding.UTF8.GetBytes(textToEncrypt);
using (var rsa = new RSACryptoServiceProvider(2048))
{
try
{
rsa.FromXmlString(publicKeyString);
var encryptedData = rsa.Encrypt(bytesToEncrypt, true);
var base64Encrypted = Convert.ToBase64String(encryptedData);
return base64Encrypted;
}
finally
{
rsa.PersistKeyInCsp = false;
}
}
}
but it throws an error on the FromXmlString ( operation not supported on this platform )
so if there is any other way maybe......
any help is very appreciated because I have been looking into it for many hours with no result.

Generate the SAME JOSE token between client and server (PHP-C#)

Well, I'm trying to generate the same key with this two libraries:
https://github.com/dvsekhvalnov/jose-jwt this is for C#
https://github.com/Spomky-Labs/jose and this is for PHP
I have implemented both:
In C#:
public static string CypherData<T>(T payload)
{
return JWT.Encode(payload, pubkey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM, JweCompression.DEF);
}
Console.WriteLine(Api.CypherData("hola"));
This how I load public key PEM string:
private static RSACryptoServiceProvider GetPubKey()
{
ConnPreCheck();
JObject obj = JsonConvert.DeserializeObject<JObject>(getPubKey.HttpGet());
pubkeyStr = obj["data"]["pubKey"].ToString();
return PemKeyUtils.GetRSAProviderFromPemString(pubkeyStr);
}
And this is what it returns:
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.L3Teg2RSXXpp4jakLkl2PQgidJ5BIXyP5QZM2B4RaO0vs55aT22D9_dPl6d83KvXezM4YkOnyoF0JA0eLX3k8W4eWkHUhgoYqxgwQSWWhjrpDxcpNdXbDlB6zVb_BKf5upro5qm23nr_SOkkqhKJGrw_sIXvD1LjOburLolDqojrnvK5awGtiFwkPZjRh_wh4z4buEfYsWJCKhTLU6aG_DipmbAXq1o9u1-cqiQJC0JlPJRbh6JJDmVc9YqBv0W0rFEa7W5HA12TO-RtV42tEPApr3hNmD8QwzvxxZYKpMzoBAsSQcvSKk878qeOAcx3pZyoDZ6WzD-LRusWy7nJwOa6AC9NsL91mv8WsZxWar9AnzOsKleSJ7k8I477pXE_H1g7XnFBgmA4egF-721sa7SO2LtS440v1ytA4504sdjVYeOfWmRSU4UljnUqaYTd031fzCevzNEO0Q7mncn-sJACVHiwQB9c703SvYZaFOyzU-vdXUqBRTR6x0JvQd_lFNgSS9pOQC5BQbAKOME9fjdXiwRUKfyHXCUAUj88CJEMqxg3_VYYfUC04GahmoysR9QUpK3l84Z5TLOi47SvO-NkR-2wf7v4ko8bZIR3E6XFHFG9lWwdzR_JPz7fm0OFiYB0HN8XgWE4bQ2tasYsmFhWVfkeRZodnlqFvImSNjA.GIGFv4h_nYkpBiA5.1_66eMEb.BJTezaLucvfluWQ8VEzgCQ
In PHP:
Well, the PHP implementation is the following:
include(__DIR__ . "/../libs/jose/autoload.php");
$file = __DIR__ . '/../keys/public.key';
$contents = file_get_contents($file);
// This is the input we want to load verify.
$input = #$_GET["input"];
$key = new \Jose\KeyConverter\RSAKey($contents);
$jwe = \Jose\Factory\JWEFactory::createJWEToCompactJSON(
$input, // The message to encrypt
new \Jose\Object\JWK($key->toArray()), // The key of the recipient
[
'alg' => 'RSA-OAEP',
'enc' => 'A256GCM',
'zip' => 'DEF',
]
);
They are similar codes no?
But the output is different:
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJ6aXAiOiJERUYifQ.jbNHUCuXYcXZWrHsJrPclV-fjYmFWwwPj4t3kAOt7PahQfsz0a1GaRrODcwKce4yRtLyjv2U7CtFMxt9ah3XTwIqm1mzzPMhO4LnFIRqMRgsxEgIijRqNOOpE85M3UPBRqjYw0wdjaqfJToLVLwaHgUPCkOqsHrdOOWkxN20fZYy4Z1PQAC0rk2WqLD0x7Za1jdV6LvVtd18iFIaRqf2uNYcRePdyInRxwHGp9JfRkVCQILTfwHlxQBKrwJxhZOzdHrjjNjxNsjAsHGMix6MAbUR0YT4hlrE59eriDVxix4uQrrChuzWVz_kY9-zvB9SDZnYDdfgONYUgTMpbkMTVWsF40JmWJ6wvGs3HaaIxN17UjQLemKgrS4-bqvbnschhZTIGi5-f8Cr_SwGo411VNYhNpD4P1H2dEjmAFpn3SdW3Oi6pPgK2tvIpEwGUS-Gi29-aCCNeuyE2m5dbFW28G9HMmvZHAky4KHE4NlNJTrH0aBxQX_Gc7eTf9q00gJtKQ5CinXaUYAB0xSEbsNACZuFLPawuxj_3FGn6dHYFOkSsatCTcqV5tiGJXG6ns-wDc682-G9Mj8HCIzYzakp-yZLIaY00fg5xQdgKJrk0QbCtvbwiQGoupyMV9f3RpEBJznPrq4STypxskdh6jksB6T_1fhPqZMVt2BHR4phCBg.9380DJUDH4TQnnr7.zEuMQwRr.BfdStBMcVnVR-2ujTLjuaA
You can see the differences here:
Left is for C# and right for PHP. You can see it there: http://qbz28b-user.freehosting.host/html/Result.html
Obviously, when I try to decrypt with PHP the text generated with C#...
... the implementation of decrypting in PHP:
include(__DIR__ . "/../libs/jose/autoload.php");
$file = __DIR__ . '/../keys/private.key';
$contents = file_get_contents($file);
// This is the input we want to load verify.
$input = #$_GET["input"];
$jwk = new \Jose\KeyConverter\RSAKey($contents);
// We create our loader.
$loader = new Jose\Loader();
$loader->load($input);
// The payload is decrypted using our key.
$jws = $loader->loadAndDecryptUsingKey(
$input, // The input to load and decrypt
new \Jose\Object\JWK($jwk->toArray()), // The symmetric or private key
['RSA-OAEP'], // A list of allowed key encryption algorithms
['A256GCM'], // A list of allowed content encryption algorithms
$recipient_index // If decrypted, this variable will be set with the recipient index used to decrypt
);
$coreData["content"] = $jws->getPayload();
The following exception occurs:
Fatal error: Uncaught InvalidArgumentException: Unable to decrypt the
JWE. in C:\xampp\htdocs\z3nth10n-PHP\libs\jose\Decrypter.php:80 Stack
trace: #0 C:\xampp\htdocs\z3nth10n-PHP\libs\jose\Loader.php(95):
Jose\Decrypter->decryptUsingKeySet(Object(Jose\Object\JWE),
Object(Jose\Object\JWKSet), NULL) #1
C:\xampp\htdocs\z3nth10n-PHP\libs\jose\Loader.php(30):
Jose\Loader->loadAndDecrypt('eyJhbGciOiJSU0E...',
Object(Jose\Object\JWKSet), Array, Array, NULL) #2
C:\xampp\htdocs\z3nth10n-PHP\includes\actions.php(321):
Jose\Loader->loadAndDecryptUsingKey('eyJhbGciOiJSU0E...',
Object(Jose\Object\JWK), Array, Array, NULL) #3
C:\xampp\htdocs\z3nth10n-PHP\api.php(9):
include('C:\xampp\htdocs...') #4 {main} thrown in
C:\xampp\htdocs\z3nth10n-PHP\libs\jose\Decrypter.php on line 80
I have been looking in the code for so much time, and I have realized that the problem isn't in the code, the problem is in the token generated. And I don't know what can I do, you can test it here.
Encoding a word...
... and then, decrypting it.
Any suggestions to look at?

Google C++ code example explanation, translating to C#

I'm working with the Google DoubleClick ad exchange API. Their examples are in C++ and well, I'm pretty awful at C++. I'm trying to convert this to C# for something I'm working on and really, I think I just need some explanation of what is actually happening in certain blocks of this code sample. Honestly I kind of know what should happen over all but I'm not sure I am getting it 'right' and with encryption/decryption there isn't a 'sort of right'.
This is the full example from their API site:
bool DecryptByteArray(
const string& ciphertext, const string& encryption_key,
const string& integrity_key, string* cleartext) {
// Step 1. find the length of initialization vector and clear text.
const int cleartext_length =
ciphertext.size() - kInitializationVectorSize - kSignatureSize;
if (cleartext_length < 0) {
// The length can't be correct.
return false;
}
string iv(ciphertext, 0, kInitializationVectorSize);
// Step 2. recover clear text
cleartext->resize(cleartext_length, '\0');
const char* ciphertext_begin = string_as_array(ciphertext) + iv.size();
const char* const ciphertext_end = ciphertext_begin + cleartext->size();
string::iterator cleartext_begin = cleartext->begin();
bool add_iv_counter_byte = true;
while (ciphertext_begin < ciphertext_end) {
uint32 pad_size = kHashOutputSize;
uchar encryption_pad[kHashOutputSize];
if (!HMAC(EVP_sha1(), string_as_array(encryption_key),
encryption_key.length(), (uchar*)string_as_array(iv),
iv.size(), encryption_pad, &pad_size)) {
printf("Error: encryption HMAC failed.\n");
return false;
}
for (int i = 0;
i < kBlockSize && ciphertext_begin < ciphertext_end;
++i, ++cleartext_begin, ++ciphertext_begin) {
*cleartext_begin = *ciphertext_begin ^ encryption_pad[i];
}
if (!add_iv_counter_byte) {
char& last_byte = *iv.rbegin();
++last_byte;
if (last_byte == '\0') {
add_iv_counter_byte = true;
}
}
if (add_iv_counter_byte) {
add_iv_counter_byte = false;
iv.push_back('\0');
}
}
Step 1 is quite obvious. This block is what I am really not sure how to interpret:
if (!HMAC(EVP_sha1(), string_as_array(encryption_key),
encryption_key.length(), (uchar*)string_as_array(iv),
iv.size(), encryption_pad, &pad_size)) {
printf("Error: encryption HMAC failed.\n");
return false;
}
What exactly is happening in that if body? What would that look like in C#? There are a lot of parameters that do SOMETHING but it seems like an awful lot crammed in a small spot. Is there some stdlib HMAC class? If I knew more about that I might better understand what's happening.
The equivalent C# code for that block is:
using (var hmac = new HMACSHA1(encryption_key))
{
var encryption_pad = hmac.ComputeHash(iv);
}
It's computing the SHA1 HMAC of the initialization vector (IV), using the given encryption key.
The HMAC function is actually a macro from OpenSSL.
Just as a comment, I think it would be easier to implement this from their pseudocode description rather than from their C++ code.

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!

Categories