ios - 使用 rsa 私钥签署数据

标签 ios objective-c encryption rsa

我知道是圣诞节,但我有一个大问题需要解决,我正在寻找我的圣诞节奇迹...

我已经阅读了 apples 文档,其中只有关于如何从证书创建 RSA 公钥和私钥的指南。就我而言,.pem 文件中只有 RSA 私钥。所以我的问题是他的:我应该如何使用该 key 签署数据? 我不想使用 openssl。我已经试过了,但没有成功,而且我认为可以通过使用苹果 API 来使用 RSA 对数据进行签名。

这是我的 key 的样子:

-----BEGIN RSA PRIVATE KEY-----
..............................
-----END RSA PRIVATE KEY-----

这是我到目前为止所做的:

-(NSString *)signing:(NSString *)dataString {
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"PrestaMobilekey" ofType:@"pem"];
    NSData *data = [[NSData alloc]initWithContentsOfFile:filePath];

    SecKeyRef privateKey = (__bridge SecKeyRef)(data);

    uint8_t *signedHashBytes = NULL;
    // calculate private key size
    size_t signedHashBytesSize = SecKeyGetBlockSize(privateKey);

    // create space to put signature
    signedHashBytes = (uint8_t *)malloc(signedHashBytesSize * sizeof(uint8_t));
    memset((void *)signedHashBytes, 0x0, signedHashBytesSize);

    OSStatus status = NULL;

    // sign data
    status = SecKeyRawSign(privateKey,
                           kSecPaddingPKCS1SHA1,
                           [[[dataString dataUsingEncoding:NSUTF8StringEncoding] SHA1] bytes],
                           CC_SHA1_DIGEST_LENGTH,
                           signedHashBytes,
                           &signedHashBytesSize);

    if (privateKey) {
        CFRelease(privateKey);
    }

    // get signature hash
    NSData *signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize];

    // release created space
    if (signedHashBytes) {
        free(signedHashBytes);
    }

    if (status != errSecSuccess) {
        return @"";
    }

    // return Base64 encoded signature string
    return [Base64 encode:signedHash];
}

我真的希望有人能帮助我,提供一些好的信息和答案。

谢谢。

最佳答案

您不需要使用 OpenSSL。您可以通过一些调整使用您的方法对数据进行签名。我认为您不能简单地桥接并将NSData 对象转换为SecKeyRef。您很可能需要先将其保存到钥匙串(keychain)。

你可以用这个方法做到这一点:

