I want to port this code to C#
byte[] result = new byte[length];
byte[] data = ...;
byte[] key = ...;
HMac hMac = new HMac(new SHA256Digest());
KDFCounterBytesGenerator g = new KDFCounterBytesGenerator(hMac);
g.init(new KDFCounterParameters(key, data, 32));
g.generateBytes(result , 0, result .length);
But I can't find any equivalent classes in C# bouncycastle library.
Any advice?
Edit:
I ported the KDFCounterBytesGenerator from the Java source and it worked fine..
BouncyCastle has an equivalent library for C# that should contain most if not all of the same features as it's Java version with some minor syntax differences:
You should be able to install it via NuGet and then reference it accordingly :
// Using statements (for BouncyCastle)
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Generators;
// Define your byte[]s
byte[] result = new byte[1];
byte[] data = new byte[2];
byte[] key = new byte[3];
// Build your HMac, Generator and generate your bytes
HMac hMac = new HMac(new Sha256Digest());
Kdf1BytesGenerator g = new Kdf1BytesGenerator(hMac.GetUnderlyingDigest());
g.Init(new Org.BouncyCastle.Crypto.Parameters.KdfParameters(key, data));
g.GenerateBytes(result, 0, result.Length);
Related
I have been converting over some code from a Java Android app to C# using Xamarin and I have come across a problem when trying to generate a signature using a certain snippet of BouncyCastle code.
Is there a replacement function in C# for the line of code
"pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) parsed);" ??
This is the Java code:
// Generating the signature
Signature signature = Signature.getInstance("SHA256withECDSA");
Reader rdr = new StringReader("privatekeygoeshere");
Object parsed = new PEMParser(rdr).readObject();
KeyPair pair;
pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair) parsed);
PrivateKey signingKey = pair.getPrivate();
signature.initSign(signingKey);
signature.update(nonceData1);
signature.update(nonceData2);
signature.update(collectorID);
signature.update(publicKeyCompressed);
byte[] signedData = signature.sign();
I have found another way to read the private key and create a KeyPair. However, the private key is stored as a AsymmetricCipherKeyPair which I cannot add into the signature.InitSign() function as this requires an IPrivateKey.
The Different ways that I have tried to create a signature do not allow me to update other byte array data to the signature generation like the Java code, this doesn't work for me so I am really stuck.
I am also open to any ideas of signature generation.
Example of this here:
AsymmetricKeyParameter signingKey;
AsymmetricCipherKeyPair keyPair = null;
using (var textReader = new System.IO.StringReader("privatekeygoeshere"))
{
// Only a private key
Org.BouncyCastle.OpenSsl.PemReader pemReader = new Org.BouncyCastle.OpenSsl.PemReader(textReader);
keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
signingKey = keyPair.Private;
}
I managed to come up with a solution for my problem using a string reader and looping through each array using the Update() command. This works well for me however, if any one can find a better way of doing this... Please comment below.
AsymmetricKeyParameter signingKey;
using (var textReader = new System.IO.StringReader(LONG_TERM_PRIVATE_KEY))
{
// Only a private key
Org.BouncyCastle.OpenSsl.PemReader pemReader = new Org.BouncyCastle.OpenSsl.PemReader(textReader);
keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
signingKey = keyPair.Private;
}
var signer = SignerUtilities.GetSigner("SHA256withECDSA");
signer.Init(true, signingKey);
foreach (byte b in terminalNonce)
{
signer.Update(b);
}
foreach (byte b in mobileDeviceNonce)
{
signer.Update(b);
}
foreach (byte b in COLLECTOR_ID)
{
signer.Update(b);
}
foreach (byte b in terminalEphemeralPublicKeyCompressed)
{
signer.Update(b);
}
var signed = signer.GenerateSignature();
I am having a PHP which is working fine and I want to replicate the same and want to do it using java.
My PHP code uses MCRYPT_RIJNDAEL_256 :
$iv = "42309842389462374623784692423642";
$key = "asfuyjkdhfabcdef";
$text = "0007";
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$padding = $block - (strlen($text) % $block);
$text .= str_repeat(chr($padding), $padding);
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_CBC, $iv);
$crypttext64=base64_encode($crypttext);
echo $crypttext64;
which returns: KdL2lWUGqy+UqLxbe9VTS6OCgvnJFn1jtMCgkj1434A=
But while encrypting using java :
byte[] sessionKey = "asfuyjkdhfabcdef".getBytes(Charset.forName("ASCII"));
byte[] iv = "42309842389462374623784692423642".getBytes(Charset.forName("ASCII"));
String plaintext="0007";
String data = new String(new char[28]).replace("\0", Character.toString ((char) 28));
String newdata = plaintext+data;
newdata = newdata.substring(0,32);
byte [] dataBytes = newdata.getBytes(Charset.forName("ASCII"));
System.out.println(dataBytes);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new RijndaelEngine(256)),new PKCS7Padding());
cipher.init(true, new ParametersWithIV(new KeyParameter(sessionKey), iv));
byte[] encrypted = new byte[cipher.getOutputSize(dataBytes.length)];
int oLen = cipher.processBytes(dataBytes, 0, dataBytes.length, encrypted, 0);
cipher.doFinal(encrypted, oLen);
String s = new String(encrypted);
System.out.println(Base64.getEncoder().encodeToString(s.getBytes()));
I am getting below output:
KdL2lWUGqy+UqLxbe9VTS6OCgvnJFn1jtMCgkj1434DMpLehN7ve0AhnMOQtT1fLcZIUfM9rF/iVOKj6UtgwyA==
Both the outputs are different.
Please suggest the way so that I can get the same output in JAVA also.
You seem to try and perform the padding twice:
String data = new String(new char[28]).replace("\0", Character.toString ((char) 28));
String newdata = plaintext+data;
newdata = newdata.substring(0,32);
byte [] dataBytes = newdata.getBytes(Charset.forName("ASCII"));
System.out.println(dataBytes);
seems to be an ill written attempt to perform the PKCS#7 padding, which you already do in your code (new PKCS7Padding()).
Just replace it by:
plaintext.getBytes(StandardCharsets.US_ASCII)
As you can see, the first part of the base 64 is identical in both PHP and Java. Note that PHP doesn't contain PKCS#7 padding, which is why this was coded using str_repeat.
Furthermore the encrypted array should be directly put into the encodeToString method, first converting to String and then back to byte[] again can only loose data. So strip out s in the following code:
String s = new String(encrypted);
System.out.println(Base64.getEncoder().encodeToString(s.getBytes()));
How do you convert the following Java to PHP?
byte[] byteArray1 = key1.getBytes("UTF8");
byte[] byteArray2 = key2.getBytes("UTF8");
byte[] byteArray3 = key3.getBytes("UTF8");
byte[] byteArray4 = key4.getBytes("UTF8");
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec derivedKey = new SecretKeySpec(byteArray1, "HmacSha1");
mac.init(derivedKey); derivedKey = new SecretKeySpec(mac.doFinal(byteArray2), "HmacSha1"); }
mac.init(derivedKey); derivedKey = new SecretKeySpec(mac.doFinal(byteArray3), "HmacSha1");
mac.init(derivedKey); derivedKey = new SecretKeySpec(mac.doFinal(byteArray4), "HmacSha1");
From all the research I've done, it looks like if we'd remove the last two lines, we could neatly convert it to PHP as follows:
hash_hmac("sha1", $key2, $key1, true);
However, how to you convert this type of reiterative hashing to PHP?
Note: I've tried the following unsuccessfully:
$derivedKey = hash_hmac("sha1", $key2, $key1, true);
$derivedKey = hash_hmac("sha1", $key3, $derivedKey, true);
$derivedKey = hash_hmac("sha1", $key4, $derivedKey, true);
I have an encrypted mp4 using Rijndael and I am decrypting in C# in the following manner.
System.Security.Cryptography.Rijndael crypt = System.Security.Cryptography.Rijndael.Create();
crypt.Key = convertedSecureString;
byte[] initializationVectorLength = new byte[sizeof(int)];
CryptoStream cryptostream = new CryptoStream(inputStream, crypt.CreateDecryptor(), CryptoStreamMode.Read);
byte[] buffer = new byte[1024];
int len;
while ((len = cryptostream.Read(buffer, 0, buffer.Length)) > 0)
{
outputStream.Write(buffer, 0, len);
buffer = new byte[1024];
}
outputStream.Flush();
cryptostream.Close();
inputStream.Close();
Now I need to convert this code to Java/Android equivalent. I am not sure where to start frankly. I am confused by so many options - some say use Bouncy Castle, some say Apache Commons, some the native Java lib. How do I do this. And what do I do about CryptoStream etc?
UPDATE
I am using the following the code in C# for assigning the key
byte[] convertedSecureString = new byte[this.key.Length];
IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(this.key);
for (int i = 0, j = 0; i < this.key.Length * UnicodeByteLength; i = i + UnicodeByteLength, j++)
{
convertedSecureString[j] = System.Runtime.InteropServices.Marshal.ReadByte(ptr, i);
}
try
{
crypt.Key = convertedSecureString;
}
where key is secure. I have the equivalent unsecure key in Java. How do i convert this piece of code to Java
UPDATE
Rfc2898DeriveBytes newKey = new Rfc2898DeriveBytes(crypt.Key.ToString(), crypt.IV);
Array.Copy(newKey.GetBytes((int)crypt.KeySize / 8), crypt.Key, (int)crypt.KeySize / 8);
Array.Copy(newKey.GetBytes((int)crypt.BlockSize / 8), crypt.IV, (int)crypt.BlockSize / 8);
I am using this in C# to mod the key using Rfc 2898 and derive the bytes - I cant find an equivalent in Java - I found here Java equivalent of C#'s Rfc2898DerivedBytes in the second comment - but what values do I give for iterator and dklen?
You need to get Cipher object. Here is one way of getting it, using byte[] aesKey, and byte[] iv (initialization vector, must always be 16 bytes for AES).
// aesKey is 128, 196 or 256-bit key (8, 12 or 16 byte array)
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
// initialization vector
IvParameterSpec ivSpec = new IvParameterSpec(iv);
// create and initialize cipher object
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
Once you have Cipher object in decrypt mode, you can feed it with encrypted data using update methods, and it will return you plain-text data. When you are done, you must call one of doFinal methods to get final block.
Alternatively, you can create CipherInputStream using your Cipher object, and original input stream that supplies encrypted data. You read data from CipherInputStream, which in turn reads data from original input stream, decrypts it, and returns you the plain-text data.
For encrypting, you need to pass Cipher.ENCRYPT_MODE into Cipher.init method, and use CipherOutputStream instead.
Update: full example, more or less equivalent to original C# code:
// Algorithm, mode and padding must match encryption.
// Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
// If you have Bouncycastle library installed, you can use
// Rijndael/CBC/PKCS7PADDING directly.
Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7PADDING");
// convertedSecureString and initVector must be byte[] with correct length
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(convertedSecureString, "AES"),
new IvParameterSpec(initVector));
CipherInputStream cryptoStream = new CipherInputStream(inputStream, cipher);
byte[] buffer = new byte[1024];
int len = cryptoStream.read(buffer, 0, buffer.length);
while (len > 0) {
outputStream.write(buffer, 0, len);
len = cryptoStream.read(buffer, 0, buffer.length);
}
outputStream.flush();
cryptoStream.close();
// no need to close inputStream here, as cryptoStream will close it
By default, Java doesn't support Rijndael algorithm (AES may or may not work, see the other answer) and PKCS7 padding. You will need to install Bouncycastle extension for that and then just use Cipher.getInstance("Rijndael/CBC/PKCS7PADDING");. Why CBC and PKCS7 Padding? Those seem to be defaults for System.Security.Cryptography.Rijndael class. If I got that wrong, use correct mode and padding for your situation.
Don't worry about the implementation in C#. Go through the below link for encrypting/decrypting for Java Language.RjIndeal Implementation in Java
I have some .net code that generates a SHA1 hash on an xml document and need it to match a SHA1 hash generated on the same xml document in java code. The xml doc is sent to the java system and they generate a hash and match against the one i send to verify they are getting the document I intended them to. Below are the snippets in use for this and they both consistently generate the same different hashes. Any ideas why the following code would not generate the same hash?
.NET
String fileName = #"D:\Projects\CHIEP\hasherror\cdadoc.xml";
byte[] buff = null;
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
long numBytes = new FileInfo(fileName).Length;
buff = br.ReadBytes((int)numBytes);
HashAlgorithm hash = new SHA1Managed();
byte[] hashBytes = hash.ComputeHash(buff);
string hex = BitConverter.ToString(hashBytes);
hex = hex.Replace("-", "").ToLower();
Java
public static String SHA1(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest md;
md = MessageDigest.getInstance("SHA-1");
byte[] sha1hash = new byte[40];
md.update(text.getBytes("UTF-8"), 0, text.length());
sha1hash = md.digest();
//String converted = convertToHex(sha1hash);
String converted = getHexString(sha1hash);
return converted;
}
.NET output
587691443778c1da54c3fd04bb35ec68a5a7fecd
Java output:
89665a8268d7d1901aba529dc8c9cea0f910c1bd
The input is a UTF-8 encoded CDA document that gets created here:
XmlSerializer serializer = new XmlSerializer(obj.GetType());
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, new UTF8Encoding(false));
XmlDocument xmlDoc = new XmlDocument();
serializer.Serialize(xmlTextWriter, obj);
memoryStream = (System.IO.MemoryStream)xmlTextWriter.BaseStream;
String xml = UTF8Encoding.UTF8.GetString(memoryStream.ToArray());
UPDATE:
Getting close to a solution. I found in the document there is a character that is being interpreted differently in the java than in the .net code.
Java reads in this:
value="21.9456" unit="kg/m²"
.net reads in this:
value="21.9456" unit="kg/m²"
If I open in the xml editor of my choice it looks like the what .net reads in. I suspect it has something to do with java doing a conversion and .net simply assuming..
I had the following java code:
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] digest = md.digest(password);
and I converted it to C# as follows:
var sha1 = SHA1Managed.Create();
byte[] outputBytes = sha1.ComputeHash(password);
In order to get my hashes to match, I did NOT include the following code that you had in your example:
string hex = BitConverter.ToString(hashBytes);
hex = hex.Replace("-", "").ToLower();