As a server, I use ASP.NET WebApi 2. As a client, a universal application on Windows 10. Everywhere NET.Framework is used. 4.6 Data is sent via http.

For encryption using PCLCrypto https://github.com/aarnott/pclcrypto

Below is a class for encryption:

public static class Crypto { public static byte[] CreateSalt(uint lengthInBytes) { return WinRTCrypto.CryptographicBuffer.GenerateRandom(lengthInBytes); } public static byte[] CreateDerivedKey(string password, byte[] salt, int keyLengthInBytes = 32, int iterations = 10000) { byte[] key = NetFxCrypto.DeriveBytes.GetBytes(password, salt, iterations, keyLengthInBytes); return key; } public static byte[] EncryptAes(string data, string password, byte[] salt) { byte[] key = CreateDerivedKey(password, salt); ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesEcbPkcs7); ICryptographicKey symetricKey = aes.CreateSymmetricKey(key); var bytes = WinRTCrypto.CryptographicEngine.Encrypt(symetricKey, Encoding.UTF8.GetBytes(data)); return bytes; } public static string DecryptAes(byte[] data, string password, byte[] salt) { byte[] key = CreateDerivedKey(password, salt); ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesEcbPkcs7); ICryptographicKey symetricKey = aes.CreateSymmetricKey(key); var bytes = WinRTCrypto.CryptographicEngine.Decrypt(symetricKey, data); return Encoding.UTF8.GetString(bytes, 0, bytes.Length); } } 

I encrypt the username and password on the client before sending:

 var salt = Crypto.CreateSalt(16); var bytes = Crypto.EncryptAes(data, pass, salt); 

I decipher on the server:

 var str = Crypto.DecryptAes(bytes, pass, salt); 

When testing in one application, everything worked. As soon as the parts were spread to the server and the client, difficulties arose in converting byte[] to string and back. used Convert.ToString() and Encoding.UTF8.GetBytes / GetString . Using these functions results in different values ​​in the byte array.

Found such an example: https://stackoverflow.com/questions/472906/converting-a-string-to-byte-array-without-using-an-encoding-byte-by-byte?lq=1

Used the first answer:

  static byte[] GetBytes(string str) { var bytes = new byte[str.Length * sizeof(char)]; Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length); return bytes; } static string GetString(byte[] bytes) { var chars = new char[bytes.Length / sizeof(char)]; Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length); return new string(chars); } 

By one machine works. But in the comments to this answer, something is said that this method will work only on one machine. There is no opportunity to check on different machines. I know about SSL, but I will not use it yet.

The salt is transmitted along with the cipher text so it is generated every time it is encrypted. Like this (new salt generation) increases the chances of being hacked by brute force on rainbow tables or something like that.

Tell me please, within the same version of net.framework, but on different devices will this code work? Will the transformations take place correctly with strings? And maybe there are more universal solutions?

  • 7
    Hm I, of course, not special in cryptography, but why transfer salt from client to server? Let it itself lies all the time on the server. - VladD
  • one
    In the question you are quoting, the conversion of a string into a set of bytes without encoding is discussed (that is, in the default encoding of this machine, since the string can not be converted into bytes without specifying the encoding). Encode them humanly, with encoding, there will be no problems. - VladD
  • You need to think not about the abstract "mode of transmission", but about the format of the transfer . How this array of bytes will look like. If you choose base64 format, then the necessary methods already exist in the Convert class. - Pavel Mayorov

2 answers 2

About the encoding +1 VladD. Further, it seems to me that you incorrectly build the logic of the password check. Client-Server assumes the following set (client-communication channel β€” server β€” place where passwords are stored). You generate salt on the client, then encrypt the password and transfer the salted array of bytes via http, but on the server side, do you send the password and salt to the decoder? How do they get there? Did you want to make an encrypted communication channel? As for storing passwords on the server in the database, there is a BCrypt solution https://bcrypt.codeplex.com. To organize a password check on the server, you should transfer the hash from the client and salt it (on the server) and compare it with the hash in the database (since the password is not stored in the database, but only hash and salt are stored by which you can determine its "correctness"). And salt is needed in order to eliminate hacking by already known hashes (formed from popular passwords). Good article .

  class Crypto { class User { public string Salt { get; set; } public string Hash { get; set; } } public static User GetUserByAddingToDatabse(string pass) { return new User() { Salt = BCrypt.Net.BCrypt.GenerateSalt() + "Y87^&*(", Hash = BCrypt.Net.BCrypt.HashPassword(pass, this.Salt) }; } public static bool Verify(string hash, string password) { return BCrypt.Net.BCrypt.Verify(hash, password); } } 
  • one
    In .NET there is a native way to hash passwords with salt, without attracting third-party BCrypt - Rfc2898DeriveBytes, Stackoverflow.com/a/546028/177221 - PashaPash ♦

To implement secure data transfer between the client and the server, there is encryption β€” there is a ready https. It is at times more reliable than clever custom implementations. Just enable it on the server.

To check the password without storing it in clear view - hashing - there is Rfc2898DeriveBytes . It allows you to generate a hash with salt as a single line, without the involvement of third-party libraries.