1

特定の Core Data 属性を暗号化するために NSValueTranformer を使用しています。NSManagedObject に応じて別の暗号化キーを使用できるようにする必要があることを除いて、これはすべて正常に機能します。変圧器クラス内からこのエンティティにアクセスできる方法はありますか?

使用例は、異なる NSManagedObject エンティティにアクセスできる異なるパスワードを持つ複数のユーザーがいる場合です。すべてのオブジェクトに同じ暗号化キーを使用すると、誰かが SQL データベースでそれらのオブジェクトの所有者を再割り当てするだけで、復号化することができます。

これについて最善の方法についてのアイデアはありますか?

編集:iOSでこれを行っていることに言及する必要があります。

4

3 に答える 3

1

状態 (つまり、暗号化キー) を持つサブクラスのカスタム インスタンスを作成し、キーを使用してオプション ディクショナリにNSValueTransformer渡すことができます。-bind:toObject:withKeyPath: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-20T12:09:34.163 に答える
1

三度目の魅力?ディスクに移動するときにのみ変換するという要件に対処できるかどうかを確認させてください. これは、他の 2 つのアプローチのハイブリッドと考えてください。

@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-20T13:03:31.077 に答える