SecPKCS12Import returns iOS 11 上的结果与 iOS 10 上的结果不同

SecPKCS12Import returns different results on iOS 11 than on iOS 10

此函数将 Base64 编码的 PKCS#12 证书字符串作为参数,然后对其进行解码并传递给 SecPKCS12Import 函数。更新到 iOS 11 SecPKCS12Import 后,会产生不同的结果。 OS 两个版本的安全错误 returns 0。

let securityError: OSStatus = SecPKCS12Import(decodedData!, options, &items)

Returns 0 项列表。而在 iOS 10 上,我在数组中得到了 1 个项目。

func certificateFromCertificate(certP12: String, psswd: String) -> SecCertificate {
    let decodedData = NSData(base64Encoded: certP12, options:NSData.Base64DecodingOptions(rawValue: 0))

    let keytmp : NSString = kSecImportExportPassphrase as NSString
    let options : NSDictionary = [keytmp : psswd]

    var certificateRef: SecCertificate? = nil

    var items : CFArray?

    let securityError: OSStatus = SecPKCS12Import(decodedData!, options, &items)

    let theArray: CFArray = items!
    if securityError == noErr && CFArrayGetCount(theArray) > 0 {
        let newArray = theArray as [AnyObject] as NSArray
        let dictionary = newArray.object(at: 0)
        let secIdentity = (dictionary as AnyObject)[kSecImportItemIdentity as String] as! SecIdentity
        let securityError = SecIdentityCopyCertificate(secIdentity , &certificateRef)
        if securityError != noErr {
            certificateRef = nil
        }
    }

    certificate = certificateRef

    return certificateRef!
}

post 在 Apple Developer forum 上说 SecPKCS12Import 实现了从 Base64 的自动转换。这意味着我应该在将普通证书传递给函数之前对其进行解码。这可能是问题所在吗?

我已将 post 提交给 Apple Forum and also Technical Support incident

开发环境:

编程语言:Swift3

调试设备:Apple iPad mini Retina Wi-Fi 32GB ME280SL/A

开发设备:iMAC mini Xcode版本9.0 9A235

使用 Xcode Version 9.0.1 (9A1004)Apple Swift version 4.0 (swiftlang-900.0.65.2 clang-900.0.37),我能够从您的函数中获得正常结果。也适用于 Swift 语言设置为 3.2。

在您引用的 post 中,@eskimo 说(加粗我的):

Your options here are:

  • File a compatibility bug against iOS 11 beta and hope that it gets fixed before GM
  • Change your app to use SecPKCS12Import in the documented way, that is, undo the Base64 decoding

似乎有人提交了错误,问题已得到修复(使其按照最初记录的方式工作)。 Base64 没有自动解码。你提供解码是对的。

Apple Developer Support 已调查此问题并得出结论,特定的 .p12 文件具有 DER 编码,不符合 iOS 11.

的安全要求

下面是 Quinn 来自 Apple DTS 的电子邮件:

To understand what’s going on you must set a breakpoint on the function SecKeyCreateRSAPrivateKey. This is a private function within the Security framework, so you’ll need to use a symbolic breakpoint (in the Breakpoints navigator, click the + button and choose Symbolic Breakpoint from the menu. Once you’ve done that, tap the “Test 1” button and you’ll stop at the breakpoint.

This function takes 4 arguments, but the two relevant ones are the second and third arguments, which are a pointer to the RSA private key

data to decode and the length of that data. Once you hit the breakpoint you can dump the data as follows:

(lldb) # Confirm the length in the third argument.
(lldb) p $arg3
(unsigned long) [=11=] = 1192
(lldb) # Dump the data pointed to be the second argument.
(lldb) memory read -f x -s 1 -c 1192 --force $arg2
… elided …

The result is too long to include here so I undid the hex encoding and attached the result as a file, RSAPrivateKey.asn1.

If you dump this with dumpasn1 you’ll see the problem:

$ dumpasn1 -p -a RSAPrivateKey.asn1 
SEQUENCE {
  INTEGER 0
  INTEGER
    …
  INTEGER 65537
  INTEGER
    00 0B 67 67 29 0A 38 38 7A 7E 17 39 E7 84 FB 7D
    02 D1 AB AD 21 27 4D CD 2C 09 C1 1F CB 73 5C 18
    37 D4 CE E2 98 61 19 3B 70 6C 1A 1B 33 E8 69 8C
    65 5B 77 B8 24 9C 90 8A 79 A7 4A 77 26 38 7C 3E
    70 3C 80 24 BD 73 DA 97 5A F3 90 0F 79 2D 45 F8
    2A 5A 37 03 4D 0C 80 DB 8F 99 67 55 D3 F3 70 CA
    2D F0 B6 48 6A D8 9A 95 CE AA C8 F1 96 24 04 61
    38 A1 7C CD 56 70 5E F9 13 D4 1B E2 F2 5D 28 67
    9A 30 E0 ED 71 84 4C EC 86 44 18 5F 64 70 46 D8
    6B 3A 52 2C D7 DE AF E5 E2 35 41 0D 1E FF E7 DE
    AC 43 83 5C A6 F2 2E C8 79 1A 30 87 7A 78 9B 42
    3E D9 2D AA C0 9D A6 7C E9 5C E3 6C 1E 8D 87 DF
    EB 05 DF CA F5 B9 2D BA B6 01 71 18 22 4D 25 4E
    D5 77 CB B8 9B 95 F9 C6 39 1C 0D D2 46 E4 4A 45
    D8 26 6F B4 25 03 E7 BE 91 02 43 7D DC B0 1E C8
    67 E8 2E 5F EA 3A 8D 1C 69 43 80 F9 60 69 BF BA
    01
    Error: Integer has non-DER encoding.
  INTEGER
    …
  INTEGER
    …
  INTEGER
    …
  INTEGER
    …
  INTEGER
    …
  }

Note the “Integer has non-DER encoding” error raised flagged in the fourth INTEGER element. This arises because the INTEGER data is not canonical. In ASN.1 DER, INTEGERs are signed, which introduces an ambiguity. A value like 80 00 could be interpreted as -32768 or 32768. To resolve this ASN.1 DER requires that the positive form is encoded as 00 80 00 (there’s a similar case that requires a leading ff). Furthermore, DER specifically requires that this leading byte only be added when necessary. In the case shown above the second byte, 0b, doesn’t have the top bit set, and thus mustn’t have that leading 00.

Earlier versions of iOS did not enforce this requirement. iOS 11 includes a number of security hardening changes, and rejecting invalidly encoded ASN.1 DER INTEGERs is one of those changes.

I’ve seen other developers get bitten by this security change but those cases were easier to debug because the keys weren’t embedded within a PKCS#12. For example, consider the following DevForums thread. https://forums.developer.apple.com/message/262593#262593 Furthermore, this problem was tricky to debug because, when you unpack the .p12 with openssl, it ignores this error on input and, on output, emits a fixed version of the private key!

Quin 建议我通过修复生成此 PKCS#12 数据的任何代码来解决此问题。如果那不可能,您将需要编写(或获取)您自己的代码来解析 PKCS#12。 iOS 11 正确地拒绝了格式错误的私钥,这不太可能改变。