Getting the hashed password from ASP.NET Membership Provider - passwords

Is there any method on the MembershipProvider (or the Membership instance) which I can use to get the hashed password itself? I'm not trying to do a recovery, I'm not trying to find the actual password, I just need the hashed string.
Also, while I'm at it, can I get the exact algorithm's name (like SHA1 or MD5) used to create that hash?

No, I couldn't find any method that returns HashedPassword. Even if you get HashedPassword, you still need salt.
Default hash algorithm is salted base64 SHA1.
public string EncodePassword(string pass, string saltBase64)
{
byte[] bytes = Encoding.Unicode.GetBytes(pass);
byte[] src = Convert.FromBase64String(saltBase64);
byte[] dst = new byte[src.Length + bytes.Length];
Buffer.BlockCopy(src, 0, dst, 0, src.Length);
Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
byte[] inArray = algorithm.ComputeHash(dst);
return Convert.ToBase64String(inArray);
}

Related

iText 7 PDF signing with GlobalSign DSS

I'm digitally signing a PDF using iText 7 in an asp.net app. I'm using vb but I could figure out a c# answer, too. We're using GlobalSign DSS in order to have the signature not come up as UNKNOWN (AATL).
I created a pfx file from our company SSL certificate and I am able to sign the PDF files using that file but I don't understand how to use the things I get from GlobalSign to accomplish the same thing. This service requires to send a hex encoded SHA256 digest and in return I receive a hex encoded signatureValue. Besides that I also receive a PEM encoded X509 signing certificate, intermediate certificate (PEM encoded X509 certificate that represents the issuer of the signing certificate), Base64 encoded DER representation of the OCSP response for signing_certificate, a Base64 encoded DER representation of timestamp token. It includes the TSA signing certificate in SignedData.CertificatesSet.
I created the code below as best as I could with limited examples from the internet for itext 7 and my even more limited knowledge of PDF signing and certificates in general. I know now that there are some things I make that I don't even use properly such as the tsaClient and the crlList. I didn't quite understand what those were until I had to switch to the DSS. I actually still don't know what the crlList would be although I think it has something to do with a chain, although I don't quite know what a chain is in this context.
Dim outputFileStream As New FileStream(Server.MapPath(strDest), IO.FileMode.Create, IO.FileAccess.ReadWrite)
Dim signer As New PdfSigner(New PdfReader(Server.MapPath(strTemp)), outputFileStream, True)
signer.SetCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS)
Dim appearance As PdfSignatureAppearance = signer.GetSignatureAppearance
Dim rect As New iText.Kernel.Geom.Rectangle(50, 425, 500, 200)
appearance.SetContact("Contact Name - Phone")
appearance.SetSignatureCreator("Company - New Program")
appearance.SetReason(vbCrLf + "eDoc Consent Agreement: " + CType(Session("eDocConsentDateTime"), Date).ToString + vbCrLf _
+ "Terms and Conditions Agreement: " + objInstall.AgreementSigned.ToString + vbCrLf _
+ "Identifier: " + Session("Ident"))
appearance.SetLocation(vbCrLf + strInstallationAddress)
appearance.SetReuseAppearance(False)
appearance.SetPageRect(rect)
appearance.SetPageNumber(2) 'Signature will appear on Page 2
appearance.SetRenderingMode(PdfSignatureAppearance.RenderingMode.DESCRIPTION)
signer.SetFieldName(signer.GetNewSigFieldName())
Dim cert As New X509Certificate(Server.MapPath("CompSSL.pfx"), "password")
Dim tsaUrl As String = CertificateUtil.GetTSAURL(DotNetUtilities.FromX509Certificate(cert))
tsaClient = New TSAClientBouncyCastle(tsaUrl)
Dim crlList As IList(Of ICrlClient) = New List(Of ICrlClient)()
Dim fsCertFile As New FileStream(Server.MapPath("CompSSL.pfx"), FileMode.Open, FileAccess.Read)
Dim pk12 As New Pkcs12Store(fsCertFile, "password")
fsCertFile.Dispose()
'then Iterate throught certificate entries to find the private key entry
Dim [alias] As String = Nothing
For Each tAlias As String In pk12.Aliases
If pk12.IsKeyEntry(tAlias) Then
[alias] = tAlias
Exit For
End If
Next
Dim pk = pk12.GetKey([alias]).Key
Dim myExternalSignature As IExternalSignature = New PrivateKeySignature(pk, DigestAlgorithms.SHA1)
signer.SignDetached(myExternalSignature, New Org.BouncyCastle.X509.X509Certificate() {pk12.GetCertificate([alias]).Certificate}, Nothing, Nothing, Nothing, 0, PdfSigner.CryptoStandard.CMS)
So I'd like to know how I can use the GlobalSign pieces in order to sign the PDF. It almost seems like they are giving me everything I need to plug into the SignDetached method at the very end but I specifically don't know these things:
I believe that I'm supposed to do the hash to create the digest AFTER I've added the appearance stuff but I don't know what I would pass those bytes to something like Dim hash() As Byte = s.ComputeHash(fileBytes) at that point in my code (right after signer.SetFieldName(signer.GetNewSigFieldName()) I assume)
I don't know how or if I use the two certificates I get from the DSS. I even saw an example that suggests I may put them together to get something.
I don't know if the Signature Value I get is the equivalent of the IExternalSignature. If so, I don't know how to convert it to type "Org.BouncyCastle.Crypto.ICipherParameters" or even if I'm supposed to be trying to.
I plug the timestamp token string I get back from the DSS into the line tsaClient = New TSAClientBouncyCastle(tsaUrl) in the place of "tsaUrl" and there is no error but I don't know if that's right.
I don't know what to do with the OCSP response that I get from the DSS to put it into an IOcspClient object.
If I don't even try to put in the timestamp or the OCSP response in, as apparently I don't in the .pfx version of my code, what will that mean?
What are the data type conversions I'll have to do, if any, to make the DSS responses work?
I've tried to follow the iTextsharp examples, even this one (itextsharp signing pdf with signed hash) which is exactly what I'm trying to do but it doesn't seem to give me the info I need to finish it off. Thanks for your help.
EDIT:
Thanks for the help, dsandoval. I went ahead and hash the unchanged file and that's what I send to GlobalSign:
Dim s As New SHA256Managed
Dim fileBytes() As Byte = IO.File.ReadAllBytes(Server.MapPath(strSrc))
Dim hash() As Byte = s.ComputeHash(fileBytes)
Dim calculatedHash As String = ByteArrayToString(hash)
With this too:
Private Function ByteArrayToString(ByVal arrInput() As Byte) As String
Dim sb As New System.Text.StringBuilder(arrInput.Length * 2)
For i As Integer = 0 To arrInput.Length - 1
sb.Append(arrInput(i).ToString("X2"))
Next
Return sb.ToString().ToUpper
End Function
Then I wrote the two certificates I get from GS to files and then use BouncyCastle to read them into certificates and put them into an array (signing cert first). I pick up where I am writing the second cert file from their 'certificate_path' string here:
Using sw As New StreamWriter(File.Open(strFile2, FileMode.OpenOrCreate))
sw.WriteLine(strTempCert2)
End Using
Dim fileStream2 = System.IO.File.OpenText(strFile2)
Dim pemReader2 = New Org.BouncyCastle.OpenSsl.PemReader(fileStream2)
Dim myCertificateChain() As Org.BouncyCastle.X509.X509Certificate = New Org.BouncyCastle.X509.X509Certificate() {pemReader.ReadObject, pemReader2.ReadObject}
I did the same thing you did with IExternalSignature using this to convert the string from GS:
Private Function StringToByteArray(s As String) As Byte()
' remove any spaces from, e.g. "A0 20 34 34"
s = s.Replace(" "c, "")
' make sure we have an even number of digits
If (s.Length And 1) = 1 Then
Throw New FormatException("Odd string length when even string length is required.")
End If
' calculate the length of the byte array and dim an array to that
Dim nBytes = s.Length \ 2
Dim a(nBytes - 1) As Byte
' pick out every two bytes and convert them from hex representation
For i = 0 To nBytes - 1
a(i) = Convert.ToByte(s.Substring(i * 2, 2), 16)
Next
Return a
End Function
For now I'm just including the certificate chain and the signature and not the timestamp or the OCSP to see if I can just get it signed. With the certificate chain and the implementation of IExternalSignature, I'm able to get SignDetached to run:
signer.SignDetached(myExternalSignature, myCertificateChain, Nothing, Nothing, Nothing, 0, PdfSigner.CryptoStandard.CMS)
The PDF is signed but the signature panel says "Certification by is invalid", "Certified by %s". The details say "There are errors in the formatting or information contained in this signature". When clicking on "Certificate details", nothing happens. I found another person online with this error (Error signing pdf with PHP's openssl_pkcs7_sign) and it was due to the signature container being too big. The zero in my SignDetached parameters is supposed to be "estimate the size" so I changed it to 8192 which was a magic number of 8k I found in some other examples online. That didn't help. Any suggestions at this point?
I'm using c# for my implementation. I'm not too familiar with VB but hopefully this helps. Most of the examples I've seen are using Java and there were some differences in how they implement this, so I had to work around them. The only things I had to implement for this work was the IExternalSignature, IOcspClient, and ITsaClient.
The digest that is sent to GlobalSign are the contents of the document prior to applying to the signature, so the appearance can be left out if applied with signature. The document is not modified until you apply the signature using the PdfSigner.SignDetached, I believe. To compute the digest, you need to use the SHA256 algorithm to hash the message and hexencode it. This is the digest that will be sent to GS.
You need to put both the certificates from GlobalSign to create the certificate chain. First add the signing certificate then the certificate you can retrieve from their 'certificate_path' endpoint.
I'm not sure about the conversion to that type, but with the c# library, the Sign method of IExternalSignature returns a byte[]. With my own implementation of the interface, I simply converted the SignatureValue we received from GS to a byte[].
For the timestamp, we also implemented our own ITsaClient that calls GS for the timestamp. The digest has to be calculated in the GetTimeStampToken method and sent to GS. The response is a hexencoded string that we convert to a byte[].
For the OCSP, we also implemented our own IOcspClient. Since we get the ocsp_response when we fetch the identity, this client simply uses that, rather than calling GS again. I had issues enabling LTV when returning the response by only converting it from a Base64 string to a byte[]. BouncyCastle had trouble reading the response in this format. I had to convert it to the following BouncyCastle objects for this to work properly.
public byte[] GetEncoded( X509Certificate checkCert, X509Certificate issuerCert, string url )
{
var decoded = Base64.Decode(_ocspResponse); //_ocspResponse is same one that we received when fecthing the identity
var response = new OcspResp(decoded);
var basicResponse = (BasicOcspResp)response.GetResponseObject();
return basicResponse.GetEncoded();
}
If no timestamp is attached, the signature will not be timestamped. It will display the time as something along the lines of "the time is time on the signer's computer's system clock" when you open the document in Adobe. If no ocsp response is attatched, the person validating will not have a way to validate that the certificate has/hasn't been revoked at the time of the signature.
I believe the only conversions I had to make were the one mentioned above.
Let me know if you need any more code examples or any other questions.
EDIT
Here is the IExternalSignature implementation. The hash and encryption algorithm has to match what GS will use. They are using SHA256 and RSA.
public class ExternalSignature : IExternalSignature
{
private readonly SigningIdentity _identity;
private readonly GlobalSignClient _client;
public ExternalSignature( GlobalSignClient client, SigningIdentity identity )
{
_client = client;
_identity = identity;
}
public string GetEncryptionAlgorithm() => "RSA";
public string GetHashAlgorithm() => DigestAlgorithms.SHA256;
public byte[] Sign( byte[] message )
{
// Using the System.Security.Cryptography implementation of SHA256
using (var sha = SHA256.Create())
{
// Hash the message, containing the PDF
var bytes = sha.ComputeHash(message);
// HexEconde the hashed message to send to GlobalSign
var digest = Helpers.ByteArrayToString(bytes);
// Send the digest to GlobalSign
var signature = _client.GetSignatureAsync(_identity.Id, digest).Result;
// Return the returned signature as a byte[]
return Helpers.StringToByteArray(signature);
}
}
}
To add the signature, just create an instance and pass it to SignDetached method.
var signature = new ExternalSignature(_client, signingIdentity);
...
...
//After creating the PDF signer, adding appearance, creating the OCSP and TSA clients.
signer.SignDetached(signature, certChain, null, ocsp, tsa, 0, PdfSigner.CryptoStandard.CMS);
I did the same with the OCSP and TSA clients.

itextsharp signing pdf with signed hash

I'm trying to sign a pdf through a signing service. This service requires to send a hex encoded SHA256 digest and in return I receive a hex encoded signatureValue. Besides that I also receive a signing certificate, intermediate certificate, OCSP response, and TimeStampToken. However, I already get stuck trying to sign the pdf with the signatureValue.
I have read Bruno's white paper, browsed the internet excessively, and tried many different ways, but the signature keeps coming up as invalid.
My latest attempt:
First, prepare pdf
PdfReader reader = new PdfReader(src);
FileStream os = new FileStream(dest, FileMode.Create);
PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0');
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Certificate = signingCertificate;
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
MakeSignature.SignExternalContainer(appearance, external, 8192);
string hashAlgorithm = "SHA-256";
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
PdfSignatureAppearance appearance2 = stamper.SignatureAppearance;
Stream stream = appearance2.GetRangeStream();
byte[] hash = DigestAlgorithms.Digest(stream, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
Hash byte[] sh and convert to string as follows
private static String sha256_hash(Byte[] value)
{
using (SHA256 hash = SHA256.Create())
{
return String.Concat(hash.ComputeHash(value).Select(item => item.ToString("x2"))).ToUpper();
}
}
and send to signing service. The received hex encoded signatureValue I then convert to bytes
private static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length).Where(x => x % 2 == 0).Select(x => Convert.ToByte(hex.Substring(x, 2), 16)).ToArray();
}
Finally, create signature
private void CreateSignature(string src, string dest, byte[] sig)
{
PdfReader reader = new PdfReader(src); // src is now prepared pdf
FileStream os = new FileStream(dest, FileMode.Create);
IExternalSignatureContainer external = new MyExternalSignatureContainer(sig);
MakeSignature.SignDeferred(reader, "Signature1", os, external);
reader.Close();
os.Close();
}
private class MyExternalSignatureContainer : IExternalSignatureContainer
{
protected byte[] sig;
public MyExternalSignatureContainer(byte[] sig)
{
this.sig = sig;
}
public byte[] Sign(Stream s)
{
return sig;
}
public void ModifySigningDictionary(PdfDictionary signDic) { }
}
What am I doing wrong? Help is very much appreciated. Thanks!
Edit: Current state
Thanks to help from mkl and following Bruno's deferred signing example I've gotten past the invalid signature message. Apparently I don't receive a full chain from the signing service, but just an intermediate certificate, which caused the invalid message. Unfortunately, the signature still has flaws.
I build the chain like this:
List<X509Certificate> certificateChain = new List<X509Certificate>
{
signingCertificate,
intermediateCertificate
};
In the sign method of MyExternalSignatureContainer I now construct and return the signature container:
public byte[] Sign(Stream s)
{
string hashAlgorithm = "SHA-256";
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
byte[] ocspResponse = Convert.FromBase64String("Base64 encoded DER representation of the OCSP response received from signing service");
byte[] hash = DigestAlgorithms.Digest(s, hashAlgorithm);
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, ocspResponse, null, CryptoStandard.CMS);
string messageDigest = Sha256_hash(sh);
// messageDigest sent to signing service
byte[] signatureAsByte = StringToByteArray("Hex encoded SignatureValue received from signing service");
sgn.SetExternalDigest(signatureAsByte, null, "RSA");
ITSAClient tsaClient = new MyITSAClient();
return sgn.GetEncodedPKCS7(hash, tsaClient, ocspResponse, null, CryptoStandard.CMS);
}
public class MyITSAClient : ITSAClient
{
public int GetTokenSizeEstimate()
{
return 0;
}
public IDigest GetMessageDigest()
{
return new Sha256Digest();
}
public byte[] GetTimeStampToken(byte[] imprint)
{
string hashedImprint = HexEncode(imprint);
// Hex encoded Imprint sent to signing service
return Convert.FromBase64String("Base64 encoded DER representation of TimeStampToken received from signing service");
}
}
Still get these messages:
"The signer's identity is unknown because it has not been included in the list of trusted identities and none or its parent
certificates are trusted identities"
"The signature is timestamped, but the timestamp could not be verified"
Further help is very much appreciated again!
"What am I doing wrong?"
The problem is that on one hand you start constructing a CMS signature container using a PdfPKCS7 instance
PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, false);
and for the calculated document digest hash retrieve the signed attributes to be
byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, CryptoStandard.CMS);
to send them for signing.
So far so good.
But then you ignore the CMS container you started constructing but instead inject the naked signature bytes you got from your service into the PDF.
This cannot work as your signature bytes don't sign the document directly but instead they sign these signed attributes (and, therefore, indirectly the document as the document hash is one of the signed attributes). Thus, by ignoring the CMS container under construction you dropped the actually signed data...
Furthermore, the subfilter ADBE_PKCS7_DETACHED you use promises that the embedded signature is a full CMS signature container, not a few naked signature bytes, so the format also is wrong.
How to do it instead?
Instead of injecting the naked signature bytes you got from your service into the PDF as is, you have to set them as external digest in the PdfPKCS7 instance in which you originally started constructing the signature container:
sgn.SetExternalDigest(sig, null, ENCRYPTION_ALGO);
(ENCRYPTION_ALGO must be the encryption part of the signature algorithm, I assume in your case "RSA".)
and then you can retrieve the generated CMS signature container:
byte[] encodedSig = sgn.GetEncodedPKCS7(hash, null, null, null, CryptoStandard.CMS);
Now this is the signature container to inject into the document using MyExternalSignatureContainer:
IExternalSignatureContainer external = new MyExternalSignatureContainer(encodedSig);
MakeSignature.SignDeferred(reader, "Signature1", os, external);
Remaining issues
Having corrected your code Adobe Reader still warns about your signatures:
"The signer's identity is unknown because it has not been included in the list of trusted identities and none or its parent certificates are trusted identities"
This warning is to be expected and correct!
The signer's identity is unknown because your signature service uses merely a demo certificate, not a certificate for production use:
As you see the certificate is issued by "GlobalSign Non-Public HVCA Demo", and non-public demo issuers for obvious reasons must not be trusted (unless you manually add them to your trust store for testing purposes).
"The signature is timestamped, but the timestamp could not be verified"
There are two reasons why Adobe does not approve of your timestamp:
On one hand, just like above, the timestamp certificate is a non-public, demo certificate ("DSS Non-Public Demo TSA Responder"). Thus, there is no reason for the verifier to trust your timestamp.
On the other hand, though, there is an actual error in your timestamp'ing code, you apply the hashing algorithm twice! In your MyITSAClient class you have
public byte[] GetTimeStampToken(byte[] imprint)
{
string hashedImprint = Sha256_hash(imprint);
// hashedImprint sent to signing service
return Convert.FromBase64String("Base64 encoded DER representation of TimeStampToken received from signing service");
}
The imprint parameter of your GetTimeStampToken implementation is already hashed, so you have to hex encode these bytes and send them for timestamp'ing. But you apply your method Sha256_hash which first hashes and then hex encodes this new hash.
Thus, instead of applying Sha256_hash merely hex encode the imprint!

