Related
I am trying to decrypt a ciphersaber encrypted hexadecimal message using an IV mixing round of 20 with the key MyKey.
The messages is:
bad85d9e7f5aff959b6b332b44af2cc554d8a6eb
I am doing this in pure C# and it should return the message: Hola Mundo
using System;
using System.Text;
public class Program
{
public static void Main(string[] args)
{
// Hexadecimal text
string hexText = "bad85d9e7f5aff959b6b332b44af2cc554d8a6eb";
// Convert hexadecimal text to byte array
byte[] encryptedData = new byte[hexText.Length / 2];
for (int i = 0; i < encryptedData.Length; i++)
{
encryptedData[i] = Convert.ToByte(hexText.Substring(i * 2, 2), 16);
}
// IV length
int ivLength = 1;
// Key loop iterations
int keyIterations = 20;
// Encryption key
string encryptionKey = "MyKey";
// Convert encryption key to byte array
byte[] keyData = Encoding.UTF8.GetBytes(encryptionKey);
// Create an array to store the IV
byte[] ivData = new byte[ivLength];
// Copy the first `ivLength` bytes of the encrypted data to the IV array
Array.Copy(encryptedData, 0, ivData, 0, ivLength);
// Create an array to store the encrypted message
byte[] messageData = new byte[encryptedData.Length - ivLength];
// Copy the remaining bytes of the encrypted data to the message data array
Array.Copy(encryptedData, ivLength, messageData, 0, messageData.Length);
// Create an array to store the decrypted message
byte[] decryptedData = new byte[messageData.Length];
// Perform the decryption
for (int i = 0; i < messageData.Length; i++)
{
decryptedData[i] = (byte)(messageData[i] ^ keyData[i % keyData.Length]);
for (int j = 0; j < keyIterations; j++)
{
decryptedData[i] = (byte)(decryptedData[i] ^ ivData[j % ivData.Length]);
}
}
// Convert the decrypted data to a string and print it
string decryptedMessage = Encoding.UTF8.GetString(decryptedData);
Console.WriteLine("Decrypted message: " + decryptedMessage);
}
}
Now when I try it returns: �$�#���Jf=�I���
What mistake am I making in the code or am I implementing it wrong?
I tested the text with the following site to see if it was ok: https://ruletheweb.co.uk/cgi-bin/saber.cgi
CipherSaber uses as IV the first 10 bytes of the encrypted message. The rest is the actual ciphertext. The IV is appended to the key (giving the key setup input), which is used as input to the CipherSaber key setup, see CipherSaber, Technical description, 1st section.
In the posted code, an IV length of 1 is applied instead of 10, which incorrectly determines IV (and thus key setup input) and actual ciphertext. The correct determination of IV and actual ciphertext is:
private static (byte[], byte[]) SeparateIvCiphertext(byte[] ivCiphertext)
{
int ivLen = 10;
byte[] iv = new byte[ivLen];
Buffer.BlockCopy(ivCiphertext, 0, iv, 0, iv.Length);
byte[] ciphertext = new byte[ivCiphertext.Length - iv.Length];
Buffer.BlockCopy(ivCiphertext, iv.Length, ciphertext, 0, ciphertext.Length);
return (iv, ciphertext);
}
and of the key setup input:
private static byte[] GetKeySetupInput(byte[] key, byte[] iv)
{
byte[] keySetupInput = new byte[key.Length + iv.Length];
Buffer.BlockCopy(key, 0, keySetupInput, 0, key.Length);
Buffer.BlockCopy(iv, 0, keySetupInput, key.Length, iv.Length);
return keySetupInput;
}
Furthermore, the decryption itself seems to be implemented incorrectly or at least incompletely. CipherSaber uses RC4 as its encryption/decryption algorithm, which can be divided into a key setup and the actual encryption/decryption:
The referenced website performs decryption using CipherSaber-2. Compared to the original CipherSaber (referred to as CipherSaber-1), a modified key setup is used in which the CipherSaber-1/RC4 key setup is repeated multiple times, 20 times in the case of the posted data.
A description of the CipherSaber-1/RC4 key setup can be found here, Key-scheduling algorithm (KSA), a possible implementation for CipherSaber-2 is:
private static byte[] sBox = new byte[256];
private static void KeySetup(byte[] input, int iterations)
{
for (int i = 0; i < 256; i++)
{
sBox[i] = (byte)i;
}
int j = 0;
for (int cs2loop = 0; cs2loop < iterations; cs2loop++) // CipherSaber-2 modification
{
for (int i = 0; i < 256; i++)
{
j = (j + sBox[i] + input[i % input.Length]) % 256;
Swap(ref sBox[i], ref sBox[j]);
}
}
}
private static void Swap(ref byte val1, ref byte val2)
{
if (val1 == val2) return;
val1 = (byte)(val1 ^ val2);
val2 = (byte)(val2 ^ val1);
val1 = (byte)(val1 ^ val2);
}
The loop marked CipherSaber-2 modification in the code snippet is the modification compared to CipherSaber-1/RC4!
The actual encryption/decryption is described here, Pseudo-random generation algorithm (PRGA), a possible implememtation is:
private static byte[] Process(byte[] input)
{
int i = 0, j = 0;
byte[] result = new byte[input.Length];
for (int k = 0; k < input.Length; k++)
{
i = (i + 1) % 256;
j = (j + sBox[i]) % 256;
Swap(ref sBox[i], ref sBox[j]);
result[k] = (byte)(sBox[(sBox[i] + sBox[j]) % 256] ^ input[k]);
}
return result;
}
Note that this algorithm is used for both encryption and decryption.
With this, the posted encrypted message can be decrypted as follows:
using System;
using System.Text;
...
byte[] key = Encoding.UTF8.GetBytes("MyKey");
byte[] encryptedData = Convert.FromHexString("bad85d9e7f5aff959b6b332b44af2cc554d8a6eb");
(byte[] iv, byte[] ciphertext) = SeparateIvCiphertext(encryptedData);
byte[] keySetupInput = GetKeySetupInput(key, iv);
int iterations = 20;
KeySetup(keySetupInput, iterations);
byte[] plaintext = Process(ciphertext);
Console.WriteLine(Encoding.UTF8.GetString(plaintext)); // Hola Mundo
which gives Hola Mundo as plaintext.
I am trying to convert c++ source to c# which encrypt and decrypt file using Rinjdael cryptography.
But c++ source has got a little bit difference from the normal en/decryptions.
And I am not really good at c++, so I am getting confused.
One of my customers' application is written in VC++, and to convert it into c# is part of my job.
And the previous c++ developer used open source code from http://www.codeproject.com/Articles/10657/A-Simple-Portable-Rinjdael-AES-Based-Stream-Cipher to manipulate en/decryption.
Here is c++ source codes.
int DCipher::DecryptFile(LPCTSTR szSrcFile, LPCTSTR szDestFile, const char* pwd, int head[19])
{
if(CheckMemSize() != 0)
return INSUFFICIENT_MEMORY;
FileSize=CurPosition=0;
_tcscpy(SrcFile, szSrcFile);
_tcscpy(OutFile, szDestFile);
//_tcscpy(OutFile, _T(".enc"));
strcpy(password, pwd);
for(int i=0; i<19; i++)
{
header[i] = head[i];
}
FILE *r, *w;
GetFileLength();
int nCheck = CheckIfEncrypted();
if(nCheck != ENCRYPTED_FILE )
return nCheck; //either NORMAL_FILE or BAD_SIGNATURE
if((r = _tfopen(SrcFile, _T("rb"))) == NULL)
return ERROR_SRC_FILE;
if((w = _tfopen(OutFile, _T("wb"))) == NULL)
{
fclose(r);
return ERROR_DST_FILE;
}
char zzz[26]; //fixed invalid pointer - DKeesler
fread(zzz, 25, 1, r); // Skip first 25 bytes of the file.
int pad = header[19];
pad *= 10;
pad += header[20];
// convert password to Rijndael key
strcpy((char*)key, (const char*)CalcMD5FromString((const char*)password));
/***************************************
Decryption algorithm
***************************************/
int rval = NO_ERRORS_DONE;
FileSize -= 25;
unsigned int BUFF_SIZE = liChunkSize;
unsigned int WRITE_SIZE = liChunkSize;
int nRound = FileSize / liChunkSize;
unsigned int LAST_BLOCK = FileSize % liChunkSize;
if(LAST_BLOCK >= 1)
nRound++;
const unsigned char* intext;
unsigned char* output;
intext = (const unsigned char*)malloc(BUFF_SIZE);
output = (unsigned char*)malloc(BUFF_SIZE+16);
if(intext == NULL || output == NULL)
{
fclose(r);
fclose(w);
return ALLOC_ERROR;
}
Rijndael rj;
rj.init(Rijndael::CBC, Rijndael::Decrypt, key, Rijndael::Key32Bytes);
for(int loop=1; loop <= nRound; loop++)
{
if(loop == nRound && LAST_BLOCK >= 1)
{
BUFF_SIZE = LAST_BLOCK;
WRITE_SIZE = LAST_BLOCK - pad;
}
fread((void*)intext, sizeof(char), BUFF_SIZE, r); // read plaintext into intext[] buffer
int bsize = BUFF_SIZE*8;
int len = rj.blockDecrypt((const UINT8*)intext, bsize, (UINT8*)output);
if(len >= 0)
{
fwrite((const void*)output, sizeof(char), WRITE_SIZE, w);
}
else
{
rval = READ_WRITE_ERROR;
break;
}
}
fclose(r); //close input file
fclose(w); //close output file
free((void*)intext);
free((void*)output);
//change these two lines if you want to leave backups or unencrypted copies...
//that would sort of defeat the purpose of encryption in my mind, but it's your
// app so write it like you want it.
if(DECRYPTION_CANCEL == rval) {
_tremove(OutFile);
}
else {
//_tremove(SrcFile); //remove input file
//_trename(OutFile, SrcFile); //rename output file to input filename
}
return rval; //ZERO .. see defines for description of error codes.
}
And c# source code is from https://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael(v=vs.110).aspx.
And I changed a little bit of codes.
Here is c# codes.
public int DecryptFile(string SourceFilePath, string DestFilePath, string Password, string Signature)
{
try
{
FileSize = CurPosition = 0;
FileInfo _fi = new FileInfo(SourceFilePath);
FileSize = _fi.Length;
// copy the signature to _header
for(int i = 0; i < 19; i++)
{
_header[i] = (byte)Signature[i];
}
/*
* check if the file is valid encrypted file.
*/
int nCheck = this.CheckIfEncrypted(SourceFilePath);
switch (nCheck)
{
case ENCRYPTED_FILE:
// The file is an encrypted file.
break;
case NORMAL_FILE:
throw new ArgumentException("The file is a normal file.");
case BAD_SIGNATURE:
throw new ArgumentException("User signature doesn't match.");
}
int pad = _header[19];
pad *= 10;
pad += _header[20];
// Rijndael session key
byte[] session_key = this.CalcMD5FromString(Password);
byte[] _restFileBytes = new byte[_fi.Length - 25];
using (FileStream _fs = new FileStream(SourceFilePath, FileMode.Open, FileAccess.Read))
{
_fs.Read(_restFileBytes, 0, _restFileBytes.Length);
}
int rval = NO_ERRORS_DONE;
FileSize -= 25;
int BUFF_SIZE = liChunkSize;
int WRITE_SIZE = liChunkSize;
int nRound = (int)FileSize / liChunkSize;
int LAST_BLOCK = (int)FileSize % liChunkSize;
if(LAST_BLOCK >= 1)
nRound++;
byte[] intext = new byte[BUFF_SIZE];
byte[] output = new byte[BUFF_SIZE + 16];
if (intext.Length == 0 || output.Length == 0)
{
return ALLOC_ERROR;
}
for (int loop = 1; loop <= nRound; loop++)
{
if (loop == nRound && LAST_BLOCK >= 1)
{
BUFF_SIZE = LAST_BLOCK;
WRITE_SIZE = LAST_BLOCK - pad;
}
intext = new byte[BUFF_SIZE];
System.Buffer.BlockCopy(_restFileBytes, (loop - 1) * this.liChunkSize, intext, 0, BUFF_SIZE);
int bsize = BUFF_SIZE * 8; // -> I still couldn't figure out what this bsize does on Rijndael decryption.
using (RijndaelManaged myRijndael = new RijndaelManaged())
{
myRijndael.Key = session_key;
//myRijndael.BlockSize = bsize;
//myRijndael.Padding = PaddingMode.None;
myRijndael.GenerateIV();
using (Rijndael rijAlg = Rijndael.Create())
{
rijAlg.Key = myRijndael.Key;
rijAlg.IV = myRijndael.IV;
// Create a decrytor to perform the stream transform.
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(intext))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
//using (StreamReader srDecrypt = new StreamReader(csDecrypt))
//{
// // Read the decrypted bytes from the decrypting stream and place them in a string.
// //string s = srDecrypt.ReadToEnd();
//}
byte[] rettt = msDecrypt.ToArray();
} // --> Padding is invalid and cannot be removed error occurs here and msDecrypt byte array is just same as intext. So, it's not decrypted at all.
}
}
}
}
return rval;
}
catch
{
throw;
}
}
According to Keesler(who is the writer of c++ source codes from codeproject.com), first 25 bytes are filled with user data(signature, padding and file status). So, I skipped first 25 bytes and save the rest bytes to _restFileBytes varialbes(byte array).
And Keesler has a variable called chunk size, which splits file bytes into chunk size(as long as I understand).
Anyway, I think I almost converted to c# but I still get this error message "Padding is invalid and cannot be removed" when CryptoStream disposing in c#.
Can anyone give me some guide to fix this error?
None should be used as padding mode. It seems like your colleague and the author of the original article made up their own padding scheme.
Furthermore, all of the ciphertext should be streamed from the file (making sure you read all the bytes). Currently you are restarting encryption with the IV for each chunk, which is not good, the IV should only be used at the start of the ciphertext.
Print out the key in hex for both C++ and C# and compare before you start.
Note that the Read method differs slightly from the fread method in C++.
Hi I am using code from this link, Can you let me know why signature is verify is not working?
Java signer is using BouncyCastleProvider with SHA1withRSA, here is dotnet verify code....
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string pubkey = #"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMf54mcK3EYJn9tT9BhRoTX+8AkqojIyeSfog9ncYEye0VXyBULGg2lAQsDRt8lZsvPioORZW7eB6IKawshoWUsCAwEAAQ==";
String signature = "770bb2610bf6b2602ce2b3ad8489054f4ed59c9b0c9299327f76ecbc60a8bb9a725cfae901fc189d4bafcf73a2f4aed8dffe9842f7b6196ddfcd040c7271c7ca";
String signData = "C2:AE:D6:2B:DF:A4";
byte[] expectedSig = System.Convert.FromBase64String(signature);
byte[] baKey = System.Convert.FromBase64String(pubkey);
byte[] data = Encoding.UTF8.GetBytes(signData);
//Console.WriteLine(p.VerifyData(data, new SHA1CryptoServiceProvider(), expectedSig));
/* Init alg */
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
/* Populate key */
signer.Init(false, DecodeX509PublicKey2(baKey));
/* Calculate the signature and see if it matches */
signer.BlockUpdate(data, 0, data.Length);
Console.WriteLine(signer.VerifySignature(expectedSig));
Console.In.ReadLine();
}
public static RsaKeyParameters DecodeX509PublicKey2(byte[] x509key)
{
byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509key);
BinaryReader reader = new BinaryReader(ms);
if (reader.ReadByte() == 0x30)
ReadASNLength(reader); //skip the size
else
return null;
int identifierSize = 0; //total length of Object Identifier section
if (reader.ReadByte() == 0x30)
identifierSize = ReadASNLength(reader);
else
return null;
if (reader.ReadByte() == 0x06) //is the next element an object identifier?
{
int oidLength = ReadASNLength(reader);
byte[] oidBytes = new byte[oidLength];
reader.Read(oidBytes, 0, oidBytes.Length);
if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1?
return null;
int remainingBytes = identifierSize - 2 - oidBytes.Length;
reader.ReadBytes(remainingBytes);
}
if (reader.ReadByte() == 0x03) //is the next element a bit string?
{
ReadASNLength(reader); //skip the size
reader.ReadByte(); //skip unused bits indicator
if (reader.ReadByte() == 0x30)
{
ReadASNLength(reader); //skip the size
if (reader.ReadByte() == 0x02) //is it an integer?
{
int modulusSize = ReadASNLength(reader);
byte[] modulus = new byte[modulusSize];
reader.Read(modulus, 0, modulus.Length);
if (modulus[0] == 0x00) //strip off the first byte if it's 0
{
byte[] tempModulus = new byte[modulus.Length - 1];
Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
modulus = tempModulus;
}
Array.Reverse(modulus); //convert to big-endian
if (reader.ReadByte() == 0x02) //is it an integer?
{
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
Array.Reverse(exponent); //convert to big-endian
//RSAParameters RSAKeyInfo = new RSAParameters();
//RSAKeyInfo.Modulus = modulus;
//RSAKeyInfo.Exponent = exponent;
return MakeKey(BitConverter.ToString(modulus).Replace("-", string.Empty), BitConverter.ToString(exponent).Replace("-", string.Empty), false);
}
}
}
}
return null;
}
public static RsaKeyParameters MakeKey(String modulusHexString, String exponentHexString, bool isPrivateKey)
{
var modulus = new Org.BouncyCastle.Math.BigInteger(modulusHexString, 16);
var exponent = new Org.BouncyCastle.Math.BigInteger(exponentHexString, 16);
return new RsaKeyParameters(isPrivateKey, modulus, exponent);
}
public static RSACryptoServiceProvider DecodeX509PublicKey(byte[] x509key)
{
byte[] SeqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509key);
BinaryReader reader = new BinaryReader(ms);
if (reader.ReadByte() == 0x30)
ReadASNLength(reader); //skip the size
else
return null;
int identifierSize = 0; //total length of Object Identifier section
if (reader.ReadByte() == 0x30)
identifierSize = ReadASNLength(reader);
else
return null;
if (reader.ReadByte() == 0x06) //is the next element an object identifier?
{
int oidLength = ReadASNLength(reader);
byte[] oidBytes = new byte[oidLength];
reader.Read(oidBytes, 0, oidBytes.Length);
if (oidBytes.SequenceEqual(SeqOID) == false) //is the object identifier rsaEncryption PKCS#1?
return null;
int remainingBytes = identifierSize - 2 - oidBytes.Length;
reader.ReadBytes(remainingBytes);
}
if (reader.ReadByte() == 0x03) //is the next element a bit string?
{
ReadASNLength(reader); //skip the size
reader.ReadByte(); //skip unused bits indicator
if (reader.ReadByte() == 0x30)
{
ReadASNLength(reader); //skip the size
if (reader.ReadByte() == 0x02) //is it an integer?
{
int modulusSize = ReadASNLength(reader);
byte[] modulus = new byte[modulusSize];
reader.Read(modulus, 0, modulus.Length);
if (modulus[0] == 0x00) //strip off the first byte if it's 0
{
byte[] tempModulus = new byte[modulus.Length - 1];
Array.Copy(modulus, 1, tempModulus, 0, modulus.Length - 1);
modulus = tempModulus;
}
Array.Reverse(modulus); //convert to big-endian
if (reader.ReadByte() == 0x02) //is it an integer?
{
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
Array.Reverse(exponent); //convert to big-endian
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
RSA.ImportParameters(RSAKeyInfo);
return RSA;
}
}
}
}
return null;
}
public static int ReadASNLength(BinaryReader reader)
{
//Note: this method only reads lengths up to 4 bytes long as
//this is satisfactory for the majority of situations.
int length = reader.ReadByte();
if ((length & 0x00000080) == 0x00000080) //is the length greater than 1 byte
{
int count = length & 0x0000000f;
byte[] lengthBytes = new byte[4];
reader.Read(lengthBytes, 4 - count, count);
Array.Reverse(lengthBytes); //
length = BitConverter.ToInt32(lengthBytes, 0);
}
return length;
}
}
}
Java code used to sign signature data:
private static final java.security.Signature signer;
static final String transformation = "RSA/ECB/PKCS1Padding";
static {
try {
signer = java.security.Signature.getInstance("SHA1withRSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
static String sign(String clearText) {
String signed = null;
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] data = clearText.getBytes("UTF-8");
signer.initSign(getPrivateKey());
signer.update(data);
byte[] digitalSignature = signer.sign();
//--toHex
signed = org.apache.commons.codec.binary.Hex.encodeHexString(digitalSignature);
} catch (Exception e) {
e.printStackTrace();
}
return signed;
}
KeyPair generateKeyPair() {
KeyPair kp = null;
// Generate a key-pair
KeyPairGenerator kpg;
SecureRandom secureRandom;
try {
kpg = KeyPairGenerator.getInstance("RSA");
secureRandom = SecureRandom.getInstance("SHA1PRNG", "SUN");
secureRandom.setSeed(secureRandomSeed);
kpg.initialize(512, secureRandom);
kp = kpg.generateKeyPair();
} catch (Exception e) {
e.printStackTrace();
}
return kp;
}
Here is code in C# that signs and verifies:
static void test3()
{
AsymmetricCipherKeyPair keys = generateNewKeys();
/* Init alg */
ISigner sig = SignerUtilities.GetSigner("SHA1withRSA");
/* Populate key */
sig.Init(true, keys.Private);
/* Get the bytes to be signed from the string */
var bytes = Encoding.UTF8.GetBytes(signData);
/* Calc the signature */
sig.BlockUpdate(bytes, 0, bytes.Length);
byte[] signature = sig.GenerateSignature();
/* Base 64 encode the sig so its 8-bit clean */
var signedString = Convert.ToBase64String(signature);
Console.WriteLine(signedString);
string expectedSignature = signedString;
/* Init alg */
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
/* Populate key */
signer.Init(false, keys.Public);
/* Get the signature into bytes */
var expectedSig = Convert.FromBase64String(expectedSignature);
/* Get the bytes to be signed from the string */
var msgBytes = Encoding.UTF8.GetBytes(signData);
/* Calculate the signature and see if it matches */
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
/*Verify*/
bool result= signer.VerifySignature(expectedSig);
Console.WriteLine(result);
}
There's a couple problems problems here.
String signature = "770bb ... 1c7ca";
...
byte[] expectedSig = System.Convert.FromBase64String(signature);
You're Base64 decoding the signature, but it's not Base64 encoded, it's Hex encoded.
The second problem is in the DecodeX509PublicKey methods (which admittedly is my mistake because I provided this code in another answer.) The specific problem lines are
Array.Reverse(modulus); //convert to big-endian
and
Array.Reverse(exponent); //convert to big-endian
I repeatedly read that the ASN.1 and the .Net API use opposite endieness for their keys, and so I was under the impression that the endieness needed to be reversed to account for this. (I really should have done a test like your signature verification to be sure, rather than just looking at the key values in memory >.<) Regardless, remove these lines, fix the encoding problem, and your signature will verify properly (successfully tested using your sample data as well as my own).
Also, this line in your sign method isn't quite right:
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
By the time you get to that point in the code, the signer object has already been instantiated using the default provider. Also, you don't need to be adding the Bouncy Castle provider each time you want to sign some data, it will only actually add the provider the first time you make this call and will ignore it for all subsequent calls.
Further, the signer object is declaired static, but your usage of it is not thread safe.
What you more likely want to do is add the provider in the static block and then instantiate the signer explicitly using the Bouncy Castle provider. If you don't explicitly specify Bouncy Castle as the provider (or add Bouncy Castle as the highest priority using insertProviderAt), the default provider will be used instead.
static {
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
} catch (Exception e) {
e.printStackTrace();
}
}
...
String signed = null;
try {
java.security.Signature signer = java.security.Signature.getInstance("SHA1withRSA", "BC");
byte[] data = clearText.getBytes("UTF-8");
signer.initSign(getPrivateKey());
...
I have to write a Vigenere encryption / decryption function that operates on full bytes (to encrypt and send files over tcp and then decrypt on the other side).
My encrypting function seems to be working (more or less, can't really test it without decrypting function).
This is the code of the encrypting function:
public static Byte[] encryptByteVigenere(Byte[] plaintext, string key)
{
Byte[] result= new Byte[plaintext.Length];
key = key.Trim().ToUpper();
int keyIndex = 0;
int keylength = key.Length;
for (int i = 0; i < plaintext.Length; i++)
{
keyIndex = keyIndex % keylength;
int shift = (int)key[keyIndex] - 65;
result[i] = (byte)(((int)plaintext[i] + shift) % 256);
keyIndex++;
}
return result;
}
However, the decrypting function, even though wrote in pretty much the same way, causes an error.
"Attempted to divide by zero."
The code of the decrypting function:
public static Byte[] decryptByteVigenere(Byte[] ciphertext, string key)
{
Byte[] result = new Byte[ciphertext.Length];
key = key.Trim().ToUpper();
int keyIndex = 0;
int keylength = key.Length;
for (int i = 0; i < ciphertext.Length; i++)
{
keyIndex = keyIndex % keylength;
int shift = (int)key[keyIndex] - 65;
result[i]= (byte)(((int)ciphertext[i] + 256 - shift) % 256);
keyIndex++;
}
return result;
}
The error points at the line
keyIndex = keyIndex % keylength;
But what wonders me is that the code is pretty much the same in the first function and it doesn't seem to cause any trouble. I'm testing it on the received fild, which arrives correctly without encryption. Could anyone help me with that?
EDIT:
The method / thread that is using the decryption function code:
public void fileListenThread()
{
try
{
fileServer.Start();
String receivedFileName = "test.dat";
String key = (textKlucz.Text).ToUpper();
while (true)
{
fileClient = fileServer.AcceptTcpClient();
NetworkStream streamFileServer = fileClient.GetStream();
int thisRead = 0;
int blockSize = 1024;
Byte[] dataByte = new Byte[blockSize];
Byte[] dataByteDecrypted = new Byte[blockSize];
FileStream fileStream = new FileStream(receivedFileName, FileMode.Create);
while (true)
{
thisRead = streamFileServer.Read(dataByte, 0, blockSize);
dataByteDecrypted = Program.decryptByteVigenere(dataByte, key);
fileStream.Write(dataByteDecrypted, 0, thisRead);
if (thisRead == 0)
break;
}
fileStream.Close();
}
}
catch (SocketException e)
{
MessageBox.Show("SocketException: " + e, "Wystąpił wyjątek", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
Ok the problem was indeed the sending / receiving method, not the function itself. I still don't really know what caused the problem, but rewriting the functions helped. Thanks for your input!
I'm leaving it here in case someone needed such function in the future... even though it's rather trivial thing.
Cheers.
Guys, I'm trying to implement a PBKDF2 function in C# that creates a WPA Shared key. I've found some here: http://msdn.microsoft.com/en-us/magazine/cc163913.aspx that seems to produce a valid result, but it's one byte too short... and the wrong PSK value.
To test the output, I am comparing it to this: http://www.xs4all.nl/~rjoris/wpapsk.html or http://anandam.name/pbkdf2/
I did find one way of getting this to work with a built in library to C# called Rfc2898DeriveBytes. Using this, I get a valid output using:
Rfc2898DeriveBytes k3 = new Rfc2898DeriveBytes(pwd1, salt1, 4096);
byte[] answers = k3.GetBytes(32);
Now, the one limitation I have using Rfc2898DeriveBytes is the "salt" must be 8 octets long. If it is shorter, the Rfc2898DeriveBytes throws an exception. I was thinking all I had to do was pad the salt (if it was shorter) to 8 bytes, and I'd be good. But NO! I've tried pretty much every combination of padding with a shorter salt, but I cannot duplicate the results I get from those two websites above.
So bottom line is, does this mean the Rfc2898DeriveBytes just simply won't work with a source salt shorter than 8 bytes? If so, does anyone know of any C# code I could use that implements PBKDF2 for WPA Preshared key?
Here is an implementation that does not require the 8 byte salt.
You can calculate a WPA key as follows:
Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(passphrase, Encoding.UTF8.GetBytes(name), 4096);
key = rfc2898.GetBytes(32);
public class Rfc2898DeriveBytes : DeriveBytes
{
const int BlockSize = 20;
uint block;
byte[] buffer;
int endIndex;
readonly HMACSHA1 hmacsha1;
uint iterations;
byte[] salt;
int startIndex;
public Rfc2898DeriveBytes(string password, int saltSize)
: this(password, saltSize, 1000)
{
}
public Rfc2898DeriveBytes(string password, byte[] salt)
: this(password, salt, 1000)
{
}
public Rfc2898DeriveBytes(string password, int saltSize, int iterations)
{
if (saltSize < 0)
{
throw new ArgumentOutOfRangeException("saltSize");
}
byte[] data = new byte[saltSize];
new RNGCryptoServiceProvider().GetBytes(data);
Salt = data;
IterationCount = iterations;
hmacsha1 = new HMACSHA1(new UTF8Encoding(false).GetBytes(password));
Initialize();
}
public Rfc2898DeriveBytes(string password, byte[] salt, int iterations) : this(new UTF8Encoding(false).GetBytes(password), salt, iterations)
{
}
public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations)
{
Salt = salt;
IterationCount = iterations;
hmacsha1 = new HMACSHA1(password);
Initialize();
}
static byte[] Int(uint i)
{
byte[] bytes = BitConverter.GetBytes(i);
byte[] buffer2 = new byte[] {bytes[3], bytes[2], bytes[1], bytes[0]};
if (!BitConverter.IsLittleEndian)
{
return bytes;
}
return buffer2;
}
byte[] DeriveKey()
{
byte[] inputBuffer = Int(block);
hmacsha1.TransformBlock(salt, 0, salt.Length, salt, 0);
hmacsha1.TransformFinalBlock(inputBuffer, 0, inputBuffer.Length);
byte[] hash = hmacsha1.Hash;
hmacsha1.Initialize();
byte[] buffer3 = hash;
for (int i = 2; i <= iterations; i++)
{
hash = hmacsha1.ComputeHash(hash);
for (int j = 0; j < BlockSize; j++)
{
buffer3[j] = (byte) (buffer3[j] ^ hash[j]);
}
}
block++;
return buffer3;
}
public override byte[] GetBytes(int bytesToGet)
{
if (bytesToGet <= 0)
{
throw new ArgumentOutOfRangeException("bytesToGet");
}
byte[] dst = new byte[bytesToGet];
int dstOffset = 0;
int count = endIndex - startIndex;
if (count > 0)
{
if (bytesToGet < count)
{
Buffer.BlockCopy(buffer, startIndex, dst, 0, bytesToGet);
startIndex += bytesToGet;
return dst;
}
Buffer.BlockCopy(buffer, startIndex, dst, 0, count);
startIndex = endIndex = 0;
dstOffset += count;
}
while (dstOffset < bytesToGet)
{
byte[] src = DeriveKey();
int num3 = bytesToGet - dstOffset;
if (num3 > BlockSize)
{
Buffer.BlockCopy(src, 0, dst, dstOffset, BlockSize);
dstOffset += BlockSize;
}
else
{
Buffer.BlockCopy(src, 0, dst, dstOffset, num3);
dstOffset += num3;
Buffer.BlockCopy(src, num3, buffer, startIndex, BlockSize - num3);
endIndex += BlockSize - num3;
return dst;
}
}
return dst;
}
void Initialize()
{
if (buffer != null)
{
Array.Clear(buffer, 0, buffer.Length);
}
buffer = new byte[BlockSize];
block = 1;
startIndex = endIndex = 0;
}
public override void Reset()
{
Initialize();
}
public int IterationCount
{
get
{
return (int) iterations;
}
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException("value");
}
iterations = (uint) value;
Initialize();
}
}
public byte[] Salt
{
get
{
return (byte[]) salt.Clone();
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
salt = (byte[]) value.Clone();
Initialize();
}
}
}
I get matching results when comparing key-derivation from .NET's Rfc2898DeriveBytes and Anandam's PBKDF2 Javascript implementation.
I put together an example of packaging SlowAES and Anandam's PBKDF2 into Windows Script Components. Using this implementation shows good interop with the .NET RijndaelManaged class and the Rfc2898DeriveBytes class.
See also:
AES in Javascript
Getting SlowAES and RijndaelManaged to play together
All of these go further than what you are asking for. They all show interop of the AES encryption. But to get interop on encryption, it is a necessary pre-requisite to have interop (or matching outputs) on the password-based key derivation.
Looking at the Microsoft link, I made some changes in order to make the PMK the same as those discovered in the links you put forward.
Change the SHA algorithm from SHA256Managed to SHA1Managed for the inner and outer hash.
Change HASH_SIZE_IN_BYTES to equal 20 rather than 34.
This produces the correct WPA key.
I know it's a bit late coming, but I've only just started looking for this sort of informatin and thought I could help others out. If anyone does read this post, any ideas on the PRF function and how to do it within C#?
This expands on Dodgyrabbit's answer and his code helped to fix mine as I developed this. This generic class can use any HMAC-derived class in C#. This is .NET 4 because of the parameters with default values, but if those were changed then this should work down to .NET 2, but I haven't tested that. USE AT YOUR OWN RISK.
I have also posted this on my blog, The Albequerque Left Turn, today.
using System;
using System.Text;
using System.Security.Cryptography;
namespace System.Security.Cryptography
{
//Generic PBKDF2 Class that can use any HMAC algorithm derived from the
// System.Security.Cryptography.HMAC abstract class
// PER SPEC RFC2898 with help from user Dodgyrabbit on StackExchange
// http://stackoverflow.com/questions/1046599/pbkdf2-implementation-in-c-sharp-with-rfc2898derivebytes
// the use of default values for parameters in the functions puts this at .NET 4
// if you remove those defaults and create the required constructors, you should be able to drop to .NET 2
// USE AT YOUR OWN RISK! I HAVE TESTED THIS AGAINST PUBLIC TEST VECTORS, BUT YOU SHOULD
// HAVE YOUR CODE PEER-REVIEWED AND SHOULD FOLLOW BEST PRACTICES WHEN USING CRYPTO-ANYTHING!
// NO WARRANTY IMPLIED OR EXPRESSED, YOU ARE ON YOUR OWN!
// PUBLIC DOMAIN! NO COPYRIGHT INTENDED OR RESERVED!
//constrain T to be any class that derives from HMAC, and that exposes a new() constructor
public class PBKDF2<T>: DeriveBytes where T : HMAC, new()
{
//Internal variables and public properties
private int _blockSize = -1; // the byte width of the output of the HMAC algorithm
byte[] _P = null;
int _C = 0;
private T _hmac;
byte[] _S = null;
// if you called the initializer/constructor specifying a salt size,
// you will need this property to GET the salt after it was created from the crypto rng!
// GET THIS BEFORE CALLING GETBYTES()! OBJECT WILL BE RESET AFTER GETBYTES() AND
// SALT WILL BE LOST!!
public byte[] Salt { get { return (byte[])_S.Clone(); } }
// Constructors
public PBKDF2(string Password, byte[] Salt, int IterationCount = 1000)
{ Initialize(Password, Salt, IterationCount); }
public PBKDF2(byte[] Password, byte[] Salt, int IterationCount = 1000)
{ Initialize(Password, Salt, IterationCount); }
public PBKDF2(string Password, int SizeOfSaltInBytes, int IterationCount = 1000)
{ Initialize(Password, SizeOfSaltInBytes, IterationCount);}
public PBKDF2(byte[] Password, int SizeOfSaltInBytes, int IterationCount = 1000)
{ Initialize(Password, SizeOfSaltInBytes, IterationCount);}
//All Construtors call the corresponding Initialize methods
public void Initialize(string Password, byte[] Salt, int IterationCount = 1000)
{
if (string.IsNullOrWhiteSpace(Password))
throw new ArgumentException("Password must contain meaningful characters and not be null.", "Password");
if (IterationCount < 1)
throw new ArgumentOutOfRangeException("IterationCount");
Initialize(new UTF8Encoding(false).GetBytes(Password), Salt, IterationCount);
}
public void Initialize(byte[] Password, byte[] Salt, int IterationCount = 1000)
{
//all Constructors/Initializers eventually lead to this one which does all the "important" work
if (Password == null || Password.Length == 0)
throw new ArgumentException("Password cannot be null or empty.", "Password");
if (Salt == null)
Salt = new byte[0];
if (IterationCount < 1)
throw new ArgumentOutOfRangeException("IterationCount");
_P = (byte[])Password.Clone();
_S = (byte[])Salt.Clone();
_C = IterationCount;
//determine _blockSize
_hmac = new T();
_hmac.Key = new byte[] { 0 };
byte[] test = _hmac.ComputeHash(new byte[] { 0 });
_blockSize = test.Length;
}
public void Initialize(string Password, int SizeOfSaltInBytes, int IterationCount = 1000)
{
if (string.IsNullOrWhiteSpace(Password))
throw new ArgumentException("Password must contain meaningful characters and not be null.", "Password");
if (IterationCount < 1)
throw new ArgumentOutOfRangeException("IterationCount");
Initialize(new UTF8Encoding(false).GetBytes(Password), SizeOfSaltInBytes, IterationCount);
}
public void Initialize(byte[] Password, int SizeOfSaltInBytes, int IterationCount = 1000)
{
if (Password == null || Password.Length == 0)
throw new ArgumentException("Password cannot be null or empty.", "Password");
if (SizeOfSaltInBytes < 0)
throw new ArgumentOutOfRangeException("SizeOfSaltInBytes");
if (IterationCount < 1)
throw new ArgumentOutOfRangeException("IterationCount");
// You didn't specify a salt, so I'm going to create one for you of the specific byte length
byte[] data = new byte[SizeOfSaltInBytes];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetBytes(data);
// and then finish initializing...
// Get the salt from the Salt parameter BEFORE calling GetBytes()!!!!!!!!!!!
Initialize(Password, data, IterationCount);
}
~PBKDF2()
{
//*DOOT* clean up in aisle 5! *KEKERKCRACKLE*
this.Reset();
}
// required by the Derive Bytes class/interface
// this is where you request your output bytes after Initialize
// state of class Reset after use!
public override byte[] GetBytes(int ByteCount)
{
if (_S == null || _P == null)
throw new InvalidOperationException("Object not Initialized!");
if (ByteCount < 1)// || ByteCount > uint.MaxValue * blockSize)
throw new ArgumentOutOfRangeException("ByteCount");
int totalBlocks = (int)Math.Ceiling((decimal)ByteCount / _blockSize);
int partialBlock = (int)(ByteCount % _blockSize);
byte[] result = new byte[ByteCount];
byte[] buffer = null;
// I'm using TT here instead of T from the spec because I don't want to confuse it with
// the generic object T
for (int TT = 1; TT <= totalBlocks; TT++)
{
// run the F function with the _C number of iterations for block number TT
buffer = _F((uint)TT);
//IF we're not at the last block requested
//OR the last block requested is whole (not partial)
// then take everything from the result of F for this block number TT
//ELSE only take the needed bytes from F
if (TT != totalBlocks || (TT == totalBlocks && partialBlock == 0))
Buffer.BlockCopy(buffer, 0, result, _blockSize * (TT - 1), _blockSize);
else
Buffer.BlockCopy(buffer, 0, result, _blockSize * (TT - 1), partialBlock);
}
this.Reset(); // force cleanup after every use! Cannot be reused!
return result;
}
// required by the Derive Bytes class/interface
public override void Reset()
{
_C = 0;
_P.Initialize(); // the compiler might optimize this line out! :(
_P = null;
_S.Initialize(); // the compiler might optimize this line out! :(
_S = null;
if (_hmac != null)
_hmac.Clear();
_blockSize = -1;
}
// the core function of the PBKDF which does all the iterations
// per the spec section 5.2 step 3
private byte[] _F(uint I)
{
//NOTE: SPEC IS MISLEADING!!!
//THE HMAC FUNCTIONS ARE KEYED BY THE PASSWORD! NEVER THE SALT!
byte[] bufferU = null;
byte[] bufferOut = null;
byte[] _int = PBKDF2<T>.IntToBytes(I);
_hmac = new T();
_hmac.Key = (_P); // KEY BY THE PASSWORD!
_hmac.TransformBlock(_S, 0, _S.Length, _S, 0);
_hmac.TransformFinalBlock(_int, 0, _int.Length);
bufferU = _hmac.Hash;
bufferOut = (byte[])bufferU.Clone();
for (int c = 1; c < _C; c++)
{
_hmac.Initialize();
_hmac.Key = _P; // KEY BY THE PASSWORD!
bufferU = _hmac.ComputeHash(bufferU);
_Xor(ref bufferOut, bufferU);
}
return bufferOut;
}
// XOR one array of bytes into another (which is passed by reference)
// this is the equiv of data ^= newData;
private void _Xor(ref byte[] data, byte[] newData)
{
for (int i = data.GetLowerBound(0); i <= data.GetUpperBound(0); i++)
data[i] ^= newData[i];
}
// convert an unsigned int into an array of bytes BIG ENDIEN
// per the spec section 5.2 step 3
static internal byte[] IntToBytes(uint i)
{
byte[] bytes = BitConverter.GetBytes(i);
if (!BitConverter.IsLittleEndian)
{
return bytes;
}
else
{
Array.Reverse(bytes);
return bytes;
}
}
}
}