RSA Encrypting password with a public Modulus and exponent in Python - c#

I need to encrypt a password and add it as a http header to make a rest call from a python client. I am trying to implement the following C# code listed below in Python, but the rest POST request seems to be failing with Python as the encrypted password string generated from python does not seem to be matching the encrypted password from C# . The C# code generates the correct encrypted password
modulus: "w1jcEfmxCTz5aB9wGg1Vl5K45VUm8Aj7+05sBarmrwbvC9BNjAqSySPmC2ajWSQGdmBs4xylKZjHKaXg5rxuNw=="
exponent:
"AQAB"
password to encrypt:'tricky'
encrypted password from C#(keeps changing each time it is generated): '%14%1d%0a%bb%a0X%24H%ad%ce%9aG%f6a%dau%d8%01%ec%d5)+%d3%11%8e%3ew%c8K%dce%ec%84K%e6%1d%ea%81%3e%d14%87%80s%8eo%a6%bc%fd%1b%8f%a1V8%c8%96%b1%ec%1f%d7qd%bbz'
encrypted password from Python:'%21%F6%7E.i%F4%F4%5E%E5%A9v%03E%8C%1C%3E%F1%D7%DBT%A2%03z%BF%E2%E8%8FJh%E3%85%AA%24%25%C2%C9Hg%18z%22a%F8g%0B%81%3C%DC%FEr%F8C%98s%B5%DA1%F6%60%23%BAw%10F'
Here is my python code using Pycrypto which does the encryption:
from base64 import b64decode
from Crypto.PublicKey.RSA import construct
def get_encrypted_password(password, modulus, exponent):
password = password.encode('utf-8')
# decode base64 string to be used as modulus(n) and exponent(e) components for constructing the RSA public key object
modulus = b64decode(modulus)
exponent = b64decode(exponent)
n = int.from_bytes(modulus, byteorder=sys.byteorder)
e = int.from_bytes(exponent, byteorder=sys.byteorder)
pubkey = construct((n,e))
encrypted = pubkey.encrypt(password,None)[0]
#url encode the encrypted password
encrypted = urllib.parse.quote_plus(encrypted)
return encrypted
This is the C# code which does the encryption:
public static string EncryptForTransport(string strToEncrypt, string rsaPublicKey)
{
KeyContainerPermission permission = new KeyContainerPermission(KeyContainerPermissionFlags.AllFlags);
permission.Assert();
if (string.IsNullOrEmpty(strToEncrypt))
{ return strToEncrypt; }
RSACryptoServiceProvider rsaEncryptor = GetRsaEncryptor(rsaPublicKey);
byte[] buffer = rsaEncryptor.Encrypt(Encoding.UTF8.GetBytes(strToEncrypt), false);
try
{
rsaEncryptor.Clear();
}
catch (CryptographicException)
{
//errors may occur ignore them.
}
string encryptedStr = HttpUtility.UrlEncode(buffer);// byteConverterGetString;
return encryptedStr;
}
private static RSACryptoServiceProvider GetRsaEncryptor(string rsaPublicKey)
{
RSACryptoServiceProvider.UseMachineKeyStore = true;
RSACryptoServiceProvider rsaEncryptor = RSACryptoServiceProvider.Create() as RSACryptoServiceProvider;
if (rsaEncryptor.PersistKeyInCsp)
rsaEncryptor.PersistKeyInCsp = false;
rsaEncryptor.FromXmlString(rsaPublicKey);
return rsaEncryptor;
}
Any ideas on what I might be doing wrong with encrypting the password using RSA in Python?

To match what the C# code is doing you must parse the big-endian modulus and exponent correctly, and use PKCS v1.5 padding. This example below modifies your code slightly to show this.
from base64 import b64decode
from Crypto.PublicKey.RSA import construct
from Crypto.Cipher import PKCS1_v1_5
import urllib.parse
def get_encrypted_password(password, modulus, exponent):
password = password.encode('utf-8')
# decode base64 string to be used as modulus(n) and exponent(e) components for
# constructing the RSA public key object
modulus = b64decode(modulus)
exponent = b64decode(exponent)
n = int.from_bytes(modulus, byteorder='big')
e = int.from_bytes(exponent, byteorder='big')
pubkey = construct((n, e))
pubkey = PKCS1_v1_5.new(pubkey)
encrypted = pubkey.encrypt(password)
# url encode the encrypted password
encrypted = urllib.parse.quote_plus(encrypted)
return encrypted