generate password for auth-plug for mosquitto in c#

I have spent all day trying to make a usable pbkdf2 password for the mosquitto-auth-plug. This program sets it up exactly the way it should be stored in the mysql database. I have a password hash generated by the program that comes with auth-plug and mosquitto loves it. I just can't replicate it in c#, if anyone can help please let me know.
public string CreatePasswordHash(string password)
{
var salt = GenerateRandomSalt();
var iterationCount = GetIterationCount();
var hashValue = GenerateHashValue(password, salt, iterationCount);
string result = "PBKDF2$sha256$" + iterationCount + "$" + Convert.ToBase64String(salt) + "$" + Convert.ToBase64String(hashValue);
return result;
}
private int GetIterationCount()
{
return 901;
}
private static byte[] GenerateRandomSalt()
{
var csprng = new RNGCryptoServiceProvider();
var salt = new byte[SaltByteLength];
csprng.GetBytes(salt);
return salt;
//return GetLetter();
}
private static byte[] GenerateHashValue(string password, byte[] salt, int iterationCount)
{
byte[] hashValue;
var valueToHash = string.IsNullOrEmpty(password) ? string.Empty : password;
using (var pbkdf2 = new Rfc2898DeriveBytes(valueToHash, salt, iterationCount))
{
hashValue = pbkdf2.GetBytes(DerivedKeyLength);
}
return hashValue;
}
---EDIT-----
Rfc2898DeriveBytes states --
Implements password-based key derivation functionality, PBKDF2, by using psuedo-random number generator based on HMACSHA1.
The program/auth-plug seems to be using sha256 is there a c# PBKDF2 that uses this.
As you have already stated in your edit, the problem appears to be the difference in hash functions used between the mosquitto plugin (which only supports SHA-256, according to the source code) and the .NET implementation (which can only do SHA-1).
A more flexible implementation of PBKDF2 is available in BouncyCastle, a more lightweight implementation can be found here. If you are not satisfied with those two, you could chose to implemnt PBKDF2 yourself, which is not really hard.
Well it appears there really wasn't an answer and there weren't many who had tried. I figured out the problem and after contacting the mosquitto-auth-plug author he felt it would be good for us to add my solution to his contrib folder on the plugins github repo.
So now if you need a c# hashing algorithm for mosquitto-auth-plug just go to the repo in git here
https://github.com/jpmens/mosquitto-auth-plug/tree/master/contrib/C%23
and follow my instructions --- Let me know if you have any problems

