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.
Related
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
I'm trying to understand why the code directly below does not require you to generate a IV key as well?? Code is from:
http://msdn.microsoft.com/en-us/library/sb7w85t6(v=vs.85).aspx
Dim key As RijndaelManaged = Nothing
Try
' Create a new Rijndael key.
key = New RijndaelManaged()
I see this sample code but requires you to generate both keys manaually?
Code is from:
http://msdn.microsoft.com/en-us/library/System.Security.Cryptography.RijndaelManaged(v=vs.110).aspx
Class RijndaelExample
Public Shared Sub Main()
Try
Dim original As String = "Here is some data to encrypt!"
' Create a new instance of the RijndaelManaged
' class. This generates a new key and initialization
' vector (IV).
Using myRijndael As New RijndaelManaged()
myRijndael.GenerateKey()
myRijndael.GenerateIV()
Also I plan to hardcode the key into the source(I know it's not the most secure)... how do I actually store these.. it looks like it will generate a new key everytime the application is open.
You're right, in that it will create a new key and IV every time you run. Instead, you should be creating a hash yourself (which is used to encrypt the data, and is derived from your password and a "salt" - see http://en.wikipedia.org/wiki/Salt_(cryptography))
For example,
SymmetricAlgorithm m_encryption;
RSACryptoServiceProvider m_rsa;
Rfc2898DeriveBytes m_hash;
string password = "Pa55w0rd";
string salt = "this is my salt. There are many like it, but this one is mine.";
public void SetupEncryption()
{
m_encryption = new RijndaelManaged();
m_hash = new Rfc2898DeriveBytes(password, Encoding.ASCII.GetBytes(salt));
m_encryption.Key = m_hash.GetBytes(m_encryption.KeySize / 8);
m_encryption.IV = m_hash.GetBytes(m_encryption.BlockSize / 8);
}
As you've noted though, storing your salt and your password are very bad form! This is just an example to show how to get started. Take a good look through wikipedia and other articles until you fully understand the principles!
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.
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);
}
I have visited and read all the Valence, and specifically the REST API, pages. I have one approved key already and a second key that has not yet been approved by D2L, and it's not clear how I request that approval.
The documentation contains a lot of information, but it is difficult to put all the pieces together. For example, in order to make any REST API call, I have to add several parameters to the end of the call. The parameters are documented in one place, but it isn't clear in some cases how to construct them (for example, one of the keys is to contain the url, timestamp, and the type of call being made, but how are they to be concatenated?). Then they have to be signed, and the documentation that tells how to sign the keys is in a completely different page that is not even referenced from the page that tells you that you have to sign the parameters. On top of that, the documentation is not extremely clear about how to do the signing, and offers no further explanation or examples. So to get anywhere, we have to jump around a lot through the documentation, and go through a whole lot of trial and error. It appears that the documentation assumes that the reader has expertise in several areas, which may or may not be true.
Code examples would make a huge difference.
There aren’t a lot of samples yet; we are working to add more, and to make the ones that are present more obvious. As one example, there is a Java Android app that has all the authentication stuff and some basic calls (including the call “whoami” which is a great test call).
The specific auth related files are available as well. From the D2LSigner class, you can see the signing algorithm we use:
Mac hmacSha256 = Mac.getInstance("hmacSHA256");
byte[] keyBytes = key.getBytes("UTF-8");
Key k = new SecretKeySpec(keyBytes, "hmacSHA256");
hmacSha256.init(k);
byte[] dataBytes = data.getBytes("UTF-8");
byte[] sig = hmacSha256.doFinal(dataBytes)
String sigString = base64Url( sig );
From D2LOperationSecurityImpl, you can see how the query string fits together:
//uppercase METHOD, lowercase PATH, timestamp as string
private static /*final*/ String BASE_STRING_TEMPLATE = "{0}&{1}&{2}";
private static /*final*/ String APP_ID_QUERY_NAME = "x_a";
private static /*final*/ String APP_SIG_QUERY_NAME = "x_c";
private static /*final*/ String USER_ID_QUERY_NAME = "x_b";
private static /*final*/ String USER_SIG_QUERY_NAME = "x_d";
private static /*final*/ String TIMESTAMP_QUERY_NAME = "x_t";
...
#Override
public Uri createAuthenticatedUri(String path, String httpMethod) {
long timestamp = System.currentTimeMillis() +
mServerSkewCorrectionMillis.longValue();
Long timestampObjectSeconds = new Long(timestamp/1000);
Object[]formatParms = {httpMethod.toUpperCase(),
path.toLowerCase(),
timestampObjectSeconds.toString()};
String signatureBaseString = MessageFormat.format(BASE_STRING_TEMPLATE,
formatParms);
String appSig = D2LSigner.base64URLSig(mAppKey, signatureBaseString);
String userSig = D2LSigner.base64URLSig(mUserKey, signatureBaseString);
if ((appSig == null) || (userSig == null)) {
return null;
}
String scheme = mEncryptOperations?ENCRYPED_SCHEME:PLAIN_SCHEME;
Uri.Builder b = new Uri.Builder();
b.scheme(scheme);
b.authority(mHostName);
b.path(path);
b.appendQueryParameter(APP_ID_QUERY_NAME, mAppID);
b.appendQueryParameter(APP_SIG_QUERY_NAME, appSig);
b.appendQueryParameter(USER_ID_QUERY_NAME, mUserID);
b.appendQueryParameter(USER_SIG_QUERY_NAME, userSig);
b.appendQueryParameter(TIMESTAMP_QUERY_NAME, timestampObjectSeconds.toString());
Uri securedURI = b.build();
return securedURI;
}
Also, you need to sign the first URL you use for logging in, but only with the application key (because you haven't yet established a user context). It uses a different base string (to protect the URL that is used during auth):
String signature = D2LSigner.base64URLSig(mAppKey, resultURLString);
BasicNameValuePair appID = new BasicNameValuePair(APP_ID_NAME, mAppID);
BasicNameValuePair appSig = new BasicNameValuePair(APP_SIG_NAME, signature);
BasicNameValuePair callbackURL = new BasicNameValuePair(CALLBACK_NAME, resultURLString);