I am trying to decrypt data that is encrypted using the Bouncy Castle library in Java with a C#. The data that is encrypted in Java (again with the Bouncy Castle Libraries) can be decrypted with Java. I am using the same keys and parameters but when I reach DoFinal I get the error "pad block corrupted".
Here is the Java:
KeyParameter keyParam = new KeyParameter(key);
CipherParameters param = new ParametersWithIV(keyParam, initVector);
BlockCipherPadding padding = new PKCS7Padding();
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), padding);
cipher.reset();
cipher.init(true, param);
byte[] fileBytes = Base64.decodeBase64(decryptedStringData);
byte[] encrypted = new byte[cipher.getOutputSize(fileBytes.length)];
int l = cipher.processBytes(fileBytes, 0, fileBytes.length, encrypted, 0);
l += cipher.doFinal(encrypted, l);
return (Base64.encodeBase64String(encrypted));
Here is the C#:
KeyParameter keyParam = new KeyParameter(key);
ICipherParameters param = new ParametersWithIV(keyParam, initVector);
IBlockCipherPadding padding = new Pkcs7Padding();
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new AesEngine()), padding);
cipher.Reset();
cipher.Init(false, param);
byte[] fileBytes = Convert.FromBase64String(encryptedDataString);
byte[] decrypted = new byte[cipher.GetOutputSize(fileBytes.Length)];
int l = cipher.ProcessBytes(fileBytes, 0, fileBytes.Length, decrypted, 0);
l += cipher.DoFinal(decrypted, l);
return(Convert.ToBase64String(decrypted));
I am generating a 32 byte PBK for the key based on a hash that has been buffered... however, we checked the key generated between Java and C# and they are the same.
It turns out that the data provided for encryption and decryption were encoded in different formats. I was pulling in UTF-8 encoded strings to decrypt and they needed to be made into Base64 strings first. Thanks for all of the help provided.
Related
I'm having a bit of an issue encrypting / decrypting cross languages.
Here is my python code to encrypt some text:
class AESCipher:
def __init__( self, key, iv ):
self.key = base64.b64decode(key)
self.iv = base64.b64decode(iv)
def encrypt( self, raw ):
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
raw = pad(raw)
cipher = AES.new( self.key, AES.MODE_CBC, self.iv)
res = self.iv + cipher.encrypt( raw )
return base64.b64encode(res)
def decrypt( self, enc ):
enc = base64.b64decode(enc)
unpad = lambda s : s[:-ord(s[len(s)-1:])]
cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
return unpad(cipher.decrypt( enc[16:] ))
crypt = AESCipher("key", "iv")
print "{0}".format(crypt.encrypt("Hallow"))
And C# Decrypting:
public static string DecryptStringFromBase64(string base64String)
{
byte[] bytes = Decrypt(Convert.FromBase64String(base64String));
var utf8 = Encoding.UTF8.GetString(bytes);
return utf8;
}
public static byte[] Decrypt(byte[] bytes)
{
AesManaged algorithm = new AesManaged();
algorithm.IV = Convert.FromBase64String("IV");
algorithm.Key = Convert.FromBase64String("KEY");
byte[] ret = null;
using (var decryptor = algorithm.CreateDecryptor())
{
using (MemoryStream msDecrypted = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msDecrypted, decryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(bytes, 0, bytes.Length);
}
ret = msDecrypted.ToArray();
}
}
return ret;
}
However the decrypted value always comes out incorrectly like this:
I think this has something to do with the padding, can anybody suggest how to get around this?
The problem is not the padding. It's the IV that you haven't sliced off. The IV doesn't have to be secret, but it should be randomly generated for each encryption. Since you're already including the IV into the ciphertext in Python, you have to use it in C# during decryption (of course the decryption should be done on the actual ciphertext excluding the IV):
C#
public static string DecryptStringFromBase64(string base64String)
{
byte[] bytes = Decrypt(Convert.FromBase64String(base64String));
var utf8 = Encoding.UTF8.GetString(bytes);
return utf8;
}
public static byte[] Decrypt(byte[] bytes)
{
byte[] iv = new byte[16]; // change
Array.copy(bytes, iv, 16); // change
AesManaged algorithm = new AesManaged();
algorithm.IV = iv; // change
algorithm.Key = Convert.FromBase64String("KEY");
byte[] ret = null;
using (var decryptor = algorithm.CreateDecryptor())
{
using (MemoryStream msDecrypted = new MemoryStream())
{
using (CryptoStream csEncrypt = new CryptoStream(msDecrypted, decryptor, CryptoStreamMode.Write))
{
csEncrypt.Write(bytes, 16, bytes.Length - 16); // change
}
ret = msDecrypted.ToArray();
}
}
return ret;
}
The Python code is also not without its issues. You don't need to pass in the IV, because you're putting it into the ciphertext. So you can just create a new random IV. Additionally, the unpad function is not correct (or at least needlessly complicated).
class AESCipher:
def __init__( self, key):
self.key = base64.b64decode(key)
def encrypt( self, raw ):
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
raw = pad(raw)
iv = Random.new().read(BS)
cipher = AES.new(self.key, AES.MODE_CBC, iv)
res = iv + cipher.encrypt( raw )
return base64.b64encode(res)
def decrypt( self, enc ):
enc = base64.b64decode(enc)
BS = 16
iv = enc[:BS]
unpad = lambda s : s[:-ord(s[-1])]
cipher = AES.new(self.key, AES.MODE_CBC, iv )
return unpad(cipher.decrypt( enc[BS:] ))
crypt = AESCipher("key", "iv")
print "{0}".format(crypt.encrypt("Hallow"))
Don't forget to authenticate your ciphertexts. Otherwise, a padding oracle attack might be launched against your system which enables an attack to decrypt every ciphertext with multiple online queries.
You can use an authenticated modes like GCM or EAX, or employ an encrypt-then-MAC scheme with a strong MAC like HMAC-SHA256.
I'm trying to convert cryptopp DiffieHellman 2 key agreement method to c# bouncy castle library.
Here is the help page about cryptoo c++ library: http://www.cryptopp.com/wiki/Diffie-Hellman
I'm trying to implement RFC 5114's 1024-bit MODP group to C#.
But there are a lot of problems I couldn't solve.
When keyPair generates a key, it is 131 bytes, but it must be 128 bytes, because server sending to me 256 bytes key with static and ephemeral key. I must send 256 bytes too .But 1-, 2-, and 3-byte values are static in every key so I'm removing first 3 bytes in keys is it true?
Which secret key must I use for converting shared secret to other encryption system keys?
Example I have a secret key, how can I convert it for Twofish, RC6, xTEA, Serpent etc?
Here is my code:
public byte[] CreateaNewDiffieHellmanKey()
{
public static string Phex = "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371";
public static string Ghex = "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5";
public static string Qhex = "F518AA8781A8DF278ABA4E7D64B7CB9D49462353";
BigInteger P = new BigInteger(Phex, 16);
BigInteger G = new BigInteger(Ghex, 16);
BigInteger Q = new BigInteger(Qhex, 16);
IAsymmetricCipherKeyPairGenerator staticKeyGen = GeneratorUtilities.GetKeyPairGenerator("DH");
IAsymmetricCipherKeyPairGenerator ephemeralKeyGen = GeneratorUtilities.GetKeyPairGenerator("DH");
DHParameters dhParams = new DHParameters(P, G, Q, 0, 160);
DHP = dhParams;
KeyGenerationParameters kgpSt = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
KeyGenerationParameters kgpEp = new DHKeyGenerationParameters(new SecureRandom(), dhParams);
staticKeyGen.Init(kgpSt);
ephemeralKeyGen.Init(kgpEp);
AsymmetricCipherKeyPair staticKeyPayir = staticKeyGen.GenerateKeyPair();
staticKeyEgri = AgreementUtilities.GetBasicAgreement("DH");
staticKeyEgri.Init(staticKeyPayir.Private);
AsymmetricCipherKeyPair ephemeralKeyPair = ephemeralKeyGen.GenerateKeyPair();
ephemeralKeyEgri = AgreementUtilities.GetBasicAgreement("DH");
ephemeralKeyEgri.Init(staticKeyPayir.Private);
AsymmetricKeyParameter StaticPublicKey = staticKeyPayir.Public;
SubjectPublicKeyInfo StaticPublicKeyinfomuz = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(StaticPublicKey);
byte[] st1 = StaticPublicKeyinfomuz.PublicKeyData.GetBytes();
byte[] staticPublic = new byte[128];
Array.Copy(st1, 3, staticPublic, 0, staticPublic.Length);
AsymmetricKeyParameter EphPublicKey = staticKeyPayir.Public;
SubjectPublicKeyInfo EphPublicKeyinfomuz = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(EphPublicKey);
byte[] ep1 = StaticPublicKeyinfomuz.PublicKeyData.GetBytes();
byte[] ephemeralPublic = new byte[128];
Array.Copy(ep1, 3, ephemeralPublic, 0, ephemeralPublic.Length);
return Bot.Birleştir(staticPublic, ephemeralPublic); // Combine 2 byte arrays
}
public bool AgreeTheKey(byte[] buffer)
{
byte[] staticpublic = new byte[128];
byte[] ephemeralpublic = new byte[128];
Array.Copy(buffer, 0, staticpublic, 0, staticpublic.Length);
Array.Copy(buffer, staticpublic.Length, ephemeralpublic, 0, ephemeralpublic.Length);
ICipherParameters istatic = new KeyParameter(staticpublic);
ICipherParameters iphemeral = new KeyParameter(ephemeralpublic);
DHPublicKeyParameters dhkpST = new DHPublicKeyParameters(new BigInteger(staticpublic), DHP);
DHPublicKeyParameters dhkpEP = new DHPublicKeyParameters(new BigInteger(staticpublic), DHP);
SharedStatic = staticKeyEgri.CalculateAgreement(dhkpST).ToByteArray();
SharedEphemeral = ephemeralKeyEgri.CalculateAgreement(dhkpEP).ToByteArray();
byte[] Sharedkey = SharedStatic;
return true;
}
If you don't want to have the added encoding, you may always cast from AsymmetricKeyParameter to DHPublicKeyParameters and retrieve Y using getY(). Of course, after getting the value as an integer, you may still want to encode it to a fixed number of octets (bytes). In general, unsigned, left padded big endian encoding is used for the numbers.
I have a string. That encrypted with java. Now I have to decrypt it in c sharp.
I use RSA algorithm with same public/private keys in c sharp and java.
But in decryption with c sharp I got a "Bad Data" error.
and an important notice is that: I can decrypt this string in java! and I can encrypt with c sharp and decrypt with c sharp !! But when I try to encrypt with one of them(c sharp or java) and decrypt with the other one it raise an error !
this is my c sharp code :
string privateKey = "<RSAKeyValue><Modulus>rFsMn+idg8jmVMk249DzJc7AFft3+/jcnYDTh9wHee3tgFu1gBRh7e+ao+MWq7NEN0N7kUHa7O4c/ND2Ahcx/h4mXD5KDoixFRBUsxYqCJVA68qYJ7vozVPMjNr4jeOo1xt+oevO5+mUWtcaib5Iw51u1Jq/6qCqLsm8Eq3cnsE=</Modulus><Exponent>AQAB</Exponent><P>5aqTyz3GH655H+vO/BzJvFzpILpxLwXZEvqp3l3NYXKqP0tdkY1g36o6M9YodI3h8ho4VFYofIqA1+V0qPkuDQ==</P><Q>wB45uljBS8Z+WGRE1cRdpZb5aKUv76uLlCp6agtPBBhjXNIQrN8MpOCTvZM2cOsuYpYhDtnwsTY9KKXdBmv6hQ==</Q><DP>KB474uzPhwk7L1BJv9BnjPfN4WGwE765xI70zVslXThidNv9ZEeF/IwHzrZCKtn99gSOgmzqdZbcGtqCHmIooQ==</DP><DQ>eLnFrvsdJJ/sP2Fq/CX0bx0rR34eXG6hPI9g84h9s+YbcnWy8t8LVASn8oYyvMydDminB20e2k1qK0PnW6S4/Q==</DQ><InverseQ>rlPpLse8LJ0KRglnu2D1xzJT90QqzS3o80P0WTpUvm95mF5d8YaVj/d9W7FBdgZ98y6Q7of+wLCmYGKLg56wYQ==</InverseQ><D>Gs8mzZDPP3p2aWXLBfCwgYcBVeoBpc318wHg5VcSSqL5uGeLedqxyOLmOOvP0PFXgQkcJWIK/aOkGqcePQECo3TNiK+uLSwc97V3spZah70FFJVyh23Y+o0wlRGHAm5Nj9QieHlVwhgJPkNUJYgH9qkwB9aCpl+rdAG3da2fQ2E=</D></RSAKeyValue>";
string publicKey = "<RSAKeyValue><Modulus>rFsMn+idg8jmVMk249DzJc7AFft3+/jcnYDTh9wHee3tgFu1gBRh7e+ao+MWq7NEN0N7kUHa7O4c/ND2Ahcx/h4mXD5KDoixFRBUsxYqCJVA68qYJ7vozVPMjNr4jeOo1xt+oevO5+mUWtcaib5Iw51u1Jq/6qCqLsm8Eq3cnsE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
public string EncryptData(string data2Encrypt )
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(publicKey);
byte[] plainbytes = System.Text.Encoding.UTF8.GetBytes(data2Encrypt);
byte[] cipherbytes = rsa.Encrypt(plainbytes, false);
return Convert.ToBase64String(cipherbytes);
}
public string DecryptData(string data2Decrypt)
{
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(privateKey);
byte[] plainbytes = rsa.Decrypt(Convert.FromBase64String(data2Decrypt), false);
return System.Text.Encoding.UTF8.GetString(plainbytes);
}
and my java code is :
byte[] expBytes = Base64.decode("AQAB".getBytes("UTF-8"), Base64.DEFAULT);
byte[] modBytes = Base64.decode("rFsMn+idg8jmVMk249DzJc7AFft3+/jcnYDTh9wHee3tgFu1gBRh7e+ao+MWq7NEN0N7kUHa7O4c/ND2Ahcx/h4mXD5KDoixFRBUsxYqCJVA68qYJ7vozVPMjNr4jeOo1xt+oevO5+mUWtcaib5Iw51u1Jq/6qCqLsm8Eq3cnsE=".getBytes("UTF-8"), Base64.DEFAULT);
byte[] dBytes = Base64.decode("Gs8mzZDPP3p2aWXLBfCwgYcBVeoBpc318wHg5VcSSqL5uGeLedqxyOLmOOvP0PFXgQkcJWIK/aOkGqcePQECo3TNiK+uLSwc97V3spZah70FFJVyh23Y+o0wlRGHAm5Nj9QieHlVwhgJPkNUJYgH9qkwB9aCpl+rdAG3da2fQ2E=".getBytes("UTF-8"), Base64.DEFAULT);
BigInteger modules = new BigInteger(1, modBytes);
BigInteger exponent = new BigInteger(1, expBytes);
BigInteger d = new BigInteger(1, dBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
Cipher cipher = Cipher.getInstance("RSA");
String input = "test";
RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modules, exponent);
PublicKey pubKey = factory.generatePublic(pubSpec);
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] encrypted = cipher.doFinal(input.getBytes());
String strEncrypted = Base64.encodeToString(encrypted, Base64.DEFAULT);
I can not decode strEncrypted value in c sharp.
and at the end encryption value in java is different with c sharp(same keys and algorithm and data) !!!
Second parameter of RSACryptoServiceProvider.Encrypt(plainbytes, false) means that PKCS#1 padding is used. true is for OAEP padding.
In Java the security provider you use maps "RSA" to "RSA/ECB/NoPadding", which is just encryption without any padding, hence an encryption result is always the same. So there is a need to specify "RSA/ECB/PKCS1Padding" to match C# behavior.
You should always specify the mode and padding otherwise the security provider will choose it's own defaults - and the defaults may differ for each provider.
I found it :)
I changed this line :
Cipher cipher = Cipher.getInstance("RSA");
to :
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
It worked well. But who knows what's different between these ?
We are using below code to encrypt in Java
public encrypt(String text) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidKeySpecException {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH); //256 bit
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
this.ecipher = Cipher.getInstance("AES");
this.ecipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] bytes = encrypt.getBytes("UTF-8");
byte[] encrypted = this.ecipher.doFinal(bytes);
return Base64.encodeBase64String(encrypted);
}
Our vendor is using C# to decrypt the data
His code
string Decrypt(string textToDecrypt, string key)
{
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Mode = CipherMode.ECB;
rijndaelCipher.KeySize = 0x80;
rijndaelCipher.BlockSize = 0x80;
byte[] encryptedData = Convert.FromBase64String(textToDecrypt);
byte[] pwdBytes = Encoding.UTF8.GetBytes(key);
byte[] keyBytes = new byte[0x10];
int len = pwdBytes.Length;
if (len > keyBytes.Length) {
len = keyBytes.Length;
}
Array.Copy(pwdBytes, keyBytes, len);
rijndaelCipher.Key = keyBytes;
byte[] plainText = rijndaelCipher.CreateDecryptor().TransformFinalBlock(encryptedData, 0, encryptedData.Length);
return Encoding.UTF8.GetString(plainText);
}
But he's unable to decrypt the data. He's getting some garbage data.
Any idea how to decrypt using C# for Java Encryption part.
First off, Don't have any allusions of security with your java code. ECB mode not a good choice.
Second, the problem with the C# code is that it's using the raw bytes of the passphase for the key rather than PBKDF2WithHmacSHA1 which the java code is using. The class in C# to do the key generation is Rfc2898DeriveBytes
I am having difficulty encrypting something in C#.
I have 3 variables.
First one is a 16 digit hex,lets call it X value I.E 0072701351979990
Second one is also a 16 digit hex value, lets call it Y I.E 3008168011FFFFFF
These two values have to be XOR 'ed to get the key for the DES-ECB encryption.
Thus resulting in 307a66934068666f . Now thus is my keyblock for the encryption.
Then i have this as my datablock,which is 64 bits for encryption 0E329232EA6D0D73
Now i have the following code for encryption this.
The result of the encryption should be XOR'ed with the datablock again and
result in a 64bit result. This is not the case.
This is my code for the encryption
$ public static string DESEncrypt(string keyBlock,string dataBlock){
DES desEncrypt = new DESCryptoServiceProvider();
byte[] keyBlockBytes = BitConverter.GetBytes(Convert.ToInt64(keyBlock, 16));
byte[] dataBlockBytes = BitConverter.GetBytes(Convert.ToInt64(dataBlock, 16));
desEncrypt.Mode = CipherMode.ECB;
desEncrypt.Key = keyBlockBytes;
ICryptoTransform transForm = desEncrypt.CreateEncryptor();
MemoryStream enecryptedStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(enecryptedStream, transForm, CryptoStreamMode.Write);
cryptoStream.Write(dataBlockBytes, 0, dataBlockBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] encryptedData = new byte[enecryptedStream.Length];
enecryptedStream.Position = 0;
enecryptedStream.Read(encryptedData, 0, encryptedData.Length);
string enCryptedHex = BitConverter.ToString(encryptedData);
return enCryptedHex.Replace("-","");
}
What am i doing wrong?
UPDATED QUESTION
I have tested the above solution from CodeInChaos.
It does give me back a 64 bit result. But still there is something wrong.
Here is my updated code.
The keyblock value is abababababababab
and the data block value is 215135734068666F.
The resultant 64 bit result should be XOR'ed with the data block again.
The final answer is suppose to be 414945DD33C97C47 but I get
288a08c01a57ed3d.
Why does it not come out right?
Here is the specifications in suppliers documentation for the encryption.
Encryption is DEA in accordance with FIPS 46-3, single DES in ECB mode, using a single 64-
bit DES Key with odd parity.
$ public static string DESEncrypt(string keyBlock,string dataBlock){
DES desEncrypt = new DESCryptoServiceProvider();
byte[] keyBlockBytes = BitConverter.GetBytes(Convert.ToInt64(keyBlock, 16));
byte[] dataBlockBytes = BitConverter.GetBytes(Convert.ToInt64(dataBlock, 16));
desEncrypt.Mode = CipherMode.ECB;
desEncrypt.Key = keyBlockBytes;
desEncrypt.Padding = PaddingMode.None;
ICryptoTransform transForm = desEncrypt.CreateEncryptor();
MemoryStream enecryptedStream = new MemoryStream();
CryptoStream cryptoStream = new CryptoStream(enecryptedStream, transForm, CryptoStreamMode.Write);
cryptoStream.Write(dataBlockBytes, 0, dataBlockBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] encryptedData = enecryptedStream.ToArray();
string enCryptedHex = BitConverter.ToString(encryptedData);
enCryptedHex = enCryptedHex.Replace("-", "");
long iDeaEncrypt = Convert.ToInt64(enCryptedHex, 16);
long iDataBlock = Convert.ToInt64(dataBlock, 16);
long decoderKey = iDeaEncrypt ^ iDataBlock;
string decKeyHex = Convert.ToString(decoderKey, 16);
return decKeyHex;
}
I think you need to set the padding to PaddingMode.None:
desEncrypt.Padding = PaddingMode.None;
But you should really think hard, if DES and ECB is really what you want.
b.t.w.
byte[] encryptedData = new byte[enecryptedStream.Length];
encryptedStream.Position = 0;
encryptedStream.Read(encryptedData, 0, encryptedData.Length);
can be replaced by:
encryptedData = encryptedStream.ToArray();
Perhaps it is necessary to set DES Provider to use the FIPS 46-3 Standard so that the DEA uses the permutation tables etc. specified in FIPS 46-3. Unfortunately I’m also struggling with this same issue.