Xamarin.iOS / Akavache - 带有自定义 EncryptionProvider 的加密缓存
Xamarin.iOS / Akavache - Encrypted Cache with custom EncryptionProvider
我有一个 Xamarin.iOS 应用程序使用 Akavache 来缓存数据并减少对服务器不必要的数据请求的数量。使用 BlobCache.LocalMachine
一切都如预期的那样运行良好,喜欢它。
现在我想确保静态数据已加密。通过研究这个和在线阅读文档,我发现默认实现实际上并没有加密 iOS 上的缓存,我实际上需要提供 IEncryptionProvider
的自定义实现。
我关注了这篇文章:http://kent-boogaart.com/blog/password-protected-encryption-provider-for-akavache
从该线程添加输入:https://github.com/akavache/Akavache/issues/190
最后实现了这个:
public interface IPasswordProtectedEncryptionProvider : IEncryptionProvider
{
void SetPassword(string password);
}
public sealed class MyEncryptionProvider : IPasswordProtectedEncryptionProvider
{
static readonly byte[] salt = Encoding.ASCII.GetBytes("dVBZMQWyFRcJOIas");
readonly IScheduler scheduler;
readonly SymmetricAlgorithm symmetricAlgorithm;
ICryptoTransform decryptTransform;
ICryptoTransform encryptTransform;
public MyEncryptionProvider()
{
scheduler = BlobCache.TaskpoolScheduler ?? throw new ArgumentNullException(nameof(scheduler), "Scheduler instance is null");
symmetricAlgorithm = new RijndaelManaged();
var securePassword = "kadhaskdhsakhaskjdhaskjdhaskdjashdkjahkfghkjhew";
SetPassword(securePassword);
}
public void SetPassword(string password)
{
if(password == null)
throw new ArgumentNullException(nameof(password), "password can't be null");
var derived = new Rfc2898DeriveBytes(password, salt);
var bytesForKey = symmetricAlgorithm.KeySize / 8;
var bytesForIV = symmetricAlgorithm.BlockSize / 8;
symmetricAlgorithm.Key = derived.GetBytes(bytesForKey);
symmetricAlgorithm.IV = derived.GetBytes(bytesForIV);
decryptTransform = symmetricAlgorithm.CreateDecryptor(symmetricAlgorithm.Key, symmetricAlgorithm.IV);
encryptTransform = symmetricAlgorithm.CreateEncryptor(symmetricAlgorithm.Key, symmetricAlgorithm.IV);
}
public IObservable<byte[]> DecryptBlock(byte[] block)
{
if (block == null)
{
throw new ArgumentNullException(nameof(block), "block can't be null");
}
if (decryptTransform == null)
{
return Observable.Throw<byte[]>(new InvalidOperationException("You must call SetPassword first."));
}
return Observable.Start(() => InMemoryTransform(block, decryptTransform), scheduler);
}
public IObservable<byte[]> EncryptBlock(byte[] block)
{
if (block == null)
{
throw new ArgumentNullException(nameof(block), "block can't be null");
}
if (encryptTransform == null)
{
return Observable.Throw<byte[]>(new InvalidOperationException("You must call SetPassword first."));
}
return Observable.Start(() => InMemoryTransform(block, encryptTransform), scheduler);
}
static byte[] InMemoryTransform(byte[] bytesToTransform, ICryptoTransform transform)
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
cryptoStream.Write(bytesToTransform, 0, bytesToTransform.Length);
}
return memoryStream.ToArray();
}
}
}
我这样注册这个实现:
Locator.CurrentMutable.RegisterConstant(new MyEncryptionProvider(), typeof(IEncryptionProvider));
在调用 BlobCache.Secure
之前
当我在该安全缓存上使用 .GetOrFetchObject
时,我看到 EncryptBlock
和 DecryptBlock
被调用。到目前为止,一切都很好。
我 运行 遇到的问题是在尝试反序列化时失败 Newtonsoft
:
{Newtonsoft.Json.JsonReaderException: Read past end of current container context. Path ''.
at Newtonsoft.Json.Bson.BsonReader.ReadNormal () [0x0013c] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.Bson.BsonReader.Read () [0x00033] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.JsonReader.ReadAndAssert () [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x000b6] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0006d] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00053] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.JsonSerializer.Deserialize[T] (Newtonsoft.Json.JsonReader reader) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0
at Akavache.Sqlite3.SQLitePersistentBlobCache.DeserializeObject[T] (System.Byte[] data) [0x00074] in <67aced6c5c1a4c15b03e120d7300429d>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.12.0.20/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:151
at System.Reactive.PlatformServices.ExceptionServicesImpl.Rethrow (System.Exception exception) [0x00006] in <427ee2007f4d40bb9d3f1fcd73e9b750>:0
at System.Reactive.ExceptionHelpers.ThrowIfNotNull (System.Exception exception) [0x0000d] in <ab76b1b1678341d69f8fc2c1f68d0bf5>:0
at System.Reactive.Subjects.AsyncSubject`1[T].GetResult () [0x00039]
当使用 BlobCache.LocalMachine
缓存相同的对象时,相同的数据结构成功存储如下:
[
{
"Name": "John Smith",
"Date": "2017-04-03T20:00:00-04:00",
"Number1": -8820768.6192349959,
"Number2": -25540081.8275,
"Number3": -0.0045255076670528034,
"Number4": 0.0457761358483606,
"RelatedObjects": [],
"LastUpdateTime": "2017-04-04T17:36:06.247-04:00"
}
]
我在这里错过了什么?非常沮丧,真的希望有人看到我没有看到的东西。感谢您的帮助。
借助此 post 中的示例:
我能够解决这个问题,这里的示例解决方案中显示了一个工作示例:https://github.com/dmitrysamuylov/xamarin-ios-akavache-secure-example
希望对以后的人有所帮助
我有一个 Xamarin.iOS 应用程序使用 Akavache 来缓存数据并减少对服务器不必要的数据请求的数量。使用 BlobCache.LocalMachine
一切都如预期的那样运行良好,喜欢它。
现在我想确保静态数据已加密。通过研究这个和在线阅读文档,我发现默认实现实际上并没有加密 iOS 上的缓存,我实际上需要提供 IEncryptionProvider
的自定义实现。
我关注了这篇文章:http://kent-boogaart.com/blog/password-protected-encryption-provider-for-akavache
从该线程添加输入:https://github.com/akavache/Akavache/issues/190
最后实现了这个:
public interface IPasswordProtectedEncryptionProvider : IEncryptionProvider
{
void SetPassword(string password);
}
public sealed class MyEncryptionProvider : IPasswordProtectedEncryptionProvider
{
static readonly byte[] salt = Encoding.ASCII.GetBytes("dVBZMQWyFRcJOIas");
readonly IScheduler scheduler;
readonly SymmetricAlgorithm symmetricAlgorithm;
ICryptoTransform decryptTransform;
ICryptoTransform encryptTransform;
public MyEncryptionProvider()
{
scheduler = BlobCache.TaskpoolScheduler ?? throw new ArgumentNullException(nameof(scheduler), "Scheduler instance is null");
symmetricAlgorithm = new RijndaelManaged();
var securePassword = "kadhaskdhsakhaskjdhaskjdhaskdjashdkjahkfghkjhew";
SetPassword(securePassword);
}
public void SetPassword(string password)
{
if(password == null)
throw new ArgumentNullException(nameof(password), "password can't be null");
var derived = new Rfc2898DeriveBytes(password, salt);
var bytesForKey = symmetricAlgorithm.KeySize / 8;
var bytesForIV = symmetricAlgorithm.BlockSize / 8;
symmetricAlgorithm.Key = derived.GetBytes(bytesForKey);
symmetricAlgorithm.IV = derived.GetBytes(bytesForIV);
decryptTransform = symmetricAlgorithm.CreateDecryptor(symmetricAlgorithm.Key, symmetricAlgorithm.IV);
encryptTransform = symmetricAlgorithm.CreateEncryptor(symmetricAlgorithm.Key, symmetricAlgorithm.IV);
}
public IObservable<byte[]> DecryptBlock(byte[] block)
{
if (block == null)
{
throw new ArgumentNullException(nameof(block), "block can't be null");
}
if (decryptTransform == null)
{
return Observable.Throw<byte[]>(new InvalidOperationException("You must call SetPassword first."));
}
return Observable.Start(() => InMemoryTransform(block, decryptTransform), scheduler);
}
public IObservable<byte[]> EncryptBlock(byte[] block)
{
if (block == null)
{
throw new ArgumentNullException(nameof(block), "block can't be null");
}
if (encryptTransform == null)
{
return Observable.Throw<byte[]>(new InvalidOperationException("You must call SetPassword first."));
}
return Observable.Start(() => InMemoryTransform(block, encryptTransform), scheduler);
}
static byte[] InMemoryTransform(byte[] bytesToTransform, ICryptoTransform transform)
{
using (var memoryStream = new MemoryStream())
{
using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
{
cryptoStream.Write(bytesToTransform, 0, bytesToTransform.Length);
}
return memoryStream.ToArray();
}
}
}
我这样注册这个实现:
Locator.CurrentMutable.RegisterConstant(new MyEncryptionProvider(), typeof(IEncryptionProvider));
在调用 BlobCache.Secure
当我在该安全缓存上使用 .GetOrFetchObject
时,我看到 EncryptBlock
和 DecryptBlock
被调用。到目前为止,一切都很好。
我 运行 遇到的问题是在尝试反序列化时失败 Newtonsoft
:
{Newtonsoft.Json.JsonReaderException: Read past end of current container context. Path ''.
at Newtonsoft.Json.Bson.BsonReader.ReadNormal () [0x0013c] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.Bson.BsonReader.Read () [0x00033] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.JsonReader.ReadAndAssert () [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x000b6] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0006d] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00053] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0
at Newtonsoft.Json.JsonSerializer.Deserialize[T] (Newtonsoft.Json.JsonReader reader) [0x00000] in <c19705166c7c4a608e182e859c4de6d2>:0
at Akavache.Sqlite3.SQLitePersistentBlobCache.DeserializeObject[T] (System.Byte[] data) [0x00074] in <67aced6c5c1a4c15b03e120d7300429d>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/10.12.0.20/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:151
at System.Reactive.PlatformServices.ExceptionServicesImpl.Rethrow (System.Exception exception) [0x00006] in <427ee2007f4d40bb9d3f1fcd73e9b750>:0
at System.Reactive.ExceptionHelpers.ThrowIfNotNull (System.Exception exception) [0x0000d] in <ab76b1b1678341d69f8fc2c1f68d0bf5>:0
at System.Reactive.Subjects.AsyncSubject`1[T].GetResult () [0x00039]
当使用 BlobCache.LocalMachine
缓存相同的对象时,相同的数据结构成功存储如下:
[
{
"Name": "John Smith",
"Date": "2017-04-03T20:00:00-04:00",
"Number1": -8820768.6192349959,
"Number2": -25540081.8275,
"Number3": -0.0045255076670528034,
"Number4": 0.0457761358483606,
"RelatedObjects": [],
"LastUpdateTime": "2017-04-04T17:36:06.247-04:00"
}
]
我在这里错过了什么?非常沮丧,真的希望有人看到我没有看到的东西。感谢您的帮助。
借助此 post 中的示例:
我能够解决这个问题,这里的示例解决方案中显示了一个工作示例:https://github.com/dmitrysamuylov/xamarin-ios-akavache-secure-example
希望对以后的人有所帮助