Python and C# Cryptography: What i'm doing wrong? - c#

I need to encrypt text in python and decrypt in C #. In python, I have this code:
I have this code in Python:
def genKey():
rsa = RSA.gen_key(2048, 65537)
rsa.save_key('c:/temp/priv-key.pem', callback=passwordCallback)
rsa.save_pub_key('c:/temp/pub-key.pem')
def encrypt():
varkey = readkey('c:/temp/pub-key.pem')
bio = BIO.MemoryBuffer(varkey)
rsa = RSA.load_pub_key_bio(bio)
encrypted = rsa.public_encrypt('My Text Here.', RSA.pkcs1_oaep_padding)
f = open("c:/temp/cript.txt", "w")
f.write(encrypted)
f.close()
This code uses M2Crypto.
Like I said, I want to decrypt the result generated up in C #. Below is my code:
static void Main(string[] args)
{
string text = GetText();
System.Text.ASCIIEncoding encoding=new System.Text.ASCIIEncoding();
Byte[] payload = encoding.GetBytes(text);
byte[] b = System.IO.File.ReadAllBytes(#"C:\temp\priv-key.pem");
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
OpenSSL.Core.BIO bio = new OpenSSL.Core.BIO(b);
OpenSSL.Crypto.CryptoKey key = OpenSSL.Crypto.CryptoKey.FromPrivateKey(bio, "mypassword");
RSA rsa = key.GetRSA();
byte[] result = rsa.PrivateDecrypt(payload, RSA.Padding.OAEP);
}
The problem is this line:
byte[] result = rsa.PrivateDecrypt(payload, RSA.Padding.OAEP);
When it is executed, this error occurs:
error:0407A079:rsa routines:RSA_padding_check_PKCS1_OAEP:oaep decoding error
error:04065072:rsa routines:RSA_EAY_PRIVATE_DECRYPT:padding check failed
The gurus of Cryptography and C# can help me?

You are writing the encryption ciphertext as text. Instead you should open your file in binary mode in python. Then in C# you do the same thing, but the other way around. Here you should return bytes instead of a string as ciphertext.
If you want to use text mode instead of binary mode then you could use base 64 encoding/decoding.

Related

How to convert C# RSA to php?

I want to convert this C# to php to use phpseclib RSA encrypt? Here is C#:
public static string Encrypt(string data)
{
CspParameters cs = new CspParameters();
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString("Key Goes here");
var dataToEncrypt = _UNICODE.GetBytes(data);
var encryptedByteArray = rsa.Encrypt(dataToEncrypt, false).ToArray();
return Convert.ToBase64String(encryptedByteArray);
}
and I want to convert it to php using phpseclib with RSA as here: http://phpseclib.sourceforge.net/rsa/examples.html#encrypt,enc2
But I don't know C#, and I don't know what to do with _UNICODE.GetBytes(data) and rsa.Encrypt(dataToEncrypt, false).ToArray() converting them to php. Please advise.
var dataToEncrypt = _UNICODE.GetBytes(data); converts a string to a byte array. phpseclib accepts strings, directly, so there's nothing to convert there.
As for rsa.Encrypt(dataToEncrypt, false).ToArray()... per Encrypt(Byte[], Boolean) the second parameter is fOAEP, which is described thusly:
true to perform direct RSA encryption using OAEP padding (only available on a computer running Windows XP or later); otherwise, false to use PKCS#1 v1.5 padding
It returns a byte array (byte[]) so running the output through .ToArray() seems kinda weird but whatever.
Anyway, based on all that, I think this should do the trick:
$rsa = new RSA;
$rsa->loadKey("Key Goes here");
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
echo base64_encode($rsa->encrypt($data));

Decrypting using RSACryptoServiceProvider.Decrypt

I'm trying to decrypt a message as part of a key exchange. I've got a 2048 bit RSA private key which I used to generate a certificate. I receive a message as part of a HTTP request which I need to decrypt with my private key. However, I receive the following error message when executing the last line:
"The data to be decrypted exceeds the maximum for this modulus of 256 bytes."
I've tried reducing the byte array of the data to decrypt as well as reversing it. If I do any of those two, I receive a "Bad Data" error.
Any help would be greatly appreciated.
Example of message to decode:
ajJDR09EQkUzT0prRHJlM2I1bzZGYjlaUWFpQTB6U2pQb0JGeDBvQ0tseEpYMGhmUkdSU0VJRnFnOEdQTDV5SlRJZmxoQUYzeFAxS3NGM1hFSnBobGl3Z3Y2UStydkY3ZkgvVmRLSit6bE5MZ3RTN0twUWZUaUZqMjlkLzBGVWVhL25qdnFXYTVrdlBrYUN2T2grZ1Rnc3FEd3U4ZVZiOUxhWVUzQWpRODk3MFY4VjM5c1VWYXRLcXdZbitQQkV4cFFSYXRJUlcyS2taSXpuRGZTVCt3dGZRcHMwU1lra3ZENSt6VHZnSGFRSmZNQXMvUlRiSERPVTZrNWo5dVR3SXNTOCtlalBWYjdMc1phOXU1c1plVTZpTlhvOUp1emxDalZpaVk3YnY0SkJCcHhqclRPaVA4NVhUYWg1TVhRYUZsMTZOVzE4dDMzYndnQmVkQmRwNEN3PT0=
C# code:
//http request containing the HMAC key which is encrypted against the public key
hmacKey = oCtx.RequestContext.RequestMessage.ToString();
hmacKey = hmacKey.Remove(0, 8);
hmacKey = hmacKey.Remove(hmacKey.Length - 9);
//decode into binary using Base64
byte[] data = Convert.FromBase64String(hmacKey);
string publicCert = "-----BEGIN CERTIFICATE-----......-----END CERTIFICATE-----";
string privateKey = "-----BEGIN RSA PRIVATE KEY-----......-----END RSA PRIVATE KEY-----";
byte[] certBuffer = Helpers.GetBytesFromPEM(publicCert, PemStringType.Certificate);
byte[] keyBuffer = Helpers.GetBytesFromPEM(privateKey, PemStringType.RsaPrivateKey);
X509Certificate2 x509cert = new X509Certificate2(certBuffer);
RSACryptoServiceProvider prov = Crypto.DecodeRsaPrivateKey(keyBuffer);
x509cert.PrivateKey = prov;
//tried to reduce the size of the data to decrypt as well as reversing it
//Array.Resize(ref data, 32);
//Array.Reverse(data);
byte[] result = prov.Decrypt(data, false);
More info on the GetBytesFromPEM method is available from this example:
http://www.codeproject.com/Articles/162194/Certificates-to-DB-and-Back
UPDATE:
Trying to decode twice, I get the following result:
code:
.....
byte[] data2 = Convert.FromBase64String(hmacKey);
string abc = Encoding.Default.GetString(data2);
byte[] data = Convert.FromBase64String(abc);
.....
byte[] result = prov.Decrypt(data, false);
string result2 = Encoding.Default.GetString(result);
result:
Óh#-šÚz;CÏ7
.«™"ã®ÿRè±àyéK.
The errors are basically due to encoding errors, both binary encoding (base 64) issues and character encoding issues (UTF-8/UTF-16).
Usually you would expect a binary HMAC to be encrypted. Instead the HMAC was hex encoded, which in turn was encoded using ASCII encoding (which is compatible with UTF-8). The .NET default is however UTF-16LE (what .NET incorrectly calls Unicode encoding).
The resulting ciphertext was base 64 encoded, which is what you would expect if the result needs to be transported in text. Instead double base 64 seemed to have been utilized. As the base 64 decoding resulted in another base 64 encoded string, the result was too large for the RSA decryption to handle.

Encrypt in Coldfusion and decrypt in C#

Here is the code used to encrypt in coldfusion
<cfset strBase64Value = encrypt(strValue,24 character key,AES) />
It is generating encrypted values like 714FEA9A9A2184769CA49D5133F08580 which seems odd to me considering it is only uppercase and numbers.
What C# library should I use to properly decrypt it ?
Also looking at this information, it seems that by default it uses the UUEncode algorithm to encode.
Should I ask the encrypter to use Base64 as encoding parameter ?
It is generating encrypted values like 714FEA9A9A2184769CA49D5133F08580
Then they are using "Hex", not the default "UUEncode". Either "hex" or "base64" is fine. As long as you both agree upon the encoding, it does not really matter.
You can use RijndaelManaged to decrypt the strings. However, the default encryption settings for ColdFusion and C# differ slightly. With the encrypt function:
"AES" is short for "AES/ECB/PKCS5Padding"
"ECB" mode does not use an IV
Key strings are always base64 encoded
NB: Despite the name difference, for the SUN provider, PKCS5Padding (CF/Java) corresponds to PaddingMode.PKCS7 (C#). As mentioned in this thread, the "... SUN provider in Java indicate[s] PKCS#5 where PKCS#7 should be used - "PKCS5Padding" should have been "PKCS7Padding". This is a legacy from the time that only 8 byte block ciphers such as (triple) DES symmetric cipher were available."
So you need to ensure your C# settings are adjusted to match. With that in mind, just decode the encrypted text from hex and the key string from base64. Using the slightly ugly example in the API, just adjust the algorithm settings to match those used by the encrypt() function:
Encrypt with ColdFusion
<cfscript>
plainText = "Nothing to see";
// 128 bit key base64 encoded
keyInBase64 = "Y25Aju8H2P5DR8mY6B0ezg==";
// "AES" is short for "AES/ECB/PKCS5Padding"
encryptedText = encrypt(plainText, keyInBase64, "AES", "hex");
WriteDump( encryptedText );
// result: 8889EDF02F181158AAD902AB86C63951
</cfscript>
Decrypt with C#
byte[] bytes = SomeMethodToConvertHexToBytes( encryptedText );
byte[] key = Convert.FromBase64String( keyInBase64 );
string decryptedText = null;
using (RijndaelManaged algorithm = new RijndaelManaged())
{
// initialize settings to match those used by CF
algorithm.Mode = CipherMode.ECB;
algorithm.Padding = PaddingMode.PKCS7;
algorithm.BlockSize = 128;
algorithm.KeySize = 128;
algorithm.Key = key;
ICryptoTransform decryptor = algorithm.CreateDecryptor();
using (MemoryStream msDecrypt = new MemoryStream(bytes))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
decryptedText = srDecrypt.ReadToEnd();
}
}
}
}
Console.WriteLine("Encrypted String: {0}", encryptedText);
Console.WriteLine("Decrypted String: {0}", decryptedText);
Keep in mind you can (and probably should) adjust the settings, such as using the more secure CBC mode instead of ECB. You just need to coordinate those changes with the CF developer.
If anyone had similar problem with JAVA I just implemented encryption and decryption of string previously encrypted/decrypted in coldfusion with "Hex" and "tripledes". Here is my code:
private static final String PADDING = "DESede/ECB/PKCS5Padding";
private static final String UTF_F8 = "UTF-8";
private static final String DE_SEDE = "DESede";
private String secretKey;
public String encrypt(String message) throws Exception {
secretKey = getSecretKey();
final byte[] secretBase64Key = Base64.decodeBase64(secretKey);
final SecretKey key = new SecretKeySpec(secretBase64Key, DE_SEDE);
final Cipher cipher = Cipher.getInstance(PADDING);
cipher.init(Cipher.ENCRYPT_MODE, key);
final byte[] plainTextBytes = message.getBytes();
final byte[] cipherText = cipher.doFinal(plainTextBytes);
return Hex.encodeHexString(cipherText);
}
public String decrypt(String keyToDecrypt) throws Exception {
secretKey = getSecretKey();
byte[] message = DatatypeConverter.parseHexBinary(keyToDecrypt);
final byte[] secretBase64Key = Base64.decodeBase64(secretKey);
final SecretKey key = new SecretKeySpec(secretBase64Key, DE_SEDE);
final Cipher decipher = Cipher.getInstance(PADDING);
decipher.init(Cipher.DECRYPT_MODE, key);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, UTF_F8);
}