AES encryption and decryption resulting in file different than original

I've decided to implement encryption for file transfers in my service. File transfers prior to this were not encrypted, and they were sent and received flawlessly with the exact same number of bytes.
Now I've introduced asymmetrical and symmetrical encryption into the mix to encrypt the data as it passes over the TCP protocol. I use asymmetrical to do an initial handshake passing the symmetrical key to the other party encrypted by the asymmetric public key. From then on out, the receiver of the file calls the sender periodically, and the sender generates a new initialization vector, encrypts the data with the symmetric key, and sends it over to be decrypted by the receiver using the IV and same symmetric key.
The chunk size I'm using is 2mb, such that the byte size of the generated chunks, with exception to the last chunk which varies, is 2097152. When AES encrypts this file with PaddingMode.PKCS7 and CipherMode.CBC, the resulting byte size is 2097168. It's gained about 16 bytes during the encryption process.
Now initially I thought this is where my problem was, but when I decrypt the data on the receiving end, it goes back to the 2097152 byte length and I write it to the file. I've proven to myself that it does indeed encrypt and decrypt the data.
On a small enough file, the file sizes from the original to the sender seem to be exactly the same. However, as I step up to larger file sizes, there exists a descrepency. On a video file(Wildlife.wmv from windows 7 install) of size 26,246,026 bytes, I am instead receiving a finished transfer that is of 26,246,218 bytes.
Why is there this size difference? What am I doing wrong here?
Here's some of my code.
For my encryption I am using the following class to encrypt or decrypt, returning a result in the form of a byte array.
public class AesCryptor
{
public byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
{
using (SymmetricAlgorithm aes = new AesManaged())
{
aes.Key = key;
aes.IV = iv;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv))
{
return Crypt(data, key, iv, encryptor);
}
}
}
public byte[] Decrypt(byte[] data, byte[] key, byte[] iv)
{
using (SymmetricAlgorithm aes = new AesManaged())
{
aes.Key = key;
aes.IV = iv;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
using (ICryptoTransform decryptor = aes.CreateDecryptor(key, iv))
{
return Crypt(data, key, iv, decryptor);
}
}
}
private byte[] Crypt(byte[] data, byte[] key, byte[] iv, ICryptoTransform transform)
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
}
return memoryStream.ToArray();
}
}
}
The sender of the file is encrypting the data(after the handshake of the private symmetric key) with this code(and a lot more that doesn't pertain to the actual encryption process. Note the chunkedFile.NextChunk(). This calls a method on the class that is doing the file chunking for me, returning 2mb chunk sizes unless the final size is smaller.
byte[] buffer;
byte[] iv = new byte[symmetricEncryptionBitSize / 8];
using (var rngCrypto = new RNGCryptoServiceProvider())
rngCrypto.GetBytes(iv);
AesCryptor cryptor = new AesCryptor();
buffer = cryptor.Encrypt(chunkedFile.NextChunk(), symmetricPrivateKey, iv);
The code below is what the receiver of the file uses(not all of it, this is what pertains to the decrypting of the data). The data is being written to a file stream(writer).
FileMessage message = hostChannel.ReceiveFile();
moreChunks = message.FileMetaData.MoreChunks;
UpdateTotalBytesTransferred(message);
writer.BaseStream.Position = filePosition;
byte[] decryptedStream;
// Copy the message stream out to a memory stream so we can work on it afterwards.
using (var memoryStream = new MemoryStream())
{
message.ChunkData.CopyTo(memoryStream);
decryptedStream = cryptor.Decrypt(memoryStream.ToArray(), symmetricPrivateKey, message.FileMetaData.InitializationVector);
}
writer.Write(decryptedStream);
By the way, in case it is needed, NextChunk is a very simple method.
public byte[] NextChunk()
{
if (MoreChunks) // If there are more chunks, procede with the next chunking operation, otherwise throw an exception.
{
byte[] buffer;
using (BinaryReader reader = new BinaryReader(File.OpenRead(FilePath)))
{
reader.BaseStream.Position = CurrentPosition;
buffer = reader.ReadBytes((int)MaximumChunkSize);
}
CurrentPosition += buffer.LongLength; // Sets the stream position to be used for the next call.
return buffer;
}
else
throw new InvalidOperationException("The last chunk of the file has already been returned.");
}
EDIT: It seems that for every chunk transferred, and thus every encryption, I am gaining 16bytes in file size. This does not happen with extremely small file sizes.
Well I solved the issue.
It turns out I was sending in the message data the chunkLength of the encrypted chunk data. So for every chunk I sent, even though I decrypted and wrote the correct filedata, I was advancing the stream position by the length of the encrypted data. This means every time I decrypted, when transferring more than 1 chunk(this is why the small files of only 1 chunk size didn't have problems) I was adding 16 bytes to the file size.
People helping me probably wouldn't have been able to figure this out, because I didn't include all of the data in the client side or the server side to see this. But thankfully I managed to answer it myself.
On the sender side, I was creating my FileMessage like this.
FileMessage message = new FileMessage();
message.FileMetaData = new FileMetaData(chunkedFile.MoreChunks, chunkedFile.ChunkLength, chunkedFile.CurrentPosition, iv);
message.ChunkData = new MemoryStream(buffer);
If you see the second parameter of FileMetaData constructor, I'm passing in chunkedFile.ChunkLength which is supposed to be the length of the chunk. I was doing this on the encrypted chunk data, which resulted in sending the incorrect chunk length.
The client on the other hand, was receiving this extra information. If you look near the end, you'll see the code filePosition += message.FileMetaData.ChunkLength;. I was using that erroneous chunkLength to advance the file position. It turns out that setting of the streamPosition was not even necessary.
using (BinaryWriter writer = new BinaryWriter(File.OpenWrite(fileWritePath)))
{
writer.BaseStream.SetLength(0);
while (moreChunks)
{
FileMessage message = hostChannel.ReceiveFile();
moreChunks = message.FileMetaData.MoreChunks;
UpdateTotalBytesTransferred(message);
writer.BaseStream.Position = filePosition;
byte[] decryptedStream;
// Copy the message stream out to a memory stream so we can work on it afterwards.
using (var memoryStream = new MemoryStream())
{
message.ChunkData.CopyTo(memoryStream);
Debug.WriteLine("Received Encrypted buffer Length: " + memoryStream.Length);
decryptedStream = cryptor.Decrypt(memoryStream.ToArray(), symmetricPrivateKey, message.FileMetaData.InitializationVector);
Debug.WriteLine("Received Decrypted buffer Length: " + decryptedStream.Length);
}
writer.Write(decryptedStream);
TotalBytesTransferred = message.FileMetaData.FilePosition;
filePosition += message.FileMetaData.ChunkLength;
}
OnTransferComplete(this, EventArgs.Empty);
StopSession();
}
Such a simple bug, but one that wasn't leaping out at me quickly at all.

securing data at the client side

am [writing reading] from a plain text file usernames and passwords, but its an bad way of securing data, is there any other way to do this, most probably by using IV's, encryption decryption algorithms.
You need to decide whether you need to be able to decrypt the text again or not. I would suggest that, for passwords at least, you can use a one-way hash You can make it a lot hard to brute force attack the hash if you make use of a salt. Ideally a separate, random salt should be used for each password: password+salt=hash. You need to store the salt as well as the hash but it can be stored in plain-text as it is not cryptographically secret. An example implementation (using SHA256):
public class SHA256
{
public static string GetHash(string password, string salt)
{
UTF8Encoding encoder = new UTF8Encoding();
SHA256Managed sha256 = new SHA256Managed();
byte[] hashedDataBytes = sha256.ComputeHash(encoder.GetBytes(salt + password));
return ByteArrayToString(hashedDataBytes);
}
/// <summary>
/// Generates a random 16 character alpha-numeric salt
/// </summary>
/// <returns></returns>
public static string GenerateRandomSalt()
{
const string allowedChars = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789";
char[] chars = new char[16];
var rd = new Random((int)DateTime.Now.Ticks);
for (int i = 0; i < 16; i++)
{
chars[i] = allowedChars[rd.Next(0, allowedChars.Length)];
}
return new string(chars);
}
/// <summary>
/// Converts supplied byte array to hex format string.
/// </summary>
/// <param name="inputArray"></param>
/// <returns></returns>
private static string ByteArrayToString(byte[] inputArray)
{
StringBuilder output = new StringBuilder("");
for (int i = 0; i < inputArray.Length; i++)
{
output.Append(inputArray[i].ToString("X2")); //Return in hex format
}
return output.ToString();
}
}
The GenerateRandomSalt method can be used to generate a salt for your password. There are lots of other questions covering this sort of thing on SO.
You can encode your passwords with HASHES like SHA1.
Function getSHA1Hash(ByVal strToHash As String) As String
Dim sha1Obj As New Security.Cryptography.SHA1CryptoServiceProvider
Dim bytesToHash() As Byte = System.Text.Encoding.ASCII.GetBytes(strToHash)
bytesToHash = sha1Obj.ComputeHash(bytesToHash)
Dim strResult As String = ""
For Each b As Byte In bytesToHash
strResult += b.ToString("x2")
Next
Return strResult
End Function
Using it is simple!
Console.Write(getSHA1Hash("password"))
So whenever you need to authenticate a user, you can take his input password, compute a hash of it using the above function and make sure it's the same with a simple IF clause:
if getSHA1Hash(input_password) = HASHED_OLD_PASSWORD then Authenticate()
SHA1 hashes aren't decryptable without sheer brute forcing every possibility so it's a pretty safe solution.