Verify DSA Signature generated by Java in C# - c#

I know this is a topic with lots of existing questions but I am failing to find any existing answers which cover my exact case.
I need to sign a string (a URL) in some Java code and deliver the string along with the signature to a C# program.
I run the following Java code once to generate a DSA keypair:
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);
KeyPair pair = keyGen.generateKeyPair();
PrivateKey priv = pair.getPrivate();
PublicKey pub = pair.getPublic();
/* save the private key in a file */
byte[] privkey = priv.getEncoded();
FileOutputStream privkeyfos = new FileOutputStream("key.priv");
privkeyfos.write(privkey);
privkeyfos.close();
/* save the public key in a file */
byte[] pubkey = pub.getEncoded();
FileOutputStream pubkeyfos = new FileOutputStream("key.public");
pubkeyfos.write(pubkey);
pubkeyfos.close();
I am then using the following code to generate the signature.
public static String Sign(String keyPath, byte[] data)
{
FileInputStream keyfis = new FileInputStream(new File(keyPath, "key.priv"));
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PrivateKey privKey = keyFactory.generatePrivate(privKeySpec);
Signature dsa = Signature.getInstance("SHA1withDSA", "SUN");
dsa.initSign(privKey);
ByteArrayInputStream in = new ByteArrayInputStream(data);
BufferedInputStream bufin = new BufferedInputStream(in);
byte[] buffer = new byte[1024];
int len;
while ((len = bufin.read(buffer)) >= 0)
{
dsa.update(buffer, 0, len);
}
bufin.close();
byte[] realSig = dsa.sign();
return new String(Base64.encodeBase64(realSig), "UTF-8");
}
In my C# code I have access to the string, the Base64 encoded signature and the "key.public" file from the first step.
Can anyone provide a block of code which combines these elements along with a suitable library to determine whether the string has been tampered with?

I have now solved this with some key input coming from this article: http://www.codeproject.com/KB/security/CryptoInteropSign.aspx
The main verification is done using the following C# function.
private static Boolean isValid(String xiString, String xiSig)
{
AsnKeyParser keyParser = new AsnKeyParser("path/to/key.public");
DSAParameters publicKey = keyParser.ParseDSAPublicKey();
DSACryptoServiceProvider DSA = new DSACryptoServiceProvider();
DSA.ImportParameters(publicKey);
DSASignatureDeformatter DSADeformatter = new DSASignatureDeformatter(DSA);
UTF8Encoding UTF8 = new UTF8Encoding();
byte[] plainBytes = UTF8.GetBytes(xiString);
var sha1 = new SHA1Managed();
var hash = sha1.ComputeHash(plainBytes);
byte[] asn1SigBytes = Convert.FromBase64String(xiSig);
byte[] sigBytes = ConvertToP1363Signature(asn1SigBytes);
Boolean retVal = DSADeformatter.VerifySignature(hash, sigBytes);
return retVal;
}
This relies on two helper methods.
1) AsnKeyParser is a class attached to the linked article. The article offers a C# download from which I used two files: AsnKeyParser.cs and BerDecodeError.cs. I deleted the RSA functions from AsnKeyParser to remove the dependency on the BigInteger file.
This class handles parsing the "key.public" file created by my Java code.
2) A function for converting the 46-48 byte DER encoded signature generated by Java into a DSA signature which C# will accept.
This function is based on code in the comments of the linked article.
private static byte[] ConvertToP1363Signature(byte[] ASN1Sig)
{
AsnParser asn = new AsnParser(ASN1Sig);
asn.NextSequence();
byte[] r = asn.NextInteger();
byte[] s = asn.NextInteger();
// Returned to caller
byte[] p1363Signature = new byte[40];
if (r.Length > 21 || (r.Length == 21 && r[0] != 0))
{
// WTF???
// Reject - signature verification failed
}
else if (r.Length == 21)
{
// r[0] = 0
// r[1]'s high bit *should* be set
Array.Copy(r, 1, p1363Signature, 0, 20);
}
else if (r.Length == 20)
{
// r[0]'s high bit *should not* be set
Array.Copy(r, 0, p1363Signature, 0, 20);
}
else
{
// fewer than 20 bytes
int len = r.Length;
int off = 20 - len;
Array.Copy(r, 0, p1363Signature, off, len);
}
if (s.Length > 21 || (s.Length == 21 && s[0] != 0))
{
// WTF???
// Reject - signature verification failed
}
else if (s.Length == 21)
{
// s[0] = 0
// s[1]'s high bit *should* be set
Array.Copy(s, 1, p1363Signature, 20, 20);
}
else if (s.Length == 20)
{
// s[0]'s high bit *should not* be set
Array.Copy(s, 0, p1363Signature, 20, 20);
}
else
{
// fewer than 20 bytes
int len = s.Length;
int off = 40 - len;
Array.Copy(s, 0, p1363Signature, off, len);
}
return p1363Signature;
}