Python to C# AES CBC PKCS7

I'm trying to convert this C# code to Python (2.5, GAE). The problem is that the encrypted string from the python script is different each time the encryption (on the same string) is run.
string Encrypt(string textToEncrypt, string passphrase)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Mode = CipherMode.CBC;
rijndaelCipher.Padding = PaddingMode.PKCS7;
rijndaelCipher.KeySize = 128;
rijndaelCipher.BlockSize = 128;
byte[] pwdBytes = Encoding.UTF8.GetBytes(passphrase);
byte[] keyBytes = new byte[16];
int len = pwdBytes.Length;
if (len > keyBytes.Length)
{
len = keyBytes.Length;
}
Array.Copy(pwdBytes, keyBytes, len);
rijndaelCipher.Key = keyBytes;
rijndaelCipher.IV = new byte[16];
ICryptoTransform transform = rijndaelCipher.CreateEncryptor();
byte[] plainText = Encoding.UTF8.GetBytes(textToEncrypt);
return Convert.ToBase64String(transform.TransformFinalBlock(plainText, 0, plainText.Length));
}
Python code: (PKCS7Encoder: http://japrogbits.blogspot.com/2011/02/using-encrypted-data-between-python-and.html)
from Crypto.Cipher import AES
from pkcs7 import PKCS7Encoder
#declared outside of all functions
key = '####'
mode = AES.MODE_CBC
iv = '\x00' * 16
encryptor = AES.new(key, mode, iv)
encoder = PKCS7Encoder()
def function(self):
text = self.request.get('passwordTextBox')
pad_text = encoder.encode(text)
cipher = encryptor.encrypt(pad_text)
enc_cipher = base64.b64encode(cipher)
The C# code is inherited. Python code must be encrypted and decrypted the same way so that the C# code can decode the value correctly.
Note: I am a noob at python :)
Edit: sorry. should have made the distinction that there was a function being called.
Thanks!
Your C# code is invalid.
The Encrypt function takes in the passphrase as string passphrase but then tries to reference it in this line byte[] pwdBytes = Encoding.UTF8.GetBytes(key);
Change key to passphrase.
The two functions now produce identical results for me:
Python
secret_text = 'The rooster crows at midnight!'
key = 'A16ByteKey......'
mode = AES.MODE_CBC
iv = '\x00' * 16
encoder = PKCS7Encoder()
padded_text = encoder.encode(secret_text)
e = AES.new(key, mode, iv)
cipher_text = e.encrypt(padded_text)
print(base64.b64encode(cipher_text))
# e = AES.new(key, mode, iv)
# cipher_text = e.encrypt(padded_text)
# print(base64.b64encode(cipher_text))
C# (with the typo fix mentioned above)
Console.WriteLine(Encrypt("The rooster crows at midnight!", "A16ByteKey......"));
Python Result
XAW5KXVbItrc3WF0xW175UJoiAfonuf+s54w2iEs+7A=
C# Result
XAW5KXVbItrc3WF0xW175UJoiAfonuf+s54w2iEs+7A=
I suspect you're re-using 'e' in your python code multiple times. If you uncomment the last two lines of my python script, you'll see the output is now different. But if you uncomment the last three lines, you'll see the output is the same. As Foon said, this is due to how CBC works.
CBC (Cipher-block chaining) works when encrypting a sequence of bytes in blocks. The first block is encrypted by incorporating the IV with the first bytes of your plaintext ("The rooster..."). The second block uses the result of that first operation instead of the IV.
When you call e.encrypt() a second time (e.g. by uncommmenting the last two lines of the python script) you pick up where you left off. Instead of using the IV when encrypting the first block, it will use the output of the last encrypted block. This is why the results look different. By uncommening the last three lines of the python script you initialize a new encryptor which will use the IV for its first block, causing you to get identical results.
changed python code to:
from Crypto.Cipher import AES
from pkcs7 import PKCS7Encoder
#declared outside of all functions
key = '####'
mode = AES.MODE_CBC
iv = '\x00' * 16
encoder = PKCS7Encoder()
def function(self):
encryptor = AES.new(key, mode, iv)**
text = self.request.get('passwordTextBox')
pad_text = encoder.encode(text)
cipher = encryptor.encrypt(pad_text)
enc_cipher = base64.b64encode(cipher)
in case anyone reaches this page via google
This esotic PKCS7 encoder is anything else then a function that pads with a static lenght.
So I implemented it with a very chip of code
#!/usr/bin/env python
from Crypto.Cipher import AES
import base64
# the block size for the cipher object; must be 16, 24, or 32 for AES
BLOCK_SIZE = 16
# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length. This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
# PKCS7 method
PADDING = '\x06'
mode = AES.MODE_CBC
iv = '\x08' * 16 # static vector: dangerous for security. This could be changed periodically
#
# one-liner to sufficiently pad the text to be encrypted
pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
# one-liners to encrypt/encode and decrypt/decode a string
# encrypt with AES, encode with base64
EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
def CryptIt(password, secret):
cipher = AES.new(secret, mode, iv)
encoded = EncodeAES(cipher, password)
return encoded
def DeCryptIt(encoded, secret):
cipher = AES.new(secret, mode, iv)
decoded = DecodeAES(cipher, encoded)
return decoded
I hope that this could help.
Cheers
Microsoft's implementation of PKCS7 is a bit different than Python's.
This article helped me with this problem:
http://japrogbits.blogspot.com/2011/02/using-encrypted-data-between-python-and.html
His code for pkcs7 encoding and decoding is on github here:
https://github.com/janglin/crypto-pkcs7-example
With that PKCS7 library, this code worked for me:
from Crypto.Cipher import AES
aes = AES.new(shared_key, AES.MODE_CBC, IV)
aes.encrypt(PKCS7Encoder().encode(data))