Related

Openssl Encrypting and signing data using symmetric key in php

The input is base64
byte[] encryptedContent = _crypto.AESEncrypt(input, key);
string bContent = Convert.ToBase64String(encryptedContent);
byte[] bSignature = _crypto.RSASign(Encoding.ASCII.GetBytes(bContent));
request.Data.Content = bContent;
request.Data.Signature = Convert.ToBase64String(bSignature);
Hello can someone help me to convert the above C# code to a php. Am trying to send an encrypted and a signed content to the server. but I get Data decryption error.
// encryption code
$encrypt_json = openssl_encrypt(json_encode($Pjoson),
"AES-128-GCM",
"$SMKey");

Generate Digest using SHA256 and Signing it with RSA C# Import key as String

I'm new to cryptography.
My goal is to generate a digest from a string with SHA256 and then signing it with RSA.
I've already generated the certificate (Public Key, Private key and the pem certificate) from a 3rd party.
I need to be able to import such Private key (from a string), generate the SHA256 digest and sign it using RSA. Such should generate a base64 string with an aprox. lenght of 344 chars.
I'll rather use .net native functions without use of 3rd party libraries.
For example:
String password = "hello world";
var privateKey = "MII..."; //Get just the base64 content.
var privateKeyBytes = Convert.FromBase64String(privateKey);
var password_byte = Encoding.ASCII.GetBytes(password);
using var rsa = RSA.Create();
rsa.ImportFromEncryptedPem(privateKey.ToCharArray(),password.ToCharArray());
decryptedData = rsa.Encrypt(dataToEncrypt_bytes, RSAEncryptionPadding.OaepSHA1);
//Display the decrypted plaintext to the console.
Console.WriteLine("Decrypted new: {0}", Convert.ToBase64String(decryptedData));
Thanks in advance.

Decrypting RSA data encrypted in python with cryptodome in C# using bouncycastle gives error block incorrect

