【问题标题】:sign data using rsa private key使用 rsa 私钥对数据进行签名
【发布时间】:2014-01-13 13:01:06
【问题描述】:

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

我已阅读苹果文档,只有指南如何从证书创建 RSA 公钥和私钥。就我而言,我在 .pem 文件中只有 RSA 私钥。所以我的问题是他的:我应该如何使用该密钥签署数据? 我不想使用 openssl。我试过没有运气,我认为可以使用苹果 API 使用 RSA 对数据进行签名。

这就是我的钥匙的样子:

-----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];
}

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

谢谢。

【问题讨论】:

  • 您似乎已经问过这个问题,如“how to get rsa private key from pem file and sign data/string with it”。
  • 没有人回答这个问题,如果您想提供帮助,请回答我的问题,这就是我所需要的。我不需要人们说这是重复的,因为我知道它不是。
  • 多次问同一个问题不会让你更快地得到答案。您的示例代码似乎完全相同(除了一些添加的 cmets),因此很明显,自从您上次询问以来,您还没有自己完成任何工作。
  • 感谢您的批评,但我会尽我所能完成我的任务。你的话有道理,但我已经为此苦苦挣扎了两个星期,我很绝望。请理解我。

标签: ios objective-c encryption rsa


【解决方案1】:

好的,我自己找到了解决这个问题的方法。我希望这对其他人有所帮助...this 帮助了我。我以为我可以在没有 openssl 的情况下做到这一点,但我错了。但是,通过在该帖子中进行操作,您的项目中不需要额外的库。使用终端

【讨论】:

    【解决方案2】:

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

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

    - (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

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

    - (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,它定义了使用此密钥的应用程序。您希望有一个单独的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

    【讨论】:

    • 你能解释一下 privateTag 和 publicTag 是什么吗?
    • @knarf74:我添加了一个部分,详细解释了私有和公共标签。让我知道是否还有其他问题。谢谢!
    • 不错!谢谢。我正在查看加密练习,甚至是 SecKeyWrapper 类,但在其中迷失了方向。感谢帮助。对于其他想知道的人,请查看 iOS 开发人员库中的 CryptoExercise:developer.apple.com/library/ios/samplecode/CryptoExercise/…
    • @knarf74:不客气!如果我能以其他方式提供帮助,请告诉我。别忘了点赞! :)
    猜你喜欢
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 2012-07-15
    • 2011-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-06
    相关资源
    最近更新 更多