在 OS X 上登录,在 iOS 和 OS 上验证状态 -9809

Sign on OS X, Verify on iOS and OSStatus -9809

我正在使用 Apple 的 Security Framework。我能够在 OS X 上签名并成功验证所有内容,但是当我尝试在 iOS 上使用 SecKeyRawVerify 时,它失败并出现 -9809 错误。

我尝试过各种 PKCS 填充选项和许多其他属性,但我无法正确验证它。

请注意,下面的代码可能到处都是漏洞,只是想先让它正常运行。

OS X 签名码:

NSData* signData(NSData* plainData, SecKeyRef privateKey) {
    CFErrorRef error;

    /* Create the transform objects */
    SecTransformRef signer = SecSignTransformCreate(privateKey, &error);
    if (error) { CFShow(error); exit(-1); }                                    

    /* Explicitly set padding type (necessary?) */
    SecTransformSetAttribute(signer,
                             kSecPaddingKey,
                             kSecPaddingPKCS1Key,
                             &error);
    if (error) { CFShow(error); exit(-1); }

    /* Specify digest type */
    SecTransformSetAttribute(signer,
                             kSecDigestTypeAttribute,
                             kSecDigestSHA1,
                             &error);
    if (error) { CFShow(error); exit(-1); }

    /* OS X calculates SHA1 hash/signature for us */
    SecTransformSetAttribute(signer,
                             kSecTransformInputAttributeName,
                             plainData,
                             &error);
    if (error) { CFShow(error); exit(-1); }

    CFTypeRef signature = SecTransformExecute(signer, &error);
    if (error || !signature) { CFShow(error); exit(-1); }

    CFRelease(signer);

    return signature;
}

和iOS验证码:

+ (NSData *)verifyData:(NSData *)data
              usingKey:(SecKeyRef)publicKey {
    size_t signatureByteLength = SecKeyGetBlockSize(publicKey);

    if (signatureByteLength > data.length)
    {
        NSLog(@"Signature length is greater that data length.");
        return nil;
    }

    NSData *signature = [data subdataWithRange:NSMakeRange(0, signatureByteLength)];
    NSData *plainData = [data subdataWithRange:NSMakeRange(signatureByteLength, data.length - signatureByteLength)];

    NSLog(@"signatureLength='%lu', signatureBytes='%@', plainDataLength=%lu", (unsigned long)signature.length, signature, (unsigned long)plainData.length);

    size_t hashByteLength = CC_SHA1_DIGEST_LENGTH;
    uint8_t* hashBytes = (uint8_t *)malloc(hashByteLength);

    if (CC_SHA1([plainData bytes], (CC_LONG)[plainData length], hashBytes))
    {
       NSData *b = [NSData dataWithBytes:hashBytes length:hashByteLength];
       NSLog(@"hashBytesLength='%lu', hashBytes=%@", (unsigned long)b.length, b);

        OSStatus status = SecKeyRawVerify(publicKey,
                                  kSecPaddingPKCS1SHA1, // I have also tried kSecPaddingPKCS1, doesn't work
                                  hashBytes,
                                  hashByteLength,
                                  (uint8_t *)[signature bytes],
                                  signatureByteLength);

        switch (status)
        {
            case errSecSuccess:
                NSLog(@"SecKeyRawVerify success.");
                return plainData;

            case errSSLCrypto:
               NSLog(@"SecKeyRawVerify failed: underlying cryptographic error");
               break;

            case errSecParam:
               NSLog(@"SecKeyRawVerify failed: one or more parameters passed to a function where not valid.");
               break;

            default:
               NSLog(@"SecKeyRawVerify failed: error code '%d'", (int)status);
               break;
       }
    }
   return nil;
}

我使用以下 OpenSSL 命令通过命令行创建了私钥和 public 密钥:

1.    // Generate private and public key pair
openssl genrsa -out rsaPrivate.pem 1024

1a. // Generate public key
openssl rsa -in rsaPrivate.pem -pubout -outform PEM -out rsaPublic.pem

2. //Create a certificate signing request with the private key
openssl req -new -key rsaPrivate.pem -out rsaCertReq.csr

3. //Create a self-signed certificate with the private key and signing request
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey rsaPrivate.pem -out rsaCert.crt

4. //Convert the certificate to DER format: the certificate contains the public key
openssl x509 -outform der -in rsaCert.crt -out rsaCert.der

非常感谢任何帮助。

if (CC_SHA1([plainData bytes], (CC_LONG)[plainData length], hashBytes))
{
    NSData *b = [NSData dataWithBytes:hashBytes length:hashByteLength];
    NSLog(@"hashBytesLength='%lu', hashBytes=%@", (unsigned long)b.length, b);
    ...
}

您似乎没有正确编码和应用填充。在散列之前应用编码和填充。请参阅 RFC 3447,Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1(或 PKCS #1 规范)。

我想通了这个问题。我发布的代码是正确的,但填充需要根据 SecKey.h 头文件设置为 kSecPaddingPKCS1SHA1

If you are verifying a proper PKCS1-style signature, with DER encoding
of the digest type - and the signedData is a SHA1 digest - use
kSecPaddingPKCS1SHA1.

此外,您可能需要确保 .der 格式的 public 密钥是正确的:)