diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln
index d6518a0af3..7cae777e00 100644
--- a/framework/Volo.Abp.sln
+++ b/framework/Volo.Abp.sln
@@ -212,7 +212,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.PostgreSql", "src\Volo.Abp.EntityFrameworkCore.PostgreSql\Volo.Abp.EntityFrameworkCore.PostgreSql.csproj", "{882E82F1-1A57-4BB9-B126-4CBF700C8F0C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Localization.Abstractions", "src\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj", "{20513A4E-FAC7-4106-8976-5D79A3BDFED1}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Localization.Abstractions", "src\Volo.Abp.Localization.Abstractions\Volo.Abp.Localization.Abstractions.csproj", "{20513A4E-FAC7-4106-8976-5D79A3BDFED1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Security.Tests", "test\Volo.Abp.Security.Tests\Volo.Abp.Security.Tests.csproj", "{7CE07034-7E02-4C78-B981-F1039412CA5E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -604,6 +606,10 @@ Global
{20513A4E-FAC7-4106-8976-5D79A3BDFED1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20513A4E-FAC7-4106-8976-5D79A3BDFED1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20513A4E-FAC7-4106-8976-5D79A3BDFED1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7CE07034-7E02-4C78-B981-F1039412CA5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7CE07034-7E02-4C78-B981-F1039412CA5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7CE07034-7E02-4C78-B981-F1039412CA5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7CE07034-7E02-4C78-B981-F1039412CA5E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -707,6 +713,7 @@ Global
{77A621CF-9562-411B-A707-C7C02CC3B8FA} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{882E82F1-1A57-4BB9-B126-4CBF700C8F0C} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{20513A4E-FAC7-4106-8976-5D79A3BDFED1} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
+ {7CE07034-7E02-4C78-B981-F1039412CA5E} = {447C8A77-E5F0-4538-8687-7383196D04EA}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/IStringEncryptionService.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/IStringEncryptionService.cs
new file mode 100644
index 0000000000..bad4e33251
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/IStringEncryptionService.cs
@@ -0,0 +1,31 @@
+using JetBrains.Annotations;
+
+namespace Volo.Abp.Security.Encryption
+{
+ ///
+ /// Can be used to simply encrypt/decrypt texts.
+ /// Use to configure default values.
+ ///
+ public interface IStringEncryptionService
+ {
+ ///
+ /// Encrypts a text.
+ ///
+ /// The text in plain format
+ /// A phrase to use as the encryption key (optional, uses default if not provided)
+ /// Salt value (optional, uses default if not provided)
+ /// Enrypted text
+ [CanBeNull]
+ string Encrypt([CanBeNull] string plainText, string passPhrase = null, byte[] salt = null);
+
+ ///
+ /// Decrypts a text that is encrypted by the method.
+ ///
+ /// The text in encrypted format
+ /// A phrase to use as the encryption key (optional, uses default if not provided)
+ /// Salt value (optional, uses default if not provided)
+ /// Decrypted text
+ [CanBeNull]
+ string Decrypt([CanBeNull] string cipherText, string passPhrase = null, byte[] salt = null);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionOptions.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionOptions.cs
new file mode 100644
index 0000000000..91768d37e8
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionOptions.cs
@@ -0,0 +1,44 @@
+using System.Text;
+
+namespace Volo.Abp.Security.Encryption
+{
+ ///
+ /// Options used by .
+ ///
+ public class StringEncryptionOptions
+ {
+ ///
+ /// This constant is used to determine the keysize of the encryption algorithm.
+ /// Default value: 256.
+ ///
+ public int Keysize { get; set; }
+
+ ///
+ /// Default password to encrypt/decrypt texts.
+ /// It's recommented to set to another value for security.
+ /// Default value: "gsKnGZ041HLL4IM8"
+ ///
+ public string DefaultPassPhrase { get; set; }
+
+ ///
+ /// This constant string is used as a "salt" value for the PasswordDeriveBytes function calls.
+ /// This size of the IV (in bytes) must = (keysize / 8). Default keysize is 256, so the IV must be
+ /// 32 bytes long. Using a 16 character string here gives us 32 bytes when converted to a byte array.
+ /// Default value: Encoding.ASCII.GetBytes("jkE49230Tf093b42")
+ ///
+ public byte[] InitVectorBytes { get; set; }
+
+ ///
+ /// Default value: Encoding.ASCII.GetBytes("hgt!16kl")
+ ///
+ public byte[] DefaultSalt { get; set; }
+
+ public StringEncryptionOptions()
+ {
+ Keysize = 256;
+ DefaultPassPhrase = "gsKnGZ041HLL4IM8";
+ InitVectorBytes = Encoding.ASCII.GetBytes("jkE49230Tf093b42");
+ DefaultSalt = Encoding.ASCII.GetBytes("hgt!16kl");
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionService.cs b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionService.cs
new file mode 100644
index 0000000000..1d235d1d84
--- /dev/null
+++ b/framework/src/Volo.Abp.Security/Volo/Abp/Security/Encryption/StringEncryptionService.cs
@@ -0,0 +1,103 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using Microsoft.Extensions.Options;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.Security.Encryption
+{
+ ///
+ /// Can be used to simply encrypt/decrypt texts.
+ ///
+ public class StringEncryptionService : IStringEncryptionService, ITransientDependency
+ {
+ protected StringEncryptionOptions Options { get; }
+
+ public StringEncryptionService(IOptions options)
+ {
+ Options = options.Value;
+ }
+
+ public virtual string Encrypt(string plainText, string passPhrase = null, byte[] salt = null)
+ {
+ if (plainText == null)
+ {
+ return null;
+ }
+
+ if (passPhrase == null)
+ {
+ passPhrase = Options.DefaultPassPhrase;
+ }
+
+ if (salt == null)
+ {
+ salt = Options.DefaultSalt;
+ }
+
+ var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
+ using (var password = new Rfc2898DeriveBytes(passPhrase, salt))
+ {
+ var keyBytes = password.GetBytes(Options.Keysize / 8);
+ using (var symmetricKey = Aes.Create())
+ {
+ symmetricKey.Mode = CipherMode.CBC;
+ using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, Options.InitVectorBytes))
+ {
+ using (var memoryStream = new MemoryStream())
+ {
+ using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
+ {
+ cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
+ cryptoStream.FlushFinalBlock();
+ var cipherTextBytes = memoryStream.ToArray();
+ return Convert.ToBase64String(cipherTextBytes);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public virtual string Decrypt(string cipherText, string passPhrase = null, byte[] salt = null)
+ {
+ if (string.IsNullOrEmpty(cipherText))
+ {
+ return null;
+ }
+
+ if (passPhrase == null)
+ {
+ passPhrase = Options.DefaultPassPhrase;
+ }
+
+ if (salt == null)
+ {
+ salt = Options.DefaultSalt;
+ }
+
+ var cipherTextBytes = Convert.FromBase64String(cipherText);
+ using (var password = new Rfc2898DeriveBytes(passPhrase, salt))
+ {
+ var keyBytes = password.GetBytes(Options.Keysize / 8);
+ using (var symmetricKey = Aes.Create())
+ {
+ symmetricKey.Mode = CipherMode.CBC;
+ using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, Options.InitVectorBytes))
+ {
+ using (var memoryStream = new MemoryStream(cipherTextBytes))
+ {
+ using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
+ {
+ var plainTextBytes = new byte[cipherTextBytes.Length];
+ var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
+ return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj b/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj
new file mode 100644
index 0000000000..e2f96d8223
--- /dev/null
+++ b/framework/test/Volo.Abp.Security.Tests/Volo.Abp.Security.Tests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ netcoreapp2.1
+ Volo.Abp.Security.Tests
+ Volo.Abp.Security.Tests
+ true
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
diff --git a/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/AbpSecurityTestModule.cs b/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/AbpSecurityTestModule.cs
new file mode 100644
index 0000000000..b71a0a214a
--- /dev/null
+++ b/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/AbpSecurityTestModule.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.Security
+{
+ [DependsOn(
+ typeof(AbpSecurityModule),
+ typeof(AbpTestBaseModule)
+ )]
+ public class AbpSecurityTestModule : AbpModule
+ {
+
+ }
+}
diff --git a/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Encryption/StringEncryptionService_Tests.cs b/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Encryption/StringEncryptionService_Tests.cs
new file mode 100644
index 0000000000..455e0863bb
--- /dev/null
+++ b/framework/test/Volo.Abp.Security.Tests/Volo/Abp/Security/Encryption/StringEncryptionService_Tests.cs
@@ -0,0 +1,26 @@
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.Security.Encryption
+{
+ public class StringEncryptionService_Tests : AbpIntegratedTest
+ {
+ private readonly IStringEncryptionService _stringEncryptionService;
+
+ public StringEncryptionService_Tests()
+ {
+ _stringEncryptionService = GetRequiredService();
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData("")]
+ [InlineData("This is a plain text!")]
+ public void Should_Enrypt_And_Decrpyt_With_Default_Options(string plainText)
+ {
+ _stringEncryptionService
+ .Decrypt(_stringEncryptionService.Encrypt(plainText))
+ .ShouldBe(plainText);
+ }
+ }
+}