Related

How do I sign a TransactionHex with SeedHex using secp256k1 in C# with Bouncy Castle?

I have this nodejs snippet which I want to implement in C#.
The SignTransaction function takes in a seedHex string (length: 64) and a TransactionHex string, a signature is created using secp256k1 algorithm and appended to the end of the TransactionHex.
const sha256 = require('sha256');
const EC = require('elliptic').ec;
const ec = new EC("secp256k1");
// Serialize a number into an 8-byte array. This is a copy/paste primitive, not worth
// getting into the details.
function uvarint64ToBuf (uint) {
const result = [];
while (uint >= 0x80) {
result.push((uint & 0xFF) | 0x80);
uint >>>= 7;
}
result.push(uint | 0);
return new Buffer(result);
}
// Sign transaction with seed
function signTransaction (seed, txnHex) {
const privateKey = ec.keyFromPrivate(seed);
const transactionBytes = new Buffer(txnHex, 'hex');
const transactionHash = new Buffer(sha256.x2(transactionBytes), 'hex');
const signature = privateKey.sign(transactionHash);
const signatureBytes = new Buffer(signature.toDER());
const signatureLength = uvarint64ToBuf(signatureBytes.length);
const signedTransactionBytes = Buffer.concat([
transactionBytes.slice(0, -1),
signatureLength,
signatureBytes
])
return signedTransactionBytes.toString('hex');
}
example transaction hex
01efa5060f5fdae5ed9d90e5f076b2c328a64e347d0c87e8e3317daec5a44fe7c8000102bd53e48625f49e60ff6b7a934e3871b54cc2a93a8737352e8320549e42e2322bab0405270000167b22426f6479223a2248656c6c6f20576f726c64227de807d461f4df9efad8a0f3f716002102bd53e48625f49e60ff6b7a934e3871b54cc2a93a8737352e8320549e42e2322b0000
example signed transaction hex returned from the javascript function
01efa5060f5fdae5ed9d90e5f076b2c328a64e347d0c87e8e3317daec5a44fe7c8000102bd53e48625f49e60ff6b7a934e3871b54cc2a93a8737352e8320549e42e2322bab0405270000167b22426f6479223a2248656c6c6f20576f726c64227de807d461f4df9efad8a0f3f716002102bd53e48625f49e60ff6b7a934e3871b54cc2a93a8737352e8320549e42e2322b00473045022100c36e2b80f2160304cf640b1296244e7a3873aacf2831098bca3727ad06f4c270022007d3697ceef266a05ad70219d92fbd570f6ec7a5731aaf02718213067d42d1cf
If you notice the signed hex is TransactionHex + 47 (length of signature) + 473045022100c36e2b80f2160304cf640b1296244e7a3873aacf2831098bca3727ad06f4c270022007d3697ceef266a05ad70219d92fbd570f6ec7a5731aaf02718213067d42d1cf (actual signature)
I am trying to use C# Bouncy Castle library in Unity3d to generate the signature part but no success.
This is the C# code (source)
public string GetSignature(string privateKey, string message)
{
var curve = SecNamedCurves.GetByName("secp256k1");
var domain = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H);
var keyParameters = new ECPrivateKeyParameters(new BigInteger(privateKey, 16), domain);
var signer = new ECDsaSigner(new HMacDsaKCalculator(new Sha256Digest()));
signer.Init(true, keyParameters);
var signature = signer.GenerateSignature(Encoding.UTF8.GetBytes(message));
var r = signature[0];
var s = signature[1];
var otherS = curve.Curve.Order.Subtract(s);
if (s.CompareTo(otherS) == 1)
{
s = otherS;
}
var derSignature = new DerSequence
(
new DerInteger(new BigInteger(1, r.ToByteArray())),
new DerInteger(new BigInteger(1, s.ToByteArray()))
)
.GetDerEncoded();
return Convert(derSignature);
}
public string Convert(byte[] input)
{
return string.Concat(input.Select(x => x.ToString("x2")));
}
public byte[] Convert(string input)
{
if (input.StartsWith("0x")) input = input.Remove(0, 2);
return Enumerable.Range(0, input.Length / 2).Select(x => System.Convert.ToByte(input.Substring(x * 2, 2), 16)).ToArray();
}
This does generate a signature but it is not identical to the one I mentioned above.
Signature generated from GetSignature
3044022018a06b1f2b8a1e2f5ea2a78b0d6d98ec483b9fa4345821cfef892be6c825ff4702207958160e533c801dff5e50600206e1cd938d6df74ebfa4b0347a95de67dda986
P.S. Here is the python equivalent of signing transactions for reference.
sha256.x2() in the NodeJS code hashes twice, while ECDsaSigner in the C# code does not hash at all. Therefore, in the C# code, GetSignature() must explicitly hash twice:
using Org.BouncyCastle.Crypto.Digests;
...
var messageBytes = Convert(message);
var hash = GetHash(GetHash(messageBytes));
var signature = signer.GenerateSignature(hash);
...
with
private static byte[] GetHash(byte[] data)
{
var digest = new Sha256Digest();
var hash = new byte[digest.GetDigestSize()];
digest.BlockUpdate(data, 0, data.Length);
digest.DoFinal(hash, 0);
return hash;
}
Since both codes use the deterministic ECDSA algorithm according to RFC6979, the hashes for the same input data are identical and can be compared.
The overall result is the concatenation of txnHex without the last byte, the hex encoded length of the signature and the signature in ASN.1/DER format; a possible implementation is:
var txnHex = "...";
var seed = "...";
string signature = GetSignature(seed, txnHex);
string signedTransactionBytes = txnHex.Substring(0, txnHex.Length - 2) + (signature.Length / 2).ToString("x") + signature;
Note that the uvarint64ToBuf() implementation is actually more complex, but can be simplified for the signature lengths used here.