RSA C# Encrypt Java Decrypt

In my program (server side - Java) I've created keystore file, with command:
keytool -genkey -alias myalias -keyalg RSA -validity 10000 -keystore my.keystore
and exported related X509 certificate with:
keytool -export -alias myalias -file cert.cer -keystore my.keystore
After I saved cert.cer on client side (C#) and I write this code:
X509Certificate2 x509 = new X509Certificate2();
byte[] rawData = ReadFile("mycert.cer");
x509.Import(rawData);
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)x509.PublicKey.Key;
byte[] plainbytes = System.Text.Encoding.ASCII.GetBytes("My Secret");
byte[] cipherbytes = rsa.Encrypt(plainbytes, true);
String cipherHex = convertToHex(cipherContent);
byte[] byteArray = encoding.GetBytes(cipherHex);
....
I write this Java code on server side:
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream("C:\\my.keystore"), "mypass".toCharArray());
Key key = keyStore.getKey("myalias", "mypass".toCharArray());
if (key instanceof PrivateKey) {
Certificate cert = keyStore.getCertificate("myalias");
PublicKey pubKey = cert.getPublicKey();
privKey = (PrivateKey)key;
}
byte[] toDecodeBytes = new BigInteger(encodeMessageHex, 16).toByteArray();
Cipher decCipher = Cipher.getInstance("RSA");
decCipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] decodeMessageBytes = decCipher.doFinal(toDecodeBytes);
String decodeMessageString = new String(decodeMessageBytes);
I receive this error:
javax.crypto.BadPaddingException: Data must start with zero
Can you help me, please?
Thanks thanks,
Your method of hex-decoding in Java using BigInteger will produce the wrong result much of the time because the Java BigInteger class encodes a value whose high-order byte is >= 128 with an extra zero byte at the front. Use the Apache commons codec for hex en/de-coding.
EDIT: Your C# code is not correct. There is a .NET class System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary that will do the hex en/de-coding for your C# code. The following C# code fragment should work better
public static String execute(String content)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] plainbytes = System.Text.Encoding.ASCII.GetBytes(content);
byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
SoapHexBinary hexBinary = new SoapHexBinary(cipherbytes);
String cipherHex = hexBinary.ToString();
// This String is used on java side to decrypt
Console.WriteLine("CIPHER HEX: " + cipherHex);
return cipherHex;
}
EDIT 2:
Seems that SoapHexBinary class might have had a short life in .NET. There are a number of good solutions to the problem at this msdn link.
Hm, I haven't done exactly what you are here, I had an DES one I had to do, and I was getting the same error. The trick was I had to get compatible CipherModes and PaddingModes on both sides. For mine it was PaddingMode.PKCS7 and CipherMode.CBC on the csharp side, and on the java side I used the DESede/CBC/PKCS5Padding xform.
hth
Mike
Thanks to GregS answers I found solution of my problem. Now I post this.
C# SIDE (Client-Side)
X509Certificate2 x509 = new X509Certificate2();
byte[] rawData = ReadFile("mycert.cer");
x509.Import(rawData);
After I loads my X509Certificate I call my execute method:
public static String execute(String content)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] plainbytes = System.Text.Encoding.ASCII.GetBytes(content);
byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
SoapHexBinary hexBinary = new SoapHexBinary(cipherbytes);
String cipherHex = hexBinary.ToString();
// This String is used on java side to decrypt
Console.WriteLine("CIPHER HEX: " + cipherHex);
return cipherHex;
}
JAVA SIDE (Server-Side)
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream("C:\\my.keystore"), "mypass".toCharArray());
Key key = keyStore.getKey("myalias", "mypass".toCharArray());
if (key instanceof PrivateKey) {
Certificate cert = keyStore.getCertificate("myalias");
PublicKey pubKey = cert.getPublicKey();
privKey = (PrivateKey)key;
}
byte[] toDecodeBytes = new BigInteger(encodeMessageHex, 16).toByteArray();
Cipher decCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
decCipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] decodeMessageBytes = decCipher.doFinal(toDecodeBytes);
String decodeMessageString = new String(decodeMessageBytes);
The problem was in Hex-Encryption on C# Side that are completely different on Java side. Thanks GregS, you are the best ;)

Categories

Resources