我使用NSValueTranformer加密某些核心数据属性。这一切都很好,但我需要能够使用不同的加密密钥取决于NSManagedObject。无论如何,我可以从我的变压器类中访问这个实体吗?
用例是,我有多个具有不同密码的用户,可以访问不同的NSManagedObject实体。如果我对所有对象使用相同的加密密钥,就可以重新分配谁在SQL db中拥有它们,它们仍然会解密。
对这件事的最佳方式有什么想法吗?
编辑:我应该说我是用iOS来做这个的。
发布于 2013-09-20 13:03:31
第三次魅力?让我看看我能不能解决你的唯一转换时磁盘的要求。把这看作是另外两种方法的混合体。
@interface UserSession : NSObject
+ (UserSession*)currentSession;
+ (void)setCurrentSession: (UserSession*)session;
- (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key;
@property (nonatomic, readonly) NSString* userName;
@property (nonatomic, readonly) NSData* encryptionKey;
@end
@implementation UserSession
static UserSession* gCurrentSession = nil;
+ (UserSession*)currentSession
{
@synchronized(self)
{
return gCurrentSession;
}
}
+ (void)setCurrentSession: (UserSession*)userSession
{
@synchronized(self)
{
gCurrentSession = userSession;
}
}
- (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key
{
if (self = [super init])
{
_userName = [username copy];
_encryptionKey = [key copy];
}
return self;
}
- (void)dealloc
{
_userName = nil;
_encryptionKey = nil;
}
@end
@interface EncryptingValueTransformer : NSValueTransformer
@end
@implementation EncryptingValueTransformer
- (id)transformedValue:(id)value
{
UserSession* session = [UserSession currentSession];
NSAssert(session, @"No user session! Can't decrypt!");
NSData* key = session.encryptionKey;
NSData* decryptedData = Decrypt(value, key);
return decryptedData;
}
- (id)reverseTransformedValue:(id)value
{
UserSession* session = [UserSession currentSession];
NSAssert(session, @"No user session! Can't encrypt!");
NSData* key = session.encryptionKey;
NSData* encryptedData = Encrypt(value, key);
return encryptedData;
}
@end这里唯一棘手的部分是,您必须确保在创建托管对象上下文之前设置了当前的UserSession,并且直到上下文保存和释放之后才进行更改。
希望这能有所帮助。
发布于 2013-09-20 12:09:34
您可以创建具有状态(即加密密钥)的NSValueTransformer子类的自定义实例,并使用-bind:toObject:withKeyPath:options:密钥在options字典中将它们传递给NSValueTransformerBindingOption。
您将无法在IB中直接设置它,因为IB按类名引用值转换器,但您可以在代码中这样做。如果您感到非常雄心勃勃,您可以在IB中设置绑定,然后在代码中用不同的选项替换它们。
它看起来可能是这样的:
@interface EncryptingValueTransformer : NSValueTransformer
@property (nonatomic,readwrite,copy) NSData* encryptionKey;
@end
@implementation EncryptingValueTransformer
- (void)dealloc
{
_encryptionKey = nil;
}
- (id)transformedValue:(id)value
{
if (!self.encryptionKey)
return nil;
// do the transformation
return value;
}
- (id)reverseTransformedValue:(id)value
{
if (!self.encryptionKey)
return nil;
// Do the reverse transformation
return value;
}
@end
@interface MyViewController : NSViewController
@property (nonatomic, readwrite, assign) IBOutlet NSControl* controlBoundToEncryptedValue;
@end
@implementation MyViewController
// Other stuff...
- (void)loadView
{
[super loadView];
// Replace IB's value tansformer binding settings (which will be by class and not instance) with specific,
// stateful instances.
for (NSString* binding in [self.controlBoundToEncryptedValue exposedBindings])
{
NSDictionary* bindingInfo = [self.controlBoundToEncryptedValue infoForBinding: binding];
NSDictionary* options = bindingInfo[NSOptionsKey];
if ([options[NSValueTransformerNameBindingOption] isEqual: NSStringFromClass([EncryptingValueTransformer class])])
{
// Out with the old
[self.controlBoundToEncryptedValue unbind: binding];
// In with the new
NSMutableDictionary* mutableOptions = [options mutableCopy];
mutableOptions[NSValueTransformerNameBindingOption] = nil;
mutableOptions[NSValueTransformerBindingOption] = [[EncryptingValueTransformer alloc] init];
[self.controlBoundToEncryptedValue bind: binding
toObject: bindingInfo[NSObservedObjectKey]
withKeyPath: bindingInfo[NSObservedKeyPathKey]
options: mutableOptions];
}
}
}
// Assuming you're using the standard representedObject pattern, this will get set every time you want
// your view to expose new model data. This is a good place to update the encryption key in the transformers'
// state...
- (void)setRepresentedObject:(id)representedObject
{
for (NSString* binding in [self.controlBoundToEncryptedValue exposedBindings])
{
id transformer = [self.controlBoundToEncryptedValue infoForBinding: NSValueBinding][NSOptionsKey][NSValueTransformerBindingOption];
EncryptingValueTransformer* encryptingTransformer = [transformer isKindOfClass: [EncryptingValueTransformer class]] ? (EncryptingValueTransformer*)transformer : nil;
encryptingTransformer.encryptionKey = nil;
}
[super setRepresentedObject:representedObject];
// Get key from model however...
NSData* encryptionKeySpecificToThisUser = /* Whatever it is... */ nil;
for (NSString* binding in [self.controlBoundToEncryptedValue exposedBindings])
{
id transformer = [self.controlBoundToEncryptedValue infoForBinding: NSValueBinding][NSOptionsKey][NSValueTransformerBindingOption];
EncryptingValueTransformer* encryptingTransformer = [transformer isKindOfClass: [EncryptingValueTransformer class]] ? (EncryptingValueTransformer*)transformer : nil;
encryptingTransformer.encryptionKey = encryptionKeySpecificToThisUser;
}
}
// ...Other stuff
@end发布于 2013-09-20 12:57:22
好的。这让我心烦意乱所以我再想一想.我认为最简单的方法是有某种类型的“会话”对象,然后在托管对象上有一个“派生属性”。假设您有一个名为UserData的实体,该实体具有一个名为encryptedData的属性,我编写了一些代码,这些代码可能有助于说明:
@interface UserData : NSManagedObject
@property (nonatomic, retain) NSData * unencryptedData;
@end
@interface UserData () // Private
@property (nonatomic, retain) NSData * encryptedData;
@end
// These functions defined elsewhere
NSData* Encrypt(NSData* clearData, NSData* key);
NSData* Decrypt(NSData* cipherData, NSData* key);
@interface UserSession : NSObject
+ (UserSession*)currentSession;
- (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key;
@property (nonatomic, readonly) NSString* userName;
@property (nonatomic, readonly) NSData* encryptionKey;
@end
@implementation UserData
@dynamic encryptedData;
@dynamic unencryptedData;
+ (NSSet*)keyPathsForValuesAffectingUnencryptedData
{
return [NSSet setWithObject: NSStringFromSelector(@selector(encryptedData))];
}
- (NSData*)unencryptedData
{
UserSession* session = [UserSession currentSession];
if (nil == session)
return nil;
NSData* key = session.encryptionKey;
NSData* encryptedData = self.encryptedData;
NSData* decryptedData = Decrypt(encryptedData, key);
return decryptedData;
}
- (void)setUnencryptedData:(NSData *)unencryptedData
{
UserSession* session = [UserSession currentSession];
NSAssert(session, @"No user session! Can't encrypt!");
NSData* key = session.encryptionKey;
NSData* encryptedData = Encrypt(unencryptedData, key);
self.encryptedData = encryptedData;
}
@end
@implementation UserSession
static UserSession* gCurrentSession = nil;
+ (UserSession*)currentSession
{
@synchronized(self)
{
return gCurrentSession;
}
}
+ (void)setCurrentSession: (UserSession*)userSession
{
@synchronized(self)
{
gCurrentSession = userSession;
}
}
- (id)initWithUserName: (NSString*)username andEncryptionKey: (NSData*)key
{
if (self = [super init])
{
_userName = [username copy];
_encryptionKey = [key copy];
}
return self;
}
-(void)dealloc
{
_userName = nil;
_encryptionKey = nil;
}
@end这里的想法是,当给定的用户登录时,您将创建一个新的UserSession对象并调用+[UserSession setCurrentSession: [[UserSession alloc] initWithUserName: @"foo" andEncryptionKey: <whatever>]]。派生属性(unencryptedData)访问器和变量获取当前会话,并使用键将值来回转换为“实”属性。(另外,不要跳过+keyPathsForValuesAffectingUnencryptedData方法。这将告诉运行时这两个属性之间的关系,并将有助于更无缝地工作。)
https://stackoverflow.com/questions/18916338
复制相似问题