I am using the following function to encrypt RSA data in PHP:
function RSAEncrypt($text){
$priv_key=file_get_contents("privateKey.key");
//$passphrase is required if your key is encoded (suggested)
$priv_key_res = openssl_get_privatekey($priv_key);
if(!openssl_private_encrypt($text,$crypttext,$priv_key_res)){
echo "Error: " . openssl_error_string ();
}
return $crypttext;
}
I am decoding this in C# with the following function:
public static string RSADecrypt(string b64cipher, string pemcert) {
byte[] bytesCypherText = Convert.FromBase64String(b64cipher);
Org.BouncyCastle.X509.X509Certificate cert = (Org.BouncyCastle.X509.X509Certificate)new Org.BouncyCastle.OpenSsl.PemReader(new StringReader(pemcert)).ReadObject();
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
//var decryptEngine = new OaepEncoding(new RsaEngine());
decryptEngine.Init(false, cert.GetPublicKey());
string decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesCypherText, 0, bytesCypherText.Length));
return decrypted;
}
I want to replace the PHP function with python, and tried the following:
from Cryptodome.PublicKey import RSA
from Cryptodome.Cipher import PKCS1_OAEP, AES, PKCS1_v1_5
import base64
from Cryptodome import Random
from Cryptodome.Random import get_random_bytes
import hashlib
def encrypt_private_key(a_message):
with open("privateKey.key", 'r') as f:
private_key = RSA.importKey(f.read())
#encryptor = PKCS1_OAEP.new(private_key)
encryptor= PKCS1_v1_5.new(private_key)
encrypted_msg = encryptor.encrypt(a_message.encode())
encoded_encrypted_msg = base64.b64encode(encrypted_msg)
return encoded_encrypted_msg
However, when decoding I get the following error:
InvalidCipherTextException: block incorrect
at byte[] Org.BouncyCastle.Crypto.Encodings.Pkcs1Encoding.DecodeBlock
(byte[] input, int inOff, int inLen) at string RSADecrypt (string
b64cipher, string pemcert)
If I try to use PKCS1_OAEP (in python and c#, see commented code), I am getting a data wrong exception.
Not sure what am I missing
After some research, it seems python lirary does not allow to make private key encryption as it is not a standard operation, however in my case I still wanted to do it, and used the code here:
https://www.php2python.com/wiki/function.openssl-private-encrypt/
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import load_pem_private_key
def openssl_private_encrypt(data):
"""Encrypt data with RSA private key.
This is a rewrite of the function from PHP, using cryptography
FFI bindings to the OpenSSL library. Private key encryption is
non-standard operation and Python packages either don't offer
it at all, or it's incompatible with the PHP version.
The backend argument MUST be the OpenSSL cryptography backend.
"""
# usage
key = load_pem_private_key(open("key.pem").read().encode(
'ascii'), None, backend=default_backend())
backend = default_backend()
length = backend._lib.EVP_PKEY_size(key._evp_pkey)
buffer = backend._ffi.new('unsigned char[]', length)
result = backend._lib.RSA_private_encrypt(
len(data), data, buffer,
backend._lib.EVP_PKEY_get1_RSA(key._evp_pkey),
backend._lib.RSA_PKCS1_PADDING)
backend.openssl_assert(result == length)
res = backend._ffi.buffer(buffer)[:]
print(res)
return base64.b64encode(backend._ffi.buffer(buffer)[:]).decode()

RSA Encryption and Decryption in C#.NET

I have below code to encrypt and decrypt the message in c#. when i am trying to run it is giving an exception ie "The data to be decrypted exceeds the maximum for this modulus of 256 bytes"
public static void Main(string[] args)
{
X509Certificate2 cert = new X509Certificate2(#"C:\Data\ABC-rsa-public-key-certificate.cer");
string encryptedText = EncrypIt("Hello", cert);
string decryptedText = DecrptIt(encryptedText, cert);
System.Console.WriteLine(decryptedText);
}
public static string EncrypIt(string inputString, X509Certificate2 cert)
{
RSACryptoServiceProvider publicKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] plainBytes = Encoding.UTF8.GetBytes(inputString);
byte[] encryptedBytes = publicKey.Encrypt(plainBytes, false);
string encryptedText = Encoding.UTF8.GetString(encryptedBytes);
return encryptedText;
}
public static string DecrptIt(string encryptedText, X509Certificate2 cert)
{
RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)cert.PublicKey.Key;
byte[] encryptedBytes = Encoding.UTF8.GetBytes(encryptedText);
byte[] decryptedBytes = privateKey.Decrypt(encryptedBytes, false);
string decryptedText = Encoding.UTF8.GetString(decryptedBytes);
return decryptedText;
}
Several problems:
RSA by default only encrypts one block. It's not suitable for long messages. You shouldn't encrypt the message itself with RSA. Generate a random AES key and encrypt the key with RSA and the actual message with AES.
You must use a binary safe encoding such as Hex or Base64 for the ciphertext. Using UTF-8 corrupts the data since it doesn't allow arbitrary byte sequences.
UTF-8 is designed to encode text, so it's fine for your plaintext.
Use OAEP, the old 1.5 padding mode is not secure. i.e. pass true as second parameter to Encrypt/Decrypt. (Technically it's possible to use it securely, but it's tricky and I wouldn't recommend it)
As a further note, once you use AES, there are some more pitfalls: 1) Use a MAC in an encrypt-then-mac scheme, else active attacks including padding-oracles will break your code 2) Use a random IV that's different for each message
RSA should not be used to encrypt this kind of data. You should be encrypting your data with a symmetric key like AES, then encrypting the symmetric key with RSA.

Encrypt in C# using RSACryptoServiceProvide and decrypt using openssl_private_decrypt in PHP

