how to verify signature with DSACryptoServiceProvider

S

steven acer

i wrote a small console app using C# and .NET 3.0 that encrypts and
signs a file in order for it to be sent across the network to a remote
computer where it will have its signature verified and its contents
decrypted.

i used a symmetric key to encrypt the file, encrypted that key with an
RSACryptoServiceProvider ( public key of the user on the remote
computer) and DSACryptoServideProvider for signature of the hash ( my
private key).

my problem lies in verifying the signature of the file on the remote
PC.For the first phase of encryption and signature, 'm writing the
signed hash to the end of the encrypted file.
here's my code for verification, i'm using a CryptoStream to decrypt
the data as it's read and than after reading and decrypting the file
contents my code gets to the signed hash, it reads it and tries to
verify the signature but it fails :

Code:
public static bool decrypt(string filein, string
fileout,FileMode outMode, string containerName, string pubKeyPath)
{
using(FileStream fin = new
FileStream(filein,FileMode.Open,FileAccess.Read),
fout = new FileStream(fileout,outMode,FileAccess.Write))
{
CryptoStream cin = null;
try
{
// start reading the file in the same order the
information was written to it
BinaryReader bReader = new BinaryReader(fin);
// read the lenght of encrypted salt and IV
int securedSaltLength = bReader.ReadInt32();
int securedIVLength = bReader.ReadInt32();
int readSize = 0;

// read the salt and IV
byte[] secureSalt = new byte[securedSaltLength],
securedIV = new byte[securedIVLength];
fin.Read(secureSalt, 0, secureSalt.Length);
fin.Read(securedIV, 0, securedIVLength);
// get the private key of the reciever to decrypt
them
RSACryptoServiceProvider receiverPvKey =
(RSACryptoServiceProvider)Util.getPrivateKeyFromContainer(containerName,
StoreName.My, typeof(RSACryptoServiceProvider));
if (receiverPvKey == null)
{
Console.WriteLine("could not load key from
container " + containerName);
return false;
}
byte[] salt =
receiverPvKey.Decrypt(secureSalt,false);

byte[] IV =
receiverPvKey.Decrypt(securedIV,false);
// rebuild the symmetric key
PasswordDeriveBytes pdb = new
PasswordDeriveBytes("lalakers",salt,"SHA256",1000);

SymmetricAlgorithm symmetricKey =
RijndaelManaged.Create();
symmetricKey.KeySize = 256;
symmetricKey.Key = pdb.GetBytes(32);
symmetricKey.Padding = PaddingMode.PKCS7;
symmetricKey.IV = IV;
symmetricKey.Padding = PaddingMode.PKCS7;

byte[] buffer = new byte[1024*128];
SHA256 hasher = SHA256.Create();
cin = new CryptoStream(fin,
symmetricKey.CreateDecryptor(), CryptoStreamMode.Read);
CryptoStream cHash = new CryptoStream(Stream.Null,
hasher, CryptoStreamMode.Write);
BinaryReader binReader = new
BinaryReader(cin);

// get the original size of the file
long lSize = binReader.ReadInt64();

// calculate the number of reads needed to
read the whole file
long numberOfReads = (long)lSize /
CryptoHelp.BUFFER_SIZE;
long leftOverToRead = (long)lSize %
CryptoHelp.BUFFER_SIZE;
for (int round = 0; round < numberOfReads;
round++)
{
// read to the decryption
int read = cin.Read(buffer, 0,
buffer.Length);
fout.Write(buffer, 0, read);
cHash.Write(buffer, 0, read);
readSize += read;
}
//read the left overs
if(leftOverToRead>0)
{
// read into the decryption stream
int toRead = (int)(leftOverToRead);
int read = cin.Read(buffer, 0, toRead);
readSize += read;
// write decrypted data into the outfile
fout.Write(buffer, 0, read);
// write it to the hash stream (calculate
its hash)
cHash.Write(buffer, 0, read);
}

// flush the hash stream to be able to
retrieve the computed hash
cHash.Flush();
cHash.Close();

// we will need to read the public key of the
sender
DSACryptoServiceProvider dsa =
(DSACryptoServiceProvider)Util.readPublicKeyFromFile(pubKeyPath,
typeof(DSA));
if (dsa == null)
{
Console.WriteLine("could not load key from
file " + pubKeyPath);
return false;
}
List<byte> left = new List<byte>();
int leftByte;
while ((leftByte = fin.ReadByte()) != -1)
left.Add((byte)leftByte);
List<byte> sign = new List<byte>();
int offset = 0,sOffset=0;
byte[] computedHash = hasher.Hash,signedHash =
left.ToArray();
bool goodSignature = true;
String oid =
CryptoConfig.MapNameToOID("SHA1");
while (offset < computedHash.Length &&
goodSignature)
{
byte[] chunk = new byte[20],sChunk=new
byte[40];
int copySize = (computedHash.Length -
offset) > 20 ? 20 : computedHash.Length - offset;
Array.Copy(computedHash, offset, chunk, 0,
copySize);
copySize = (signedHash.Length - sOffset) >
40 ? 40 : signedHash.Length - sOffset;
Array.Copy(signedHash, sOffset, sChunk, 0,
copySize);
offset += 20;sOffset+=40;
goodSignature = dsa.VerifyHash(chunk,oid,
sChunk);
sign.AddRange(signedHash);
}
if (!goodSignature)
{
Console.WriteLine("file verification
failed ! signatures do not macth");
return false;

}
else
Console.WriteLine("signature verified");
if (lSize != readSize)
{
Console.WriteLine("file verification
failed ! file sizes do not macth ->original="+lSize+"
read="+readSize);
return false;
}
else
Console.WriteLine("signature verified");
fout.Flush();
cin.Clear();
return true;
}
finally
{
if(cin!=null)
cin.Close();
fin.Close();
fout.Close();
}
}
}


i tried verifying the signature in one pass as
dsa.verifyHash(computedHash,oid,signature)
but this is throwing a
"System.Security.Cryptography.CryptographicException: SHA1 algorithm
hash size is 20 bytes" exception. i then tried to do it by chunks as
in the code above, (i'm signing the hash the same way actually, every
20 bytes in a pass and then combining all the chunks together) no more
exception but the signatures do not match anymore. the reason i'm
using 20 and 40 byte chunks is that i read that DSA signature only
works with a hash of maximum 20 bytes and after running some tests i
noticed that every 20 bytes generate a signed hash of 40 bytes which
means that i *should*( possibly wrong emplacement of should) 40 bytes
of the signed hash written to the file for every 20 bytes of the
inline computed hash to verify.

please advise and correct me if my analysis is wrong.

other than that i'm running into another problem which is the
CryptoStream throwing a
Unhandled Exception:
System.Security.Cryptography.CryptographicException: Padding is
invalid and cannot be removed.
at
System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[]
inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]&
outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean
fLast)
at
System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[]
inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.CryptoStream.FlushFinalBlock()
at System.Security.Cryptography.CryptoStream.Dispose(Boolean
disposing)
at System.IO.Stream.Close()

when in the above code, cin is closed and only when i'm verifying the
signature using the DSA key ( if i ommit the verifying code it works).
i researched this one a lot too and found too many proposed solutions
but none worked, i think its more linked to the first problem

any help is greatly appreciated
regards
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top