using System.Text; using System.Threading.Tasks; namespace System.Security { /// /// 为键/值对提供简单的安全存储 /// /// /// 每个平台使用平台提供的本机API来安全存储数据: /// /// /// iOS:数据存储在KeyChain中。有关SecAccessible的其他信息 /// /// /// Android:加密密钥存储在密钥库中,加密数据存储在命名的共享首选项容器中 /// /// /// UWP:数据使用 DataProtectionProvider 加密并存储在命名的 ApplicationDataContainer /// /// /// Windows 10 Desktop:数据使用 DataProtectionProvider 加密并存储在仓储中 /// /// /// Other Desktop:数据使用 AES 加密并存储在仓储中 /// /// /// AspNetCore:数据存储在仓储中 /// /// /// 注意:在运行 Android 23(6.0/M)以下的 Android 设备上,KeyStore中没有AES可用。作为最佳实践,此API将生成存储在KeyStore中的RSA/ECB/PKCS7Padding密钥对(KeyStore中这些较低的API级别支持的唯一类型),用于包装运行时生成的AES密钥。这个包装好的密钥存储在首选项中 /// public partial interface IStorage { #region Public API /// /// 获取给定键的值 /// /// 键 /// 如果键不存在,则返回null async Task GetAsync(string key) { if (IsNativeSupportedBytes) { var bytesValue = await GetBytesAsync(key); if (bytesValue == null) return null; try { return Encoding.UTF8.GetString(bytesValue); } catch { return null; } } else { throw new NotImplementedException(); } } /// /// 存储给定键的值 /// /// 键 /// 值 /// Task SetAsync(string key, string? value) { if (IsNativeSupportedBytes) { var bytesValue = value == null ? null : Encoding.UTF8.GetBytes(value); return SetAsync(key, bytesValue); } else { throw new NotImplementedException(); } } /// /// 移除给定键的键/值对 /// /// 要移除的键 /// 如果删除了键值对,则返回true Task RemoveAsync(string key); /// async Task GetAsync(string key) where TValue : notnull { try { if (IsNativeSupportedBytes) { var bytesValue = await GetBytesAsync(key); if (bytesValue == null) return default; var value = Serializable.DMP(bytesValue); return value; } else { var strValue = await GetAsync(key); if (strValue == null) return default; var value = Serializable.DMPB64U(strValue); return value; } } catch (Exception e) { Log.Error(nameof(IStorage), e, "GetAsync Fail, key: {0}.", key); return default; } } /// Task SetAsync(string key, TValue value) { if (IsNativeSupportedBytes) { var bytesValue = Serializable.SMP(value); return SetAsync(key, bytesValue); } else { var strValue = Serializable.SMPB64U(value); return SetAsync(key, strValue); } } static IStorage Instance => DI.Get(); #endregion protected bool IsNativeSupportedBytes { get; } /// protected async Task GetBytesAsync(string key) { if (IsNativeSupportedBytes) { throw new NotImplementedException(); } else { var strValue = await GetAsync(key); try { return strValue.Base64UrlDecodeToByteArray_Nullable(); } catch { return null; } } } /// protected Task SetAsync(string key, byte[]? value) { if (IsNativeSupportedBytes) { throw new NotImplementedException(); } else { var strValue = value.Base64UrlEncode_Nullable(); return SetAsync(key, strValue); } } } }