I spent whole day for trying to get it work but no luck:(
I use these code in C# for encryption:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters rsaParam = rsa.ExportParameters(false);
rsaParam.Modulus = Convert.FromBase64String("MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwlhAsNcNCDRgzCc49u/0iSDrdJn7yoiH/HHipbQp0QSejzg/48mMA6wb32OPQ7qzBgJNvwiQbMvi89BvGNAJ9K8vM0RW7WOqtnb/8IK9BAJVtEwJ3vvKTf5EluiUgWVbGYpWPjbl/lsD3/hRTR0uF46h7q4OlARxOupl9xVS2wQIDAQAB");
rsa.ImportParameters(rsaParam);
string msg = "This is a test.";
byte[] encValue = rsa.Encrypt(Encoding.UTF8.GetBytes(msg), true);
Console.WriteLine(Convert.ToBase64String(encValue));
This is the PHP code I use to decrypt.
// Read key
$fp = fopen($KeyPath,"r");
$Key = fread($fp,8192);
fclose($fp);
openssl_private_decrypt($data, $decrypted, openssl_get_privatekey($Key, "123456"));
The private key I used(Passphase "123456"):
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,16B167A1F1E4E61E
A0eOxhU9Fp4ZIkmSpCUOA2VGG8evE71bEz/eO5LlUatUTt4RQcu68mFOM23PdnHl
YqjTV7AwedUu+LsNtjDfy+NJzvvi+re/kpYAD9RWDE0buHGp86vIhxJLCA633JSt
kcMbkFBBbJPBW74FK7Djo2tlE/jKFb4Uy6EIBEsI56pcbdOIKWHTOLHb3/gG8spx
/jPyylR1D1Rm1Nw9mhfQ8c+GZoXYn919Zx9fvKYq5CiH8hqy6q1TCsTjUpdgdkxz
3kDQ6nn4cOCCwCwU2F+iRpzmSE0DORPtCK/rNdhHXaVqm/SvIDSerpX6L4l8NiRx
Vb19htQChysfB7O/XiDVxL8gqSUmMrSujP50NUlyuBEG/32JyCounlX/4JQEGvAN
ALGkLwULhp1D2ATQVck5aNncxcbuB56laf+KZm5E83Tbeu1j4MG+JzJq+kbLVuYi
7TJ1JqjjL4Ixlyh/M23UuViFsw1V0zuZslFhvtq0/hdNXhIgNVmMFPFadOSKMtVY
kTkX7+coEXrtwPV+4ztoH3M2+zwZczkNECbed9H0PPw6uhrOpu+EyGR4qJ/TsMe1
Ht60veBMuPhwC5TqKP8Luz8x57d5Y4+kBFMvQda1b38PUuXSRw2OZ+cBHk0wrET/
pnyOIhg3lvREPvhXpe1oyaSZZLu95xTAj9YSDG+iKToDCD8hgdaRn1Pi6VOaz8Ru
+AUjz0L0fbwrU7jZ3x2L1AGsdwVwmwTL8Fwk/WY8sWu3KCW4olW1nQ8o/4+jO7q9
JvrYW/HxqIxP0Mnc9ODNmaG/NH1q5v7LIPAz47bpqXwdr8hDV/L5mA==
-----END RSA PRIVATE KEY-----
I am not familiar with encryption, can some one please tell how to get it work?
PS: I think the code in php is fine since I tested the code seperately.
You can use this code for encrypt your string.
public string EncodeData(string sData)
{
try {
byte[] encData_byte = new byte[sData.Length];
encData_byte = System.Text.Encoding.UTF8.GetBytes(sData);
string encodedData = Convert.ToBase64String(encData_byte);
return encodedData;
} catch (Exception ex) {
throw new Exception("Error in base64Encode" + ex.Message);
}
}
and for decrypt your string you can use this code.
public string DecodeData(string sData)
{
System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
System.Text.Decoder utf8Decode = encoder.GetDecoder();
byte[] todecode_byte = Convert.FromBase64String(sData);
int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
char[] decoded_char = new char[charCount];
utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
string result = new String(decoded_char);
return result;
}
Try this this will work.
Thank you.
Are you sure RSACryptoProvider is working with the key in the format you're providing it in?
A lot of examples of RSACryptoProvider I've seen use XML-based private keys. eg.
RSA Signing with PHP and verifying with C#
"<RSAKeyValue><Modulus>3BqiIB3ouyXHDMpW43TlZrx8fkts2FVVARJKNXFRQ/WIlsthDzL2jY2KEJVN6BKE4A51X+8LMzAI+2z3vIgAQT3bRSfOwygpGBjdhhnXJwFlQ6Gf/+z0ffQfVx/DHw3+QWphcwGDBst+KIA6u6ayy+RDE+jEityyyWDiWqkR9J8=</Modulus><Exponent>AQAB</Exponent><P>8a8nuVhIANh7J2TLn4wWTXhZY1tvlyFKaslOeAOVr+wgEWLQpLZ0Jpjm8aUyyOYPXlk7xrA5BOebtz41diu4RQ==</P><Q>6SQ9y3sEMjrf/c4bHGVlhOj4LUVykradWWUNC0ya7llnR8y1djJ1uUut+EoAa1JQCGukuv4K8NvN1Ieo72Fhkw==</Q><DP>cg0VMusNN5DxNRrk2IrUL4TesfuBQpGMO6554DrY1acZTvsRuNj9IQXA3kH2IEYo9H4prk6U6dKeci/iLLze/Q==</DP><DQ>m/pZNXeZ+RkWnrFzxe24m9FZqMAbxThT0Wkf7v1Tcj9yL8EvbmKYDF4riD/KRAMP9HJABbLNExObg6M3TOAz7Q==</DQ><InverseQ>w8PvW8srrPCuOcphBKXSyoZxCZn81+rovBxuE8AB95m5X+URE8SunK7f+g7hBBin6nUOaVGohBP8jzkQEsdx1Q==</InverseQ> <D>AsVPDypxOJHkLJQLffeFv8JVqt1WNG72j/nj90JC7KEVpBhRU3inw+ZpO4Y1odtB0vQ7pAaFVJKhOlEH2Va48hNUEQujML8rE+LZXgI3lu0TlqOCIqTHIljeJry0ca30XFtFDp9kh0Kr/0CgGMqgIed+hDUjAad8ke9D2YicDok=</D></RSAKeyValue>"
I searched for days and finally figured out myself:)
I am loading the parameters in the wrong way.
According to the RSA Public key structures(PEM):
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwlhAsNcNCDRgzCc49u/0iSDrdJn7yoiH/HHipbQp0QSejzg/48mMA6wb32OPQ7qzBgJNvwiQbMvi89BvGNAJ9K8vM0RW7WOqtnb/8IK9BAJVtEwJ3vvKTf5EluiUgWVbGYpWPjbl/lsD3/hRTR0uF46h7q4OlARxOupl9xVS2wQIDAQAB
Which I split the string into 3 parts(base64 encoded):
Header
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC
Modulus:
wlhAsNcNCDRgzCc49u/0iSDrdJn7yoiH/HHipbQp0QSejzg/48mMA6wb32OPQ7qzBgJNvwiQbMvi89BvGNAJ9K8vM0RW7WOqtnb/8IK9BAJVtEwJ3vvKTf5EluiUgWVbGYpWPjbl/lsD3/hRTR0uF46h7q4OlARxOupl9xVS2wQ
Exponent:
IDAQAB
(Note that I still didn't get a clear idea of the RSA key strutures. Those above are just a blurry view of a key structure, but for those who interested in, I recommend you to read the API Documentation "RSAParameters" or the RSA specification)
Obviously what I was doing is to import the entire key string to the RSAParameters.Modulus. That is not the way to import the key. So that's why it didn't work.
The way to do it is to extract the modulus and exponent which was needed for a public encryption from the key file. And import into RSAParameters
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
Then encrypt the string:
RSA.Encrypt("HAHA I GOT IT!!", false);
The way to extract. I recommend going to JavaScience for more info. There are bunch of cryptographic utilities there.

Categories