/// <inheritdoc/> public byte[] Encrypt( byte[] message, SecureString password, KeyDerivationCostType costType, string encryptorName, string kdfName = Pbkdf2.CryptoKdfName, string compression = null) { if (message == null) { throw new ArgumentNullException(nameof(message)); } ValidatePassword(password); if (_randomSource == null) { throw new ArgumentNullException(nameof(_randomSource)); } if (string.IsNullOrWhiteSpace(encryptorName)) { encryptorName = BouncyCastleXChaCha20.CryptoAlgorithmName; } if (string.IsNullOrWhiteSpace(kdfName)) { encryptorName = Pbkdf2.CryptoKdfName; } ISymmetricEncryptionAlgorithm encryptor = new SymmetricEncryptionAlgorithmFactory().CreateAlgorithm(encryptorName); IKeyDerivationFunction kdf = new KeyDerivationFactory().CreateKdf(kdfName); // Prepare header CryptoHeader header = new CryptoHeader(); header.PackageName = PackageName; header.AlgorithmName = encryptor.Name; header.Nonce = _randomSource.GetRandomBytes(encryptor.ExpectedNonceSize); header.KdfName = kdf.Name; header.Salt = _randomSource.GetRandomBytes(kdf.ExpectedSaltSizeBytes); int cost = kdf.RecommendedCost(costType); header.Cost = cost.ToString(); header.Compression = compression; try { if (string.Equals(CompressionGzip, header.Compression, StringComparison.InvariantCultureIgnoreCase)) { message = CompressUtils.Compress(message); } byte[] key = kdf.DeriveKeyFromPassword(password, encryptor.ExpectedKeySize, header.Salt, cost); byte[] cipher = encryptor.Encrypt(message, key, header.Nonce); return(CryptoHeaderPacker.PackHeaderAndCypher(header, cipher)); } catch (Exception ex) { throw new CryptoException("An unexpected error occured, while encrypting the message.", ex); } }
/// <summary> /// Encrypts a message with a user password, and adds a header containing all information /// necessary for the decryption (algorithm, nonce, salt, ...). /// </summary> /// <param name="message">Plain text message to encrypt.</param> /// <param name="password">Password to use for encryption, minimum length is 7 characters.</param> /// <param name="costType">The cost type to use for encryption.</param> /// <param name="randomSource">A cryptographically safe random source.</param> /// <param name="encryptorName">The name of an encryption algorithm which shall be used to /// do the encryption.</param> /// <param name="kdfName">The name of a key derivation function, which can convert the /// password to a key.</param> /// <returns>A binary array containing the cipher.</returns> public byte[] Encrypt( byte[] message, string password, KeyDerivationCostType costType, ICryptoRandomSource randomSource, string encryptorName, string kdfName = Pbkdf2.CryptoKdfName) { if (message == null) { throw new ArgumentNullException("message"); } ValidatePassword(password); if (randomSource == null) { throw new ArgumentNullException("randomSource"); } if (string.IsNullOrWhiteSpace(encryptorName)) { encryptorName = BouncyCastleAesGcm.CryptoAlgorithmName; } if (string.IsNullOrWhiteSpace(kdfName)) { encryptorName = Pbkdf2.CryptoKdfName; } ISymmetricEncryptionAlgorithm encryptor = new SymmetricEncryptionAlgorithmFactory().CreateAlgorithm(encryptorName); IKeyDerivationFunction kdf = new KeyDerivationFactory().CreateKdf(kdfName); // Prepare header CryptoHeader header = new CryptoHeader(); header.AppName = _appName; header.AlgorithmName = encryptor.Name; header.Nonce = randomSource.GetRandomBytes(encryptor.ExpectedNonceSize); header.KdfName = kdf.Name; header.Salt = randomSource.GetRandomBytes(kdf.ExpectedSaltSizeBytes); int cost = kdf.RecommendedCost(costType); header.Cost = cost.ToString(); try { byte[] key = kdf.DeriveKeyFromPassword(password, encryptor.ExpectedKeySize, header.Salt, cost); byte[] cipher = encryptor.Encrypt(message, key, header.Nonce); return(CryptoHeaderPacker.PackHeaderAndCypher(header, cipher)); } catch (Exception ex) { throw new CryptoException("An unexpected error occured, while encrypting the message.", ex); } }
/// <summary> /// Generates a random string of a given length, using the random source of /// the operating system.The string contains only safe characters of this /// alphabet: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz /// </summary> /// <param name="length">Number of characters the string should have.</param> /// <param name="randomSource">Random generator.</param> /// <returns>New randomly generated string.</returns> public static string GenerateRandomBase62String(int length, ICryptoRandomSource randomSource) { if (length < 0) { throw new ArgumentOutOfRangeException("length"); } StringBuilder result = new StringBuilder(); int remainingLength = length; do { // We take advantage of the fast base64 encoding int binaryLength = (int)((remainingLength * 3.0 / 4.0) + 1.0); byte[] randomBytes = randomSource.GetRandomBytes(binaryLength); string base64String = Convert.ToBase64String(randomBytes); // Remove invalid characters result.Append(base64String); result.Replace("+", string.Empty); result.Replace("/", string.Empty); result.Replace("=", string.Empty); // If too many characters have been removed, we repeat the procedure remainingLength = length - result.Length; }while (remainingLength > 0); result.Length = length; return(result.ToString()); }
public void MeasureTime() { IKeyDerivationFunction kdf = new Pbkdf2(); ICryptoRandomSource randomSource = CommonMocksAndStubs.CryptoRandomService(); SecureString password = CryptoUtils.StringToSecureString("candidate"); byte[] salt = randomSource.GetRandomBytes(kdf.ExpectedSaltSizeBytes); int iterations = 10000; Stopwatch watch = new Stopwatch(); watch.Start(); kdf.DeriveKeyFromPassword(password, 32, salt, iterations); watch.Stop(); Console.WriteLine("Pbkdf2 time for {0} iterations: {1}ms", iterations, watch.ElapsedMilliseconds); }