- (SecKeyRef)saveKeyToKeychain:(NSData *)key keySize:(NSUInteger)keySize private:(BOOL)isPrivate {
    OSStatus sanityCheck = noErr;
    NSData *tag;
    id keyClass;
    if (isPrivate) {
            tag = privateTag;
            keyClass = (__bridge id) kSecAttrKeyClassPrivate;
    }
    else {
            tag = publicTag;
            keyClass = (__bridge id) kSecAttrKeyClassPublic;
    }

    NSDictionary *saveDict = @{
                    (__bridge id) kSecClass : (__bridge id) kSecClassKey,
                    (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
                    (__bridge id) kSecAttrApplicationTag : tag,
                    (__bridge id) kSecAttrKeyClass : keyClass,
                    (__bridge id) kSecValueData : key,
                    (__bridge id) kSecAttrKeySizeInBits : [NSNumber numberWithUnsignedInteger:keySize],
                    (__bridge id) kSecAttrEffectiveKeySize : [NSNumber numberWithUnsignedInteger:keySize],
                    (__bridge id) kSecAttrCanDerive : (__bridge id) kCFBooleanFalse,
                    (__bridge id) kSecAttrCanEncrypt : (__bridge id) kCFBooleanTrue,
                    (__bridge id) kSecAttrCanDecrypt : (__bridge id) kCFBooleanFalse,
                    (__bridge id) kSecAttrCanVerify : (__bridge id) kCFBooleanTrue,
                    (__bridge id) kSecAttrCanSign : (__bridge id) kCFBooleanFalse,
                    (__bridge id) kSecAttrCanWrap : (__bridge id) kCFBooleanTrue,
                    (__bridge id) kSecAttrCanUnwrap : (__bridge id) kCFBooleanFalse
    };

    SecKeyRef savedKey = NULL;
    sanityCheck = SecItemAdd((__bridge CFDictionaryRef) saveDict, (CFTypeRef *)&savedKey);
    if (sanityCheck != errSecSuccess) {
            LOGGING_FACILITY1(sanityCheck != noErr, @"Problem saving the key to keychain, OSStatus == %d.", sanityCheck);
    }

    return savedKey;
}

如果不想立即获取引用,可以将方法类型改为void,去掉return语句。将 (CFTypeRef *)&savedKey 更改为 NULL

然后您可以像这样检索保存的 key :

- (SecKeyRef)getKeyRef:(BOOL)isPrivate {
    OSStatus sanityCheck = noErr;
    NSData *tag;
    id keyClass;
    if (isPrivate) {
        if (privateKeyRef != NULL) {
            // already exists in memory, return
            return privateKeyRef;
        }
        tag = privateTag;
        keyClass = (__bridge id) kSecAttrKeyClassPrivate;
    }
    else {
        if (publicKeyRef != NULL) {
            // already exists in memory, return
            return publicKeyRef;
        }
        tag = publicTag;
        keyClass = (__bridge id) kSecAttrKeyClassPublic;
    }

    NSDictionary *queryDict = @{
            (__bridge id) kSecClass : (__bridge id) kSecClassKey,
            (__bridge id) kSecAttrKeyType : (__bridge id) kSecAttrKeyTypeRSA,
            (__bridge id) kSecAttrApplicationTag : tag,
            (__bridge id) kSecAttrKeyClass : keyClass,
            (__bridge id) kSecReturnRef : (__bridge id) kCFBooleanTrue
    };

    SecKeyRef keyReference = NULL;
    sanityCheck = SecItemCopyMatching((__bridge CFDictionaryRef) queryDict, (CFTypeRef *) &keyReference);
    if (sanityCheck != errSecSuccess) {
        NSLog(@"Error trying to retrieve key from server. isPrivate: %d. sanityCheck: %li", isPrivate, sanityCheck);
    }

    if (isPrivate) {
        privateKeyRef = keyReference;
    }
    else {
        publicKeyRef = keyReference;
    }
    return keyReference;
}

此外,返回 base64 编码字符串的更简单方法是:

NSString *signatureString = [signedHash base64EncodedStringWithOptions:nil];

关于privateTag和publicTag

privateTagpublicTag 用于标记kSecAttrApplicationTag,它定义了使用此 key 的应用程序。您希望有一个单独的 privateTagpublicTag 来区分您的私钥和公钥。

这有点令人费解,因为我遵循了示例代码,但我是这样定义我的 privateTagpublicTag 的:

SecKeyWrapper.h

#define kPublicKeyTag           "com.sample.app.publickey"
#define kPrivateKeyTag          "com.sample.app.privatekey"

SecKeyWrapper.m

// just under @implementation or @synthesize lines
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag;
static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag;

- (id)init {
    if (self = [super init]) {
        // Tag data to search for keys.
        privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)];
        publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)];
    }

    return self;
}

然后像在我上面提供的代码示例中那样使用 privateTagpublicTag

关于ios - 使用 rsa 私钥签署数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20775227/

相关文章:

IOS : Releasing variable with @property (nonatomic,保留)

ios - 如何在运行 Windows 或 Android 的其他设备上镜像 iPhone

ios - 单击按钮时从另一个类返回 NSDictionary

vb.net - 在 VB.NET 中执行应该溢出的 UInt32 添加的最佳方法

java - Cipher.getBlockSize() 即使使用 256 位 key 也会返回 128 位

ios - 移动时为多个帧设置动画的正确方法

iOS - Scrollview 的滚动条显示但不滚动内容

objective-c - NSURLConnection sendSynchronousRequest - 缺少数据

objective-c - 如何将视频路径转换为nsdata?

sql-server - Hibernate/JPA 和 MS SQL Server - 在 DecryptByKey 之前打开对称 key