bykovme/nswallet

View on GitHub
src/NSWallet/NSWallet.Shared/Security/SecurityStandard.cs

Summary

Maintainability
C
1 day
Test Coverage
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using NSWallet.Shared.Helpers.Logs.AppLog;

namespace NSWallet.Shared
{
    public static partial class Security
    {
        const int KEY_LENGTH = 32; // AES-256

        static byte[] prepareKey(string password, string hash, int reEncryptionCount)
        {
            while (password.Length < KEY_LENGTH)
            {
                password += password;
            }

            var key = password.Substring(0, KEY_LENGTH);

            if (string.IsNullOrEmpty(hash))
            {
                key = GetReEncryptedPassword(key, reEncryptionCount);
            }
            else if (reEncryptionCount > 0)
            {
                key = hash;
            }

            var keyFull = Encoding.UTF8.GetBytes(key);
            byte[] keyFinal = new byte[KEY_LENGTH];
            Array.Copy(keyFull, keyFinal, KEY_LENGTH);

            return keyFinal;
        }

        public static byte[] EncryptStringAES(string plainText, string password, int reEncryptionCount, string hash, out bool ok)
        {
            ok = false;
            byte[] bytes;

            try
            {
                var key = prepareKey(password, hash, reEncryptionCount);

                var md5str = CalcMD5(plainText);
                var fullStr = md5str + plainText;
                var data = Encoding.UTF8.GetBytes(fullStr);

                using (MemoryStream ms = new MemoryStream())
                {
                    AesManaged myAes = new AesManaged();
                    myAes.Mode = CipherMode.CBC;
                    myAes.Padding = PaddingMode.PKCS7;
                    myAes.Key = key;
                    myAes.IV = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
                    using (var cs = new CryptoStream(ms, myAes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(data, 0, data.Length);
                        cs.Close();
                    }
                    bytes = ms.ToArray();
                }
            } catch(Exception ex) {
                log(ex.Message, nameof(EncryptStringAES));
                return null;
            }


            ok = true;
            return bytes;
        }

        public static string DecryptStringAES(byte[] encryptedData, string password, int reEncryptionCount, string hash, out bool ok)
        {
            ok = false;

            var key = prepareKey(password, hash, reEncryptionCount);
            byte[] decryptedData;

            try
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    using (AesManaged myAes = new AesManaged())
                    {
                        myAes.Mode = CipherMode.CBC;
                        myAes.Padding = PaddingMode.PKCS7;
                        myAes.Key = key;
                        myAes.IV = new byte[16] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };


                        using (var cs = new CryptoStream(ms, myAes.CreateDecryptor(), CryptoStreamMode.Write))
                        {
                            cs.Write(encryptedData, 0, encryptedData.Length);
                            cs.Close();
                        }
                        decryptedData = ms.ToArray();
                    }
                }

                /*
                ISymmetricKeyAlgorithmProvider aes = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
                ICryptographicKey symetricKey = aes.CreateSymmetricKey(key);
                var bytes = WinRTCrypto.CryptographicEngine.Decrypt(symetricKey, encryptedData);
                */
                var resultText = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);

                if (resultText.Length < KEY_LENGTH)
                    return "";

                var md5 = resultText.Substring(0, KEY_LENGTH);
                var fullStr = resultText.Substring(KEY_LENGTH);
                if (md5 == CalcMD5(fullStr))
                {
                    ok = true;
                    return fullStr;
                }
            }
            catch(Exception ex)
            {
                log(ex.Message, nameof(DecryptStringAES));
                ok = false;
            }

            // FIXME: remove as soon as possible
            // This is workaround created to avoid decryption bug in old iOS app when first byte in non ASCII
            // chars was replaced with /x0
            string fullStr2 = DecryptStringAES_IOS(encryptedData, password, reEncryptionCount, hash, out bool ok2);
            if (ok2) {
                ok = ok2;
                return fullStr2;
            }
            // End of workaround


            return "";
        }

        public static string GetReEncryptedPassword(string password, int reEncryptionCount)
        {
            var retPass = password;
            for (int i = 0; i < reEncryptionCount; i++)
            {
                retPass = CalcMD5(retPass);
            }

            return retPass;
        }

        public static string CalcMD5(string password)
        {
            MD5 md5 = MD5.Create();
            //var hashProvider = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(HashAlgorithm.Md5);
            var inputBytes = Encoding.UTF8.GetBytes(password);
            var hash = md5.ComputeHash(inputBytes);
            //var hash = hashProvider.HashData(inputBytes);

            var sb = new StringBuilder();

            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("x2"));
            }
            return sb.ToString();
        }

        const string lowerLetters = "qwertyuiopasdfghjklzxcvbnm";
        const string upperLetters = "QWERTYUIOPASDFGHJKLZXCVBNM";
        const string digitsSymbols = "1234567890";
        const string specialSymbols = "!@#$%^&*()_+-={}[];:|,.<>?~";
        const string allSymbols = lowerLetters + upperLetters + digitsSymbols + specialSymbols;

        public static string GeneratePassword(bool lowerCase, bool upperCase, bool digits, bool special, int len)
        {
            Random rand = new Random(DateTime.Now.Millisecond);

            string finalSymbols = "";
            if (lowerCase)
                finalSymbols += lowerLetters + lowerLetters+ lowerLetters;

            if (upperCase)
                finalSymbols += upperLetters + upperLetters+ upperLetters;

            if (digits)
                finalSymbols += digitsSymbols + digitsSymbols;

            if (special)
                finalSymbols += specialSymbols;

            var generatedPassword = "";
            for (int i = 0; i < len; i++)
            {
                generatedPassword += finalSymbols.Substring(rand.Next(0, finalSymbols.Length - 1), 1);
            }

            return generatedPassword;

        }

        public static string GenerateCleverPassword(string passwordPattern)
        {
            Random rand = new Random(DateTime.Now.Millisecond);

            var generatedPassword = "";
            for (int i = 0; i < passwordPattern.Length; i++)
            {
                var symb = passwordPattern.Substring(i, 1);
                if (lowerLetters.Contains(symb))
                {
                    generatedPassword += lowerLetters.Substring(rand.Next(0, lowerLetters.Length - 1), 1);
                    continue;
                }
                if (upperLetters.Contains(symb))
                {
                    generatedPassword += upperLetters.Substring(rand.Next(0, upperLetters.Length - 1), 1);
                    continue;
                }
                if (digitsSymbols.Contains(symb))
                {
                    generatedPassword += digitsSymbols.Substring(rand.Next(0, digitsSymbols.Length - 1), 1);
                    continue;
                }
                if (specialSymbols.Contains(symb))
                {
                    generatedPassword += specialSymbols.Substring(rand.Next(0, specialSymbols.Length - 1), 1);
                    continue;
                }
                generatedPassword += allSymbols.Substring(rand.Next(0, allSymbols.Length - 1), 1);
            }
            return generatedPassword;
        }

        static void log(string message, string method = null)
        {
            AppLogs.Log(message, method, nameof(Security) + " (SecurityStandard)");
        }
    }
}