How to convert C++ Rijndael Cryptography to C#, when there is an error saying "Padding is invalid and cannot be removed"?

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++.

RSA and PublicKey interop with dotnet

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());
...

Use PEM encoded RSA private key in .NET

I have a private key which looks like this:
-----BEGIN RSA PRIVATE KEY-----
Some private key data
-----END RSA PRIVA
I need to use this key in my C# project, but I couldn't find any example how to use key in this format. Thanks
step 1 get "Some private key data" content.remove -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----, Removes all line symbols("\n");
step 2. parse key to RSA.
private RSACryptoServiceProvider CreateRsaProviderFromPrivateKey(string privateKey)
{
var privateKeyBits = System.Convert.FromBase64String(privateKey);
var RSA = new RSACryptoServiceProvider();
var RSAparams = new RSAParameters();
using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits)))
{
byte bt = 0;
ushort twobytes = 0;
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130)
binr.ReadByte();
else if (twobytes == 0x8230)
binr.ReadInt16();
else
throw new Exception("Unexpected value read binr.ReadUInt16()");
twobytes = binr.ReadUInt16();
if (twobytes != 0x0102)
throw new Exception("Unexpected version");
bt = binr.ReadByte();
if (bt != 0x00)
throw new Exception("Unexpected value read binr.ReadByte()");
RSAparams.Modulus = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.Exponent = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.D = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.P = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.Q = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.DP = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.DQ = binr.ReadBytes(GetIntegerSize(binr));
RSAparams.InverseQ = binr.ReadBytes(GetIntegerSize(binr));
}
RSA.ImportParameters(RSAparams);
return RSA;
}
private int GetIntegerSize(BinaryReader binr)
{
byte bt = 0;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = 0;
bt = binr.ReadByte();
if (bt != 0x02)
return 0;
bt = binr.ReadByte();
if (bt == 0x81)
count = binr.ReadByte();
else
if (bt == 0x82)
{
highbyte = binr.ReadByte();
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, 0);
}
else
{
count = bt;
}
while (binr.ReadByte() == 0x00)
{
count -= 1;
}
binr.BaseStream.Seek(-1, SeekOrigin.Current);
return count;
}
All major .NET/C# Cryptography libraries (like BouncyCastle, or SecureBlackbox [commercial]) should support this format, as well as operations with loaded key (encryption/decryption/signing/verification).
Though an older post I though I'd glue my own answer to this question as I bumped into the same challenge earlier this year.
I wrote a library for the handling of PEM keys, encryption, decryption, signing and signature verification.
There's an complete example solution attached to it (Load-Encrypt-Decrypt-Save), so you should be able to be up running in no-time.
https://github.com/jrnker/CSharp-easy-RSA-PEM
Cheers,
Christoffer
The following article I wrote about digital signature gives a solution in c# (no additional library needed). The code shows how a RSA private key in PEM format can be converted to the XML format used by the .NET RSACryptoServiceProvider Class.
With Atlando Crypto Currency Geo Service your identity is stored in your browser after registration. At each request a contract with us is signed and encrypted by this identity with your private key. This article explains how it works.
Code below gives an implementation in C# (RSACryptoServiceProvider Class) of the authentication process through comparison of the original and a signed version. The modulus comes from the RSA public key in PEM format (exponent AQAB).
private static bool Verify(string original, string signature, string modulus)
{
SHA256Managed sha = new SHA256Managed();
byte[] bytes = Encoding.UTF8.GetBytes(original);
byte[] hash = sha.ComputeHash(bytes);
sha.Clear();
byte[] signed = new byte[signature.Length/2];
for (int i = 0; i < signature.Length; i += 2)
{
signed[i/2] = Convert.ToByte(Convert.ToInt32(signature.Substring(i, 2), 16));
}
string key = "<RSAKeyValue><Modulus>" + modulus + "</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(key);
RSAPKCS1SignatureDeformatter RSADeformatter = new RSAPKCS1SignatureDeformatter(rsa);
RSADeformatter.SetHashAlgorithm("SHA256");
return RSADeformatter.VerifySignature(hash, signed);
}
}
public static string Modulus(string pem)
{
byte[] x509der = Convert.FromBase64String(pem.Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", ""));
byte[] seqOID = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 };
MemoryStream ms = new MemoryStream(x509der);
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) return null; //is the object identifier rsaEncryption PKCS#1?
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;
}
if (reader.ReadByte() == 0x02) //is it an integer?
{
int exponentSize = ReadASNLength(reader);
byte[] exponent = new byte[exponentSize];
reader.Read(exponent, 0, exponent.Length);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus;
RSAKeyInfo.Exponent = exponent;
rsa.ImportParameters(RSAKeyInfo);
return rsa.ToXmlString(false).Replace("<RSAKeyValue><Modulus>", "").Replace("</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>", "");
}
}
}
}
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;
}
First, you need to transform the private key to the form of RSA parameters using Bouncy Castle library. Then you need to pass the RSA parameters to the RSA algorithm as the private key. Lastly, you use the JWT library to encode and sign the token.
public string GenerateJWTToken(string rsaPrivateKey)
{
var rsaParams = GetRsaParameters(rsaPrivateKey);
var encoder = GetRS256JWTEncoder(rsaParams);
// create the payload according to your need
var payload = new Dictionary<string, object>
{
{ "iss", ""},
{ "sub", "" },
// and other key-values
};
var token = encoder.Encode(payload, new byte[0]);
return token;
}
private static IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
{
var csp = new RSACryptoServiceProvider();
csp.ImportParameters(rsaParams);
var algorithm = new RS256Algorithm(csp, csp);
var serializer = new JsonNetSerializer();
var urlEncoder = new JwtBase64UrlEncoder();
var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
return encoder;
}
private static RSAParameters GetRsaParameters(string rsaPrivateKey)
{
var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
using (var ms = new MemoryStream(byteArray))
{
using (var sr = new StreamReader(ms))
{
// use Bouncy Castle to convert the private key to RSA parameters
var pemReader = new PemReader(sr);
var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
return DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters);
}
}
}
ps: the RSA private key should have the following format:
-----BEGIN RSA PRIVATE KEY-----
{base64 formatted value}
-----END RSA PRIVATE KEY-----

PBKDF2 implementation in C# with Rfc2898DeriveBytes

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;
}
}
}
}

Categories