35 Replies
      Latest reply on Jan 13, 2017 7:28 AM by hfossli
      fijibill Level 1 Level 1 (0 points)

        Hi all,

         

        Using iOS 9 beta 2, I'm trying to export an elliptic curve public key that was generated with kSecAttrTokenIDSecureEnclave and kSecAccessControlPrivateKeyUsage but I am having a few issues.

         

        First, I can't specify kSecAttrIsPermanent for kSecPublicKeyAttrs or SecKeyGeneratePair() fails. I guess that makes sense because kSecAttrTokenIDSecureEnclave is specified for the entire SecKeyGeneratePair() operation (it fails if I put it under kSecPrivateKeyAttrs?) and there is no reason to save an elliptic curve public key with Secure Enclave protection. But this means that later looking up the elliptic curve public key with SecItemCopyMatching and kSecReturnData fail, so there doesn't seem to be a way to get the public key material in order to export the elliptic curve public key using the KeyChain API calls.

         

        Second, of course I have the SecKeyRef for the elliptic curve public key returned by SecKeyGeneratePair(), but on iOS there is no way to export the elliptic curve public key from this opaque handle.

         

        Third, SecKeyRef will print out diagnostic info for the elliptic curve public key though! This is the output for a typical elliptic curve public key as returned by the OS:

         

        <SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 3, block size: 256 bits, y: 0620A1AE78F7EA7D79F1CA6F63F5954BD710BDBCEA9F03838A5F939F60140A7E01, x: 120DE3D293CF8B6F8A6049942ABD2C206BC7050B2330C348FDBA2999A8CB1AD90620A1AE78F7EA7D79F1CA6F63F5954BD710BDBCEA9F03838A5F939F60140A7E01, addr: 0x134672110>

         

        x and y are specified, so for the time being I thought I could export the elliptic curve public key from the x and y dump. But x is 130 hexadecimal digits and y is 66 hexadecimal digits? Shouldn't these values be 32 bytes each?

         

        The Apple KeyChainTouchID sample from iOS 9 beta 2 does not show how to export elliptic curve public keys, only how to generate, sign, and delete.

         

        Things work properly with RSA, but then kSecAttrTokenIDSecureEnclave and kSecAccessControlPrivateKeyUsage can't be specified.

         

        Confused. Any help appreciated!

        • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
          Eric Meng Level 1 Level 1 (0 points)

          hi

            i‘ve encounter exactaly the same problem. but after days of coding and searching,

          stil can't find a way to solve it. have you solve this yet?

          • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
            CarySalt Level 1 Level 1 (0 points)

            Hi,

            Also trapped by this. Any solution now?

            • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
              CarySalt Level 1 Level 1 (0 points)

              Hi guys,

               

              I think I figured out how to intepret SecKeyRef data. It's mainly about endianess. Verified on iOS9 beta 4.

               

              To ease the explanation, I removed kSecAttrTokenIDSecureEnclave when generating the keys so that I can read the public key's data by using kSecReturnData : @YES in SecItemCopyMatching. Now we have the following data for analysis.

               

              In non Secure Enclave mode, the data I got on my device is:

              Generated public key: <SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 3, block size: 256 bits, y: C327638AB594B9D13E6C25D9817888E8DA03E3B10F43479B088F793301BB040601, x: CBC9F4AE68D0A519FC3BB15A94F286FE9AC14DCCE75128F9ADD8DBF49FF13394C327638AB594B9D13E6C25D9817888E8DA03E3B10F43479B088F793301BB040601, addr: 0x146a6b90>

               

              Public key data retrieved: <04 9433f19ff4dbd8adf92851e7cc4dc19afe86f2945ab13bfc19a5d068aef4c9cb 0604bb0133798f089b47430fb1e303dae8887881d9256c3ed1b994b58a6327c3>

               

              The expected public data shoud begin with 1 byte of value 0x04, then following by two 32bytes unsigned integers in big-endian (for x and y values). Here in this case is x value: <0x9433f19ff4dbd8adf92851e7cc4dc19afe86f2945ab13bfc19a5d068aef4c9cb> and y value: <0x0604bb0133798f089b47430fb1e303dae8887881d9256c3ed1b994b58a6327c3>.

               

              Now let's look back at the SecKeyRef printouts. y value in the printout is <C327638AB594B9D13E6C25D9817888E8DA03E3B10F43479B088F793301BB040601>. If we exclude the ending <0x01> bytes and convert the y value from little-endian into big-endian, we have <0x0604bb0133798f089b47430fb1e303dae8887881d9256c3ed1b994b58a6327c3>, which is exactly the value we have for y in the public key data. I'm not sure what's the meaning of <0x01>, but it seems to be a constant there. Hence, we can construct y value from SecKeyRef.

               

              Now let's look at x value. The x value in SecKeyRef is way too long compared to the expected 32bytes value. But I noticed that the ending part of the long x value is exactly the same as the y value in SecKeyRef. By removing the redundant y part from the long x value, we have <CBC9F4AE68D0A519FC3BB15A94F286FE9AC14DCCE75128F9ADD8DBF49FF13394>. Convert it from little-endian to big-endian and we got <0x9433f19ff4dbd8adf92851e7cc4dc19afe86f2945ab13bfc19a5d068aef4c9cb>, which is exactly the x value we read in the actual public key data.

               

              Therefore, although we cannot use SecItemCopyMatching in Secure Enclave mode to query pulic key data, the infomation in SecKeyRef is sufficient to re-construct the needed public key information and get stored for server side usage.

               

              In your example, <SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 3, block size: 256 bits, y: 0620A1AE78F7EA7D79F1CA6F63F5954BD710BDBCEA9F03838A5F939F60140A7E01, x: 120DE3D293CF8B6F8A6049942ABD2C206BC7050B2330C348FDBA2999A8CB1AD90620A1AE78F7EA7D79F1CA6F63F5954BD710BDBCEA9F03838A5F939F60140A7E01, addr: 0x134672110>, using the method above, the public key data would be

              <0x04 d91acba89929bafd48c330230b05c76b202cbd2a9449608a6f8bcf93d2e30d12 7e0a14609f935f8a83039feabcbd10d74b95f5636fcaf1797deaf778aea12006>

              • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                csgodenzim Level 1 Level 1 (0 points)

                Has anyone found a good solution for this?

                I am stuck at the same point and parsing the SecKeyRef description string is not a valid solution for me.

                 

                To elaborate, I also tried to save the generated public key into the keychain, so that I would be able to later use the SecItemCopyMatching() function to get at the data, but the SecItemAdd() operation always fails with errSecParam error.

                 

                This is what I am doing.

                 

                Key generation:

                 

                let publicKeyAttributes : [String:AnyObject] = [
                     kSecAttrApplicationTag as String: publicKeyTag
                ]
                
                let privateKeyAttributes : [String:AnyObject] = [
                     kSecAttrIsPermanent as String: kCFBooleanTrue,
                     kSecAttrApplicationTag as String: privateKeyTag
                ]
                
                var errorRef : Unmanaged<CFErrorRef>? = nil
                let acl = SecAccessControlCreateWithFlags(kCFAllocatorDefault,
                     kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                     SecAccessControlCreateFlags.PrivateKeyUsage,
                     &errorRef)!
                
                
                if errorRef != nil {
                     throw Error.Unknown
                }
                
                
                let keyPairAttributes : [String:AnyObject] = [
                     kSecAttrKeyType as String: kSecAttrKeyTypeEC as String,
                     kSecAttrKeySizeInBits as String: 256,
                     kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave as String,
                     kSecAttrAccessControl as String: acl,
                     kSecPublicKeyAttrs as String: publicKeyAttributes,
                     kSecPrivateKeyAttrs as String: privateKeyAttributes
                ]
                
                
                var publicKey : SecKey? = nil
                var privateKey : SecKey? = nil
                let status = SecKeyGeneratePair(keyPairAttributes, &publicKey, &privateKey)
                if !IsSuccess(status) {
                     throw Error.SecItemError(status)
                }
                

                 

                And my attempt to add the public key to the keychain:

                 

                CFErrorRef error = NULL;
                SecAccessControlRef acl = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleAfterFirstUnlock, kNilOptions, &error);
                NSDictionary * attributes = @{
                     (id)kSecUseItemList: @[(__bridge id)key],
                     (id)kSecClass: (id)kSecClassKey,
                     (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic,
                     (id)kSecAttrAccessControl: (__bridge id)acl,
                     (id)kSecAttrApplicationTag: tag};
                OSStatus status = SecItemAdd((CFDictionaryRef)attributes, &result); // status is always -50 (errSecParam)
                
                  • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                    eskimo Apple Staff Apple Staff (9,835 points)

                    Has anyone found a good solution for this?

                    If you get completely stuck, you should open a DTS tech support incident for this.

                    Share and Enjoy

                    Quinn "The Eskimo!"
                    Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                    let myEmail = "eskimo" + "1" + "@apple.com"

                      • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                        Digital Leaves Level 1 Level 1 (0 points)

                        A DTS tech support won't help you (at least it didn't help me, the guy who answered me was even more clueless than myself).

                         

                        Once you generate the keys with SecKeyGeneratePair, you have to add the public key to the keychain, using the reference returned by SecKeyGeneratePair:

                         

                        If you specify kSecReturnData = true in the SecItemAdd dictionary, you will get the key data directly, and then you are able to send it to your server.

                         

                                let parameters = [

                                    kSecClass as String: kSecClassKey,

                                    kSecAttrKeyType as String: kSecAttrKeyTypeEC,

                                    kSecAttrLabel as String: "...",

                                    kSecAttrIsPermanent as String: true,

                                    kSecValueRef as String: publicKey,

                                    kSecAttrKeyClass as String: kSecAttrKeyClassPublic,

                                    kSecReturnData as String: true

                                ]

                                var data: AnyObject?

                                let status = SecItemAdd(parameters, &data)

                                return status == errSecSuccess ? data as? NSData : nil

                         

                        I'm still trying to figure out how to use this EC key bytes in PHP (i.e: getting it to a standard OpenSSL format), so if anyone has a clue, please let me know.

                        If I can be of any further help, I have the code working on the iOS side, so let me know.

                        Best.

                        • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                          Digital Leaves Level 1 Level 1 (0 points)

                          Any hint on how to use these ECSDA secp256r1 keys in PHP would be greatly appreciated. I tried phpecc without success. I think it's not a standard format, or at least not readable by openssl (openssl ec -text -noout -pubin -in mypubkey.pem).

                      • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                        crazyquark Level 1 Level 1 (0 points)

                        Hello,

                         

                        I managed to generate the keypair but I think I am having issues with the ACL.

                        If I use the freshly generated SecKey reference for the keypair I can sign data.

                        But, if I try to retrieve it later with SecItemCopyMatching() I get SecKey reference but any attempt to sign results in errSecAuthFailed.

                        I've tried many ACL combinations and kSecUseOperationPrompt when retrieving the key but nothing sticks.

                         

                        Has anyone done this successfuly?

                        I am on iOS 9.0 final.

                        • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                          jmarne Level 1 Level 1 (0 points)

                          So, I'm able to generate a public key using the new kSecAttrKeyTypeEC and kSecAttrTokenIDSecureEnclave. However, I'm finding that I cannot parse the public key as a standard X.509 formatted key. In digging, I've discovered that the header bytes (everything before the 0x04) are what's causing my problems.

                           

                          I would like to know if anyone knows how the header bytes are generated on the ECDSA public key in this case? In my examples, I get these bytes:

                           

                              0x30,0x53,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x03,0x42,0x00

                           

                          However, I've found that I can parse the public key as anormal X.509 public key if I substitute the header bytes above for the ones from the SecKey::SecKeyCreateFromPublicData function documented in:

                          http://www.opensource.apple.com/source/Security/Security-55471/libsecurity_keychain/lib/SecKey.cpp

                           

                           

                             } else if (kSecECDSAAlgorithmID == algorithmID) {

                                  CFMutableDataRef tempData;

                                  uint8 headerBytes[] = { 0x30,0x59,0x30,0x13,0x06,0x07,0x2a,0x86,

                                                          0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a,

                                                          0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03,

                                                          0x42,0x00 };

                           

                          However, I would like to confirm that this is not just a happy coincidence. Ideally, I'd like for the public key bytes I get from the SecKeyGeneratePair to be X.509 compliant so I don't have to monkey with the bits.

                            • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                              Digital Leaves Level 1 Level 1 (0 points)

                              Hi jmarne,

                               

                              Same here, I generated a keypair in openssl using the curve secp256r1, the one that's using SecKeyGeneratePair with kSecAttrKeyTypeEC. By trial and error I discovered that I had to add a standard ASN.1 OID header exactly like yours to the raw public key bytes returned by SecItemCopyMatching:

                              $ hexdump -C header_secp256r1.bytes

                              00000000  30 59 30 13 06 07 2a 86  48 ce 3d 02 01 06 08 2a  |0Y0...*.H.=....*|

                              00000010  86 48 ce 3d 03 01 07 03  42 00                    |.H.=....B.|

                               

                              And then I'm able to use it with openssl. Now I'm trying to use this key to verify a signature sent by the iOS App, but I'm clueless on how to hash the data with the kind of signature hash algorithm that OpenSSL/PHP is waiting (that's "ecdsa-with-SHA1"). I have tried with openssl's generated keypair and it's working, so apparently there should be a way fo doing the same with the result of SecKeyRawSign.

                                • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                  jmarne Level 1 Level 1 (0 points)

                                  Digital Leaves, Sorry but I don't know anything about the openssl/php library. However, after a quick google search, I'm guessing that you need to pass in this hash algorithm: "[14] => ecdsa-with-SHA1" as documented on this page: http://php.net/manual/en/function.openssl-get-md-methods.php to the verify function. Also, I'm not sure you have to re-hash the original data during verification in PHP. If I'm reading this function correctly, http://php.net/manual/en/function.openssl-verify.php. You just have to pass the original data that was used to generate the signature along with the hash algorithm used and php will hash it for you during the verify call.

                                   

                                  As an FYI, my ANS1 Dump of the iOS ECC public key produces this OID:

                                   

                                       ObjectIdentifier(1.2.840.113549.1.1.1)

                                   

                                  Which appears to correspond to an RSA encryption header (according to RFC 5480 https://tools.ietf.org/html/rfc5480).

                                   

                                       -- RSA PK Algorithm and Key

                                   

                                       rsaEncryption OBJECT IDENTIFIER ::= {

                                            iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 1 }

                                   

                                  So, if I'm guessing correctly, we're getting an RSA header on an ECC public key which is reason for the need to swap out OID headers.

                                    • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                      Digital Leaves Level 1 Level 1 (0 points)

                                      Hi Jmarne,

                                       

                                      Thank you. In my opinion, for some reason, Apple does not store the public key header information. I guess this is for the same reason that they only allow ECC keys when using secure enclave, the size of the keys (ECC keys are way shorter and space-efficient than, say, RSA ones). The header actually corresponds to a header for a secp256r1 curve, only they won't include that, which is very unfortunate, and leave us guessing how to transform/use that key.

                                       

                                      The problem with the signing process (for me at least) is that the signature usually occurs in conjuction with a hash of the data, not directly to the data, but I have no way of knowing if SecKeyRawSign is doing that for ECC keys, and if so, if it's using what openssl will call a SHA1 hash algorithm or the ecdsa-with-SHA1 one (I can't find any ECC reference in all the CC_SHAXXX functions in CC). I will definitely have to check out, but due to signature verification being a blackbox (0-1), and the lack of Apple documentation regarding EC key signing-hashing process, I will need to spend some time to make it work I guess.

                                       

                                      Anyway thanks a lot, that info really helps me to verify that I'm in the right track.

                                        • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                          csgodenzim Level 1 Level 1 (0 points)

                                          At the end, I finally managed to solve this. Meaning, I am able to generate the key pair, protecting the private key in the secure enclave and touch id, export the public key, sign data using the private key and finally verify the signature using openssl.

                                           

                                          As described in the posts above, the public key is missing the header bytes, so those need to be added.

                                          Now when signing the data on the device, I did this:

                                          SecKeyRawSign(privateKey, kSecPaddingSigRaw, hashBytes, hashBytesSize, signedHashBytes, &signedHashBytesSize);
                                          

                                          hashBytes: this is the SHA1 hash of the data I want to sign.

                                           

                                          Now to verify I use openssl like this:

                                          openssl dgst -ecdsa-with-SHA1 -verify pubkey.pem -signature signature.bin data.txt
                                          

                                          signature.bin: this just contains the output of SecKeyRawSign(), no manipulation needed.

                                           

                                          Hope this is helpful.

                                            • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                              Digital Leaves Level 1 Level 1 (0 points)

                                              Hi,

                                              Thanks csgodenzim, I tried with that padding also (all paddings, actually without success). Is there a way I can talk to you by email or something?

                                              Are you doing a Hash by using CC_SHA1 of the data in iOS prior to calling SecKeyRawVerify? I'm getting mad trying to make this work. Here is my code. I tried "ecdsa-with-SHA1", "SHA1", etc at the server side with no luck (well, I think I tried everything).

                                               

                                                          if let privateKeyRef = self.getPrivateKeyReference() { // <==== Private key is valid, EC key ref from secure enclave (checked)
                                                              let resultData       = NSMutableData(length: LargeEnoughBufferSize)!
                                                              let resultPointer    = UnsafeMutablePointer<UInt8>(resultData.mutableBytes)
                                                              var resultLength     = resultData.length
                                                        
                                                              if let plainData = message.dataUsingEncoding(NSUTF8StringEncoding) {
                                                                  let hashData = NSMutableData(length: Int(CC_SHA1_DIGEST_LENGTH))!
                                                                  let hash = UnsafeMutablePointer<UInt8>(hashData.mutableBytes)
                                                                  CC_SHA1(UnsafePointer<Void>(plainData.bytes), CC_LONG(CC_SHA1_DIGEST_LENGTH), hash)
                                              
                                                                  let status = SecKeyRawSign(privateKeyRef, SecPadding.PKCS1, hash, hashData.length, resultPointer, &resultLength) // <== SigRaw shows equal results
                                                                  if status != errSecSuccess {
                                                                      ... // handle error.
                                                                  } else { // <========= Status is errSecSuccess, this is reached and resultData is shown and looks valid.
                                                                      resultData.length = resultLength
                                                                      print("Result: \(status). Generated result: \(resultData)")
                                                                  }
                                                              } else { ... } // handle error
                                                              // <========= now send resultData to server. Server is unable to get it.
                                                          } else { ... } // handle error
                                              
                                              

                                              Please, any help REALLY appreciated. Thanks in advance.

                                    • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                      eskimo Apple Staff Apple Staff (9,835 points)

                                      A developer wrote into DTS asking about Secure Enclave cryptography, which gave me the opportunity to spend some time looking at this.  Let’s explain what’s going on here.

                                      When you generate a key pair as per the KeychainTouchID sample code, the private key gets store in the Secure Enclave leaving the public key represented solely in memory.  You can add that public key to the keychain with code like this:

                                      let addErr = SecItemAdd([
                                          kSecClass as String:                kSecClassKey,
                                          kSecValueRef as String:             publicKey,
                                          kSecAttrApplicationTag as String:   uuidStr
                                      ] as NSDictionary, nil)
                                      

                                      Note In my case I wanted to tag the key with a UUID so that I could come back to it later.

                                      If you then do a SecItemCopyMatching on the public key to get back some data (via kSecReturnData), you see something like this:

                                      04
                                      B24DB122 E2DDDC97 FB0F58ED 7836F2CA
                                      6868C040 6B483FDA 473FDDD0 41D380B0
                                      4EDB6183 3273046A 33D7A9A8 D66B93AA
                                      A10B8732 93C68114 9CC7FA5E CD3523CE
                                      

                                      This is secp256r1 public key as defined by:

                                      The latter standard shows examples that look just like this (see Section 2.4.2).

                                      Notably, the leading 04 indicates that the key is uncompressed.  You could potentially see a 02 or 03 in the same place, indicating a compressed key.  Either way, that first byte is a good way to detect that you’re seeing the data you’re expecting to see.

                                      When you put such a key into a certificate, you have to wrap it in a SubjectPublicKeyInfo ASN.1 structure.  That wrapping is defined by RFC 5480 “Elliptic Curve Cryptography Subject Public Key Information.  The structure looks like this:

                                      SubjectPublicKeyInfo  ::=  SEQUENCE  {
                                          algorithm         AlgorithmIdentifier,
                                          subjectPublicKey  BIT STRING
                                      }
                                      

                                      where subjectPublicKey is the secp256r1 public key as discussed earlier and algorithm is defined like this:

                                      AlgorithmIdentifier  ::=  SEQUENCE  {
                                          algorithm   OBJECT IDENTIFIER,
                                          parameters  ANY DEFINED BY algorithm OPTIONAL
                                      }
                                      

                                      Its algorithm OID is always 1.2.840.10045.2.1 (that is, id-ecPublicKey) and there’s a single key parameter, another OID, 1.2.840.10045.3.1.7, or secp256r1, which identifies a secp256r1 public key.

                                      IMPORTANT iOS tends to deal with raw keys whereas most other security toolkits, like OpenSSL, tend to deal with public keys wrapped in a SubjectPublicKeyInfo structure.

                                      Now, for a given EC key type the first N bytes of the SubjectPublicKeyInfo are fixed, hence the headerBytes array that jmarne uncovered earlier.  If you take those header bytes:

                                      30593013 06072A86 48CE3D02 0106082A
                                      8648CE3D 03010703 4200
                                      

                                      and slap them on the front of a secp256r1 public key.

                                      04
                                      B24DB122 E2DDDC97 FB0F58ED 7836F2CA
                                      6868C040 6B483FDA 473FDDD0 41D380B0
                                      4EDB6183 3273046A 33D7A9A8 D66B93AA
                                      A10B8732 93C68114 9CC7FA5E CD3523CE
                                      

                                      you end up with a file that you can dump with dumpasn1:

                                      $ dumpasn1 -p tmp.asn1
                                      SEQUENCE {
                                        SEQUENCE {
                                          OBJECT IDENTIFIER '1 2 840 10045 2 1'
                                          OBJECT IDENTIFIER ansiX9p256r1 (1 2 840 10045 3 1 7)
                                          }
                                        BIT STRING
                                          04 B2 4D B1 22 E2 DD DC 97 FB 0F 58 ED 78 36 F2
                                          CA 68 68 C0 40 6B 48 3F DA 47 3F DD D0 41 D3 80
                                          B0 4E DB 61 83 32 73 04 6A 33 D7 A9 A8 D6 6B 93
                                          AA A1 0B 87 32 93 C6 81 14 9C C7 FA 5E CD 35 23
                                          CE
                                        }
                                      

                                      And, lo!, a nicely wrapped EC public key suitable for use with OpenSSL and so on.

                                      WARNING This ‘prefix with a fixed header’ approach only works if the public key is of the right type and is not compressed.  In an ideal world you would build up the SubjectPublicKeyInfo structure properly, which would allow you to work with both compressed and uncompressed keys.  That would, however, require you to write some ASN.1 generation code.

                                      Share and Enjoy

                                      Quinn "The Eskimo!"
                                      Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                      let myEmail = "eskimo" + "1" + "@apple.com"

                                        • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                          Digital Leaves Level 1 Level 1 (0 points)

                                          hi Eskimo,

                                           

                                          Thank you so very much for this comprehensive explanation. it really makes a lot more sense now. I suspect the signature generated by the SecKeyRawSign has a similar issue with the wrapping that prevents it for being properly verified from OpenSSL, which is expecting some kind of ASN.1 wrapping?

                                           

                                          thanks again.

                                            • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                              eskimo Apple Staff Apple Staff (9,835 points)

                                              I suspect the signature generated by the SecKeyRawSign has a similar issue with the wrapping that prevents it for being properly verified from OpenSSL, which is expecting some kind of ASN.1 wrapping?

                                              When last I looked at that side of things I definitely had fun mapping OpenSSL’s padding options to those used by our Security framework.  I eventually got it working, see the CryptoCompatibility sample code, but that was for RSA, not EC.

                                              Share and Enjoy

                                              Quinn "The Eskimo!"
                                              Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                              let myEmail = "eskimo" + "1" + "@apple.com"

                                                • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                  eskimo Apple Staff Apple Staff (9,835 points)

                                                  When last I looked at that side of things I definitely had fun mapping OpenSSL’s padding options to those used by our Security framework.  I eventually got it working, see the CryptoCompatibility sample code, but that was for RSA, not EC.

                                                  I had cause to play around with this today and managed to implement an end-to-end test from iOS to OpenSSL:

                                                  1. iOS app generates an EC key pair with the private key in the Secure Enclave

                                                  2. iOS app exports the public key

                                                  3. iOS app signs some data with the private key and prints the data, the signature, and the public key

                                                  4. on the Mac I put that data into various files

                                                  5. I prepended the SubjectPublicKeyInfo to the raw EC public key

                                                  6. I converted the public key to PEM (see below)

                                                  7. I then verified the public key with OpenSSL’s command line tool (see below)


                                                  $ openssl ec -pubin -inform DER -in EC\ key.asn1 -pubout -outform PEM -out EC\ key.pem
                                                  read EC key
                                                  writing EC key
                                                  

                                                  $ openssl dgst -ecdsa-with-SHA1 -verify EC\ key.pem -signature signature.dat dataToSign.dat
                                                  Verified OK
                                                  

                                                  While working on this I encountered two gotchas:

                                                  • When verifying the signature, you have to use -ecdsa-with-SHA1 because OpenSSL treats -sha1 as implying RSA.

                                                  • When calling SecKeyRawSign with an EC key, you have to use a padding of kSecPaddingPKCS1 because kSecPaddingPKCS1SHA1 implies RSA (probably for the same reason).

                                                  Share and Enjoy

                                                  Quinn "The Eskimo!"
                                                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                  let myEmail = "eskimo" + "1" + "@apple.com"

                                                    • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                      Digital Leaves Level 1 Level 1 (0 points)

                                                      Hi Eskimo,

                                                       

                                                      Thanks. Unfortunately, it's not working on my side. I knew that I had to use ecdsa-with-SHA1, and tried with both PKCS1SHA1 and PKCS1 with no luck.

                                                       

                                                      I have the following scenario:

                                                      1. I generate an EC keypair with secure enclave, private key is held there, public key is extracted as NSData after adding to the keychain, pre-pended with the SubjectPublicKeyInfo containing the ASN.1 data, and sent to the server.

                                                       

                                                              let privateKeyParams: [String: AnyObject] = [
                                                                  kSecAttrAccessControl as String: accessControl,
                                                                  kSecAttrIsPermanent as String: true,
                                                                  kSecAttrApplicationTag as String: "privateAppTag"
                                                              ]
                                                         
                                                              let publicKeyParams: [String: AnyObject] = [
                                                                  kSecAttrApplicationTag as String: "publicAppTag"
                                                              ]
                                                          
                                                              let parameters: [String: AnyObject] = [
                                                                  kSecAttrKeyType as String:          kSecAttrKeyTypeEC,
                                                                  kSecAttrKeySizeInBits as String:    256,
                                                                  kSecAttrTokenID as String:          kSecAttrTokenIDSecureEnclave,
                                                                  kSecPublicKeyAttrs as String:       publicKeyParams,
                                                                  kSecPrivateKeyAttrs as String:      privateKeyParams
                                                              ]
                                                         
                                                              dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
                                                                  var pubKey, privKey: SecKeyRef?
                                                                  let status = SecKeyGeneratePair(parameters, &pubKey, &privKey)
                                                                  if status == errSecSuccess {
                                                                     // add public key to keychain, prepend SubjectPublicKeyInfo to raw data and send to the server.
                                                                  } else {
                                                                     // handle error
                                                                  }
                                                              }
                                                          }
                                                      
                                                      
                                                      
                                                      

                                                       

                                                      2. The server receives the key, properly parses it and converts it to PEM. I know the key is successfully processed because openSSL recognizes it:

                                                      $ openssl ec -pubin -noout -text -in public_key.pem
                                                      read EC key
                                                      pub:
                                                          04:1f:48:a0:df:0f:ca:c9:95:59:91:65:e2:d0:bf:
                                                          4c:03:53:a0:f7:c1:a4:e6:c4:37:e9:bc:0a:af:1e:
                                                          ab:a2:9a:1d:5f:33:1a:fb:e5:30:e8:7e:b6:49:e1:
                                                          01:2c:83:7a:55:39:39:87:4c:97:3d:f3:a0:c3:51:
                                                          b6:ad:12:77:58
                                                      ASN1 OID: prime256v1
                                                      
                                                      
                                                      
                                                      

                                                       

                                                      This is the ASN.1 dump of the key:

                                                       

                                                      $ openssl asn1parse -in public_key.pem
                                                          0:d=0  hl=2 l=  89 cons: SEQUENCE
                                                          2:d=1  hl=2 l=  19 cons: SEQUENCE
                                                          4:d=2  hl=2 l=   7 prim: OBJECT            :id-ecPublicKey
                                                         13:d=2  hl=2 l=   8 prim: OBJECT            :prime256v1
                                                         23:d=1  hl=2 l=  66 prim: BIT STRING
                                                      
                                                      
                                                      
                                                      

                                                       

                                                      3. I sign a plain text (from the iOS side) to send to the server for verification. I use SecKeyRawSign with SecPadding.PKCS1. Data is a SHA1 of the plain text using the CC_SHA1 function.

                                                       

                                                      signing plain text: haGXtJfQBLPmUlCW+ArdtOiBW4Yckv8hCJhhrGKcwo8yj46o0YgaQg==
                                                      Result: 0. Generated signature: <30460221 00ed3648 6788fa05 1eff6b3b 0f09438a 4032a358 2bcd37b9 f3db9429 497a12f6 7e022100 a4ff57e1 6cb763c7 a7f2f0d4 b44add73 e939019c 4eb75df6 36590f22 2ce29258>
                                                      
                                                      
                                                      
                                                      

                                                       

                                                      After that I do a verification in iOS that passes OK (just to be sure). I use the same CC_SHA1 function to get the digest and the same padding.

                                                      Verifying signature...
                                                      Text to verify: haGXtJfQBLPmUlCW+ArdtOiBW4Yckv8hCJhhrGKcwo8yj46o0YgaQg==
                                                      Signature to verify: Optional(<30460221 00ed3648 6788fa05 1eff6b3b 0f09438a 4032a358 2bcd37b9 f3db9429 497a12f6 7e022100 a4ff57e1 6cb763c7 a7f2f0d4 b44add73 e939019c 4eb75df6 36590f22 2ce29258>)
                                                      Verifying self signature with public key:
                                                      text as data:<68614758 744a6651 424c506d 556c4357 2b417264 744f6942 57345963 6b763868 434a6868 72474b63 776f3879 6a34366f 30596761 51673d3d>
                                                      signature as data:<30460221 00ed3648 6788fa05 1eff6b3b 0f09438a 4032a358 2bcd37b9 f3db9429 497a12f6 7e022100 a4ff57e1 6cb763c7 a7f2f0d4 b44add73 e939019c 4eb75df6 36590f22 2ce29258>
                                                      Key: <SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 3, block size: 256 bits, y: 587712ADB651C3A0F33D974C873939557A832C01E149B67EE830E5FB1A335F1D01, x: 9AA2AB1EAF0ABCE937C4E6A4C1F7A053034CBFD0E265915995C9CA0FDFA0481F587712ADB651C3A0F33D974C873939557A832C01E149B67EE830E5FB1A335F1D01, addr: 0x135a20870>
                                                      Status: 0 (errSecSuccess)
                                                      
                                                      
                                                      
                                                      

                                                       

                                                      4. However, if I get the signature data, copy and paste it on a binary file, and verify it with openssl (same command as you used):

                                                       

                                                      openssl dgst -ecdsa-with-SHA1 -verify public_key.pem -signature signature.bin plaintext.txt
                                                      
                                                      Verification Failure
                                                      
                                                      
                                                      
                                                      

                                                       

                                                       

                                                      If I generate the keypair using openSSL, sign and verify, everything goes fine:

                                                      1. create the private EC key w/ public key.
                                                      openssl ecparam -genkey -name prime256v1 -noout -out myprivatekey.pem
                                                      2. generate the public EC key from the private EC key
                                                      openssl ec -in myprivatekey.pem -pubout -out mypubkey.pem
                                                      3. Sign the plaintext.txt file
                                                      openssl dgst -ecdsa-with-SHA1 -sign myprivatekey.pem plaintext.txt > signature.bin
                                                      4. Verify signature
                                                      openssl dgst -ecdsa-with-SHA1 -verify mypubkey.pem -signature signature.bin plaintext.txt
                                                      
                                                      Verified OK
                                                      
                                                      
                                                      
                                                       

                                                       

                                                      The Swift signing code is really simple, I think, and I don't know where the problem may be:

                                                       

                                                      let resultData       = NSMutableData(length: 1024)!
                                                      let resultPointer    = UnsafeMutablePointer<UInt8>(resultData.mutableBytes)
                                                      var resultLength     = resultData.length
                                                      
                                                      if let plainData = message.dataUsingEncoding(NSUTF8StringEncoding) {
                                                          /
                                                          let hashData = NSMutableData(length: Int(CC_SHA1_DIGEST_LENGTH))!
                                                          let hash = UnsafeMutablePointer<UInt8>(hashData.mutableBytes)
                                                          CC_SHA1(UnsafePointer<Void>(plainData.bytes), CC_LONG(CC_SHA1_DIGEST_LENGTH), hash)
                                                      
                                                          let status = SecKeyRawSign(privateKeyRef, SecPadding.PKCS1, hash, hashData.length, resultPointer, &resultLength)
                                                          if status != errSecSuccess {
                                                              error = .UnableToEncrypt
                                                          } else {
                                                              resultData.length = resultLength
                                                              print("Result: \(status). Generated signature: \(resultData)")
                                                              // base64 encode resultData and send to the server for verification.
                                                      
                                                          }
                                                      } else { error = .WrongInputDataFormat }
                                                      
                                                      
                                                      
                                                      

                                                       

                                                      I really don't know what can be wrong or how to get out of this. Thanks in advance for your time.

                                                       

                                                      Best

                                                        • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                          eskimo Apple Staff Apple Staff (9,835 points)

                                                          Unfortunately, it's not working on my side.

                                                          Bummer.  I couldn’t see anything obviously wrong with your technique.  Please drop me a line at my individual email address (in my signature, below).

                                                          Share and Enjoy

                                                          Quinn "The Eskimo!"
                                                          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                          let myEmail = "eskimo" + "1" + "@apple.com"

                                                            • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                              Ronnyk Level 1 Level 1 (0 points)

                                                              Hi Eskimo.

                                                              I am having the same problem her. I have done

                                                              • Created the key in the secure element.
                                                              • Made a signature with kSecPaddingPKCS1 of a SHA1 hashed plain text.
                                                              • Verified it on the Iphone sucessfully.
                                                              • Exported the public key sucessfully with the header.(Looks OK using a ASN.1 dump)

                                                               

                                                              And I am completely unable to verify this signature using either openssl nor a java program....

                                                               

                                                              Would you mind sharing the data you are doing your experiment on ? You are not ecplictly stading that you do make a SHA-1 hash before signing and that you place the plaintext into the file for openssl validation, but I assume you are ?

                                                              I.e. what do you sign ?

                                                              And do you make any special provisions when putting this on a file for openSSL verification (Like to compensate for end of file marker ?)

                                                              In addition what version of open SSL are you using ?

                                                               

                                                              Thaks for your time...

                                                               

                                                              Ronny

                                                                • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                                  eskimo Apple Staff Apple Staff (9,835 points)

                                                                  You are not ecplictly stading that you do make a SHA-1 hash before signing and that you place the plaintext into the file for openssl validation, but I assume you are ?

                                                                  Correct.  In the example I posted:

                                                                  • EC key.pem is the concatenation of the ‘standard’ EC header, EC key header.dat, and the EC key from iOS, EC key raw.dat

                                                                  • signature.dat was the output from SecKeyRawSign on iOS

                                                                  • dataToSign.dat was a bunch of random bytes that’s my test ‘message’

                                                                  The paste dumps are pasted in below.

                                                                  And do you make any special provisions when putting this on a file for openSSL verification (Like to compensate for end of file marker ?)

                                                                  No.

                                                                  In addition what version of open SSL are you using ?

                                                                  The one built in to OS X 10.11.

                                                                  Share and Enjoy

                                                                  Quinn "The Eskimo!"
                                                                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                                  let myEmail = "eskimo" + "1" + "@apple.com"


                                                                  $ hexdump -v EC\ key\ header.dat
                                                                  0000000 30 59 30 13 06 07 2a 86 48 ce 3d 02 01 06 08 2a
                                                                  0000010 86 48 ce 3d 03 01 07 03 42 00
                                                                  

                                                                  $ hexdump -v EC\ key\ raw.dat
                                                                  0000000 04 5a e5 3a da 6e b3 21 53 a4 0e 6b de bc 54 bb
                                                                  0000010 6d 89 c2 6c 22 8a 6a 82 47 96 57 5a e3 a0 a4 dc
                                                                  0000020 17 0c fb ce 4a f2 ed 34 a5 62 77 04 f7 b9 2e 5b
                                                                  0000030 38 61 74 81 c0 a3 62 63 79 74 0e 67 af dd df 74
                                                                  0000040 ab
                                                                  

                                                                  $ hexdump -v signature.dat
                                                                  0000000 30 44 02 20 09 57 a8 25 a4 60 79 3b 00 24 06 81
                                                                  0000010 81 7a c6 f8 81 29 96 65 ca dc da 7d f6 15 ef c2
                                                                  0000020 68 d2 b9 d9 02 20 22 05 cd d8 a8 28 91 85 9c 31
                                                                  0000030 1f c8 a4 57 cc 94 42 68 73 94 05 fd 54 b3 ba 39
                                                                  0000040 16 a4 eb c0 8a 9c
                                                                  

                                                                  $ hexdump -v dataToSign.dat
                                                                  0000000 2f 8d e6 68 42 19 d4 5b e2 e3 30 c8 c6 2c e1 0b
                                                                  0000010 69 4c 9c af c4 82 ee d5 67 e0 15 49 72 c8 60 4a
                                                                  0000020 4b 31 2d 9f 94 89 c5 49 f3 6e b9 ab b7 ae 76 52
                                                                  0000030 5a 56 d8 c4 c6 15 17 50 db 72 83 f4 c3 76 f8 fe
                                                                  0000040 8e db 8a b5 6c 62 7b 5f 33 87 57 47 53 1c 69 b5
                                                                  0000050 f5 e9 40 fc c8 fb d0 62 76 51 13 c5 8e fd b9 42
                                                                  0000060 dc a9 c7 68 47 ea dc 4f 7f b1 f5 04 fb 53 a3 96
                                                                  0000070 f4 58 88 9d 2d fd 13 ff 0a b4 d7 2b 5f f4 f2 ce
                                                                  0000080 9f 31 25 d9 fa 86 27 2e 71 36 ce 99 8a c3 0e b8
                                                                  0000090 9b 1c d6 be 63 3c a7 6d db 6e 7c da 53 40 83 03
                                                                  00000a0 ec f5 d3 fb e9 1c ad a9 b1 5f 9b dd 79 d7 45 d2
                                                                  00000b0 16 0c c9 2f 62 a6 78 99 8d ea fb 50 21 42 90 81
                                                                  00000c0 2b 7f 3e d7 56 52 43 9c 81 21 40 39 50 7d d6 a6
                                                                  00000d0 b9 d9 1c a7 b6 0c 05 95 5c f5 1e 20 84 ce f9 5e
                                                                  00000e0 89 00 83 e9 08 7f 3c 96 f0 f6 90 e3 d4 e8 7c 4e
                                                                  00000f0 02 28 8a bf ba 2d ba 91 dc 67 f8 6d bd 50 ef fe
                                                                  0000100 db 31 5e 05 6f cc b8 26 b7 2c 3e aa f2 32 56 5b
                                                                  0000110 18 ae 98 d0 a5 27 85 62 78 49 51 79 88 05 5d e8
                                                                  0000120 10 dc 79 fe 1f f2 a9 37 3f e1 c3 4c fb cb 18 26
                                                                  0000130 2e 35 cb 55 d6 e6 06 db 5c 98
                                                                  

                                                                    • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                                      Ronnyk Level 1 Level 1 (0 points)

                                                                      Thanks Eskimo.

                                                                      I did not verify this using openssl but I did verify it using a java program so I kind of doubth that it will not work using openssl. In my case I must have messed up with the public key by running this program to often at not deleting the public key placed in keystore or maybe one other suspect which is the way you do hasing for small data. I started out using.

                                                                       

                                                                                     CC_SHA1_Init(&SHA1);

                                                                                      CC_SHA1_Update(&SHA1, data , usedLen);

                                                                                      unsigned char digest[CC_SHA1_DIGEST_LENGTH];

                                                                                      memset(digest, 0, CC_SHA1_DIGEST_LENGTH);

                                                                                      CC_SHA1_Final(digest, &SHA1);

                                                                       

                                                                      And substituded this to

                                                                                         CC_SHA1(data,usedLen,digest);

                                                                       

                                                                      I kind of doubth if this was the problem, but since I am not that convinced that I actually messed up with the public key (since I do belive to have verified the signature with both the generated ans stored key) either I tougth to mention it for some body  else on the verge of a deep depression.

                                                                       

                                                                      So given that you follow Eskimo´s procedure it does work and as you can see I am using Objective C while Eskimo has done this in Swift.

                                                                      One other strage thing I experienced was that the private key was only good for one go..If you intend to use it again you must retrive it again. Failing to do so will produce a strange delay and a strage error message about credentials.

                                                                        • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                                          eskimo Apple Staff Apple Staff (9,835 points)

                                                                          So given that you follow Eskimo´s procedure it does work …

                                                                          Yay!

                                                                          One other strage thing I experienced was that the private key was only good for one go..If you intend to use it again you must retrive it again. Failing to do so will produce a strange delay and a strage error message about credentials.

                                                                          Well, that’s weird.  I suspect it’s related to the Touch ID integration.  Please file a bug about this, then post your bug number, just for the record.

                                                                          Share and Enjoy

                                                                          Quinn "The Eskimo!"
                                                                          Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                                          let myEmail = "eskimo" + "1" + "@apple.com"

                                                                            • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                                              Ezonw Level 1 Level 1 (0 points)

                                                                              Hi Eskimo and all,

                                                                              thank you for contributing to this thread. Lot of good information. I have read through this discussion multiple times to understand if I'm doing anything wrong.
                                                                              I'm basically running into the same problem that users in this thread ran into, i.e. verifying the signed content outside iOS.
                                                                              Verification is failing in both java program (BouncyCastle libs and JDK7) and openssl.

                                                                              The keypair was generated using with keytype as kSecAttrKeyTypeEC. See the code below.

                                                                              I'm getting dataToSign, signature and public key from a iOS device.
                                                                              On Java side  or OpenSSL (after adding the header) I'm able to parse the public key with no issues.

                                                                              Here is the section that is used to generate the key pair.

                                                                                      var publicKey: SecKey? = nil
                                                                                      let publicKeyAttrs: [String: AnyObject] = [
                                                                                        kSecAttrKeyClass        as String : kSecAttrKeyClassPublic,
                                                                                        kSecAttrApplicationTag as String : uuidStr
                                                                                      ]
                                                                                      var privateKey: SecKey? = nil
                                                                                      let err = SecKeyGeneratePair([
                                                                                          kSecAttrTokenID as String:          kSecAttrTokenIDSecureEnclave,
                                                                                          kSecAttrKeyType as String:          kSecAttrKeyTypeEC,
                                                                                          kSecAttrKeySizeInBits as String:    256,
                                                                                          kSecAttrApplicationTag as String:   uuidStr,
                                                                                          kSecPrivateKeyAttrs as String: [
                                                                                              kSecAttrIsPermanent as String:  true,
                                                                                              kSecAttrAccessControl as String:access,
                                                                                          ],
                                                                                          kSecPublicKeyAttrs as String: publicKeyAttrs
                                                                                      ] as NSDictionary, &publicKey, &privateKey)
                                                                                     
                                                                              and code to sign the content...
                                                                              ...
                                                                                    let stringToSign: String = "SignThisString"
                                                                                    let dataToSign: NSMutableData = stringToSign.dataUsingEncoding(NSUTF8StringEncoding)! as! NSMutableData
                                                                                     NSLog("dataToSign %@", dataToSign)
                                                                                  
                                                                                      let signature = NSMutableData(length: 128)!
                                                                                      var signatureLength = signature.length
                                                                                /* Tried various padding options i.e. .PKCSSHA1, .NONE*/
                                                                                      let signErr = SecKeyRawSign(privateKey, .PKCS1, UnsafePointer(dataToSign.bytes), dataToSign.length, UnsafeMutablePointer(signature.mutableBytes), &signatureLength)
                                                                                      guard signErr == errSecSuccess else
                                                                                          NSLog("verify sign error %d", signErr)

                                                                                     
                                                                                      NSLog("signature %@", signature)
                                                                                      NSLog("signatureLength %d", signatureLength)

                                                                               

                                                                              Here is the data I'm trying to verify using a java program. The below data was output from iOs program.


                                                                                String dataToSignInHex="22f3e5d2d0f6031340f987a979dba76b0f0c1a7c"; 
                                                                                String publicKeyInHex="046052c59c1cc8aa3a48b33ec9908f8332af82402ace8754eabf087918a94a18446ddb01f4fef4936784200e6f4b66b0079f7fa363a42b5828e15c1c265e260e04";
                                                                                String sigInHex="3044022021250762bbb601867165181ba1b8462cc4c52dac7bb895764068b7fa38d46014022054ee80cead07b1f022242e158a17cb097a9ee7520ed7f3bf8c4695f1a5ccfb8f";

                                                                              Passed public key info

                                                                              Algorithm=ECDSA
                                                                              getFormat=X.509
                                                                              getEncoded=[B@5b4ec310
                                                                              sigBytes Length=70
                                                                              parsed pk_in_hex=308201333081ec06072a8648ce3d02013081e0020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff30440420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b0441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020101034200046052c59c1cc8aa3a48b33ec9908f8332af82402ace8754eabf087918a94a18446ddb01f4fef4936784200e6f4b66b0079f7fa363a42b5828e15c1c265e260e04

                                                                              Verification keeps failing in both Java and OpenSSL. Not sure what we are missing. Greatly appreciate your help.

                                                                                • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                                                  eskimo Apple Staff Apple Staff (9,835 points)

                                                                                  Verification is failing in both java program (BouncyCastle libs and JDK7) and openssl.

                                                                                  I had cause to look at this again and modified my code to make it easier to test things.  To start, here’s the code I used to generate the key.

                                                                                  func generate() {
                                                                                      let uuidStr = NSUUID().UUIDString
                                                                                  
                                                                                      let access = SecAccessControlCreateWithFlags(
                                                                                          nil,
                                                                                          kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
                                                                                          [.TouchIDAny, .PrivateKeyUsage],
                                                                                          nil
                                                                                      )!
                                                                                  
                                                                                      var publicKey: SecKey? = nil
                                                                                      var privateKey: SecKey? = nil
                                                                                      let err = SecKeyGeneratePair([
                                                                                          kSecAttrTokenID as String:          kSecAttrTokenIDSecureEnclave,
                                                                                          kSecAttrKeyType as String:          kSecAttrKeyTypeEC,
                                                                                          kSecAttrKeySizeInBits as String:    256,
                                                                                          kSecAttrApplicationTag as String:  uuidStr,
                                                                                          kSecPrivateKeyAttrs as String: [
                                                                                              kSecAttrIsPermanent as String:  true,
                                                                                              kSecAttrAccessControl as String:access,
                                                                                          ]
                                                                                      ] as NSDictionary, &publicKey, &privateKey)
                                                                                      if err != errSecSuccess {
                                                                                          NSLog("generate error %d", err)
                                                                                      } else {
                                                                                          NSLog("generate success")
                                                                                  
                                                                                          let addErr = SecItemAdd([
                                                                                              kSecClass as String:                kSecClassKey,
                                                                                              kSecValueRef as String:            publicKey!,
                                                                                              kSecAttrApplicationTag as String:  uuidStr
                                                                                          ] as NSDictionary, nil)
                                                                                          if addErr != errSecSuccess {
                                                                                              NSLog("add error %d", addErr)
                                                                                          } else {
                                                                                              NSLog("add success")
                                                                                          }
                                                                                      }
                                                                                  }
                                                                                  

                                                                                  Here’s the top-level signing code:

                                                                                  func sign() {
                                                                                      // Create some unique data to sign.
                                                                                  
                                                                                      let stringToSign = NSString(format: "This string was signed after %@.", NSDate())
                                                                                      let dataToSign = stringToSign.dataUsingEncoding(NSUTF8StringEncoding)!
                                                                                  
                                                                                      // Start off our shell script with some lines that put that data in "dataToSign.dat".
                                                                                  
                                                                                      var shellScriptLines = [ "#! /bin/sh" ]
                                                                                      shellScriptLines.append(String(format: "echo %@ | xxd -r -p > dataToSign.dat", QHex.hexStringWithData(dataToSign)))
                                                                                  
                                                                                      // Now add lines for each key.
                                                                                  
                                                                                      for (_, (publicKey: publicKey, privateKey: privateKey)) in self.keyPairsByUUIDStr() {
                                                                                          _ = try? self.signData(dataToSign, withPrivateKey: privateKey, publicKey: publicKey, shellScriptLines: &shellScriptLines)
                                                                                      }
                                                                                  
                                                                                      // Finally print the script.
                                                                                  
                                                                                      for l in shellScriptLines {
                                                                                          print(l)
                                                                                      }
                                                                                  }
                                                                                  

                                                                                  This uses two helpers:

                                                                                  • keyPairsByUUIDStr() iterates through all the keys in the keychain returning public and private keys matched by their UUID.  If you’re only using one key then this level of complexity is unnecessary, so I haven’t included this code here.

                                                                                  • signData(xxx), which actually does the signing.  It shown below.

                                                                                  -

                                                                                  func signData(dataToSign: NSData, withPrivateKey privateKey: SecKey, publicKey: SecKey, inout shellScriptLines: [String]) throws {
                                                                                      let digestToSign = CommonCryptoAccess.sha1DigestForData(dataToSign)
                                                                                      // let digestToSign = CommonCryptoAccess.sha256DigestForData(dataToSign)
                                                                                  
                                                                                      // There's no reliable API to get the correct signature buffer length to use
                                                                                      // <rdar://problem/23128926> so we hard code 128 as the theoretical maximum.
                                                                                  
                                                                                      let signature = NSMutableData(length: 128)!
                                                                                      var signatureLength = signature.length
                                                                                      let signErr = SecKeyRawSign(privateKey, .PKCS1, UnsafePointer(digestToSign.bytes), digestToSign.length, UnsafeMutablePointer(signature.mutableBytes), &signatureLength)
                                                                                      guard signErr == errSecSuccess else {
                                                                                          NSLog("verify sign error %d", signErr)
                                                                                          throw NSError(domain: NSOSStatusErrorDomain, code: Int(signErr), userInfo: nil)
                                                                                      }
                                                                                      signature.length = signatureLength
                                                                                  
                                                                                      // Add a command to create "signature.dat".
                                                                                  
                                                                                      shellScriptLines.append(String(format: "echo %@ | xxd -r -p > signature.dat", QHex.hexStringWithData(signature)))
                                                                                  
                                                                                      var matchResult: AnyObject? = nil
                                                                                      let err = SecItemCopyMatching([
                                                                                          kSecClass as String:                kSecClassKey,
                                                                                          kSecValueRef as String:            publicKey,
                                                                                          kSecReturnData as String:          true
                                                                                      ] as NSDictionary, &matchResult)
                                                                                      if err != errSecSuccess {
                                                                                          NSLog("match error %d", err)
                                                                                      } else if let keyRaw = matchResult as? NSData {
                                                                                  
                                                                                          // We take the raw key and prepend an ASN.1 prefix to it.  The end result is an
                                                                                          // ASN.1 SubjectPublicKeyInfo structure, which is what OpenSSL is looking for.
                                                                                          //
                                                                                          // See the following DevForums post for more details on this.
                                                                                          //
                                                                                          // <https://forums.developer.apple.com/message/84684#84684>.
                                                                                  
                                                                                          let keyHeader = QHex.dataWithValidHexString("3059301306072a8648ce3d020106082a8648ce3d030107034200")
                                                                                          let keyASN1 = NSMutableData(data: keyHeader)
                                                                                          keyASN1.appendData(keyRaw)
                                                                                  
                                                                                          // Convert the key to Base64 then wrap it up as "key.pem".
                                                                                  
                                                                                          let keyBase64 = keyASN1.base64EncodedStringWithOptions([.Encoding64CharacterLineLength])
                                                                                          shellScriptLines.append("cat > key.pem <<EOF")
                                                                                          shellScriptLines.append("-----BEGIN PUBLIC KEY-----")
                                                                                          shellScriptLines.appendContentsOf( keyBase64.componentsSeparatedByString("\r\n") )
                                                                                          shellScriptLines.append("-----END PUBLIC KEY-----")
                                                                                          shellScriptLines.append("EOF")
                                                                                      } else {
                                                                                          NSLog("match cast problem")
                                                                                      }
                                                                                  
                                                                                      // Add a command to verify the signature.
                                                                                      //
                                                                                      // IMPORTANT: -ecdsa-with-SHA1 is a bit of a hack that works in the ancient version
                                                                                      // of OpenSSL that's built in to OS X.  Alas, ecdsa-with-SHA256 does not work. If
                                                                                      // you need to use a long digest, SHA256 say, you should replace this with "-sha256". 
                                                                                      // That won't work on OS X but will work with other, more modern versions of OpenSSL,
                                                                                      // where it detects the key type and does an ECDSA digest.
                                                                                  
                                                                                      shellScriptLines.append("openssl dgst -ecdsa-with-SHA1 -verify key.pem -signature signature.dat dataToSign.dat")
                                                                                  }
                                                                                  

                                                                                  Finally, here’s the wrappers I use to access Common Crypto from Swift.

                                                                                  + (NSData *)sha1DigestForData:(NSData *)data {
                                                                                      NSMutableData *    result;
                                                                                  
                                                                                      result = [[NSMutableData alloc] initWithLength:CC_SHA1_DIGEST_LENGTH];
                                                                                      assert(result != nil);
                                                                                  
                                                                                      CC_SHA1(data.bytes, (CC_LONG) data.length, result.mutableBytes);
                                                                                  
                                                                                      return result;
                                                                                  }
                                                                                  
                                                                                  + (NSData *)sha256DigestForData:(NSData *)data {
                                                                                      NSMutableData *    result;
                                                                                  
                                                                                      result = [[NSMutableData alloc] initWithLength:CC_SHA256_DIGEST_LENGTH];
                                                                                      assert(result != nil);
                                                                                  
                                                                                      CC_SHA256(data.bytes, (CC_LONG) data.length, result.mutableBytes);
                                                                                  
                                                                                      return result;
                                                                                  }
                                                                                  

                                                                                  The end result here is a shell script that you can literally paste into a Terminal window to verify the key using the OpenSSL built in to OS X.  To ensure that there wasn’t anything OS X-specific in play here, I ran the same shell script on Ubuntu 14.04.02.  Verifying it using other crypto toolkits is left as an exercise for the reader (-:

                                                                                  Share and Enjoy

                                                                                  Quinn "The Eskimo!"
                                                                                  Apple Developer Relations, Developer Technical Support, Core OS/Hardware
                                                                                  let myEmail = "eskimo" + "1" + "@apple.com"

                                                                                  DTS will close for the winter holidays at the end of business on Wed, 23 Dec 2015 and re-open on Mon, 4 Jan 2016.

                                                                              • Re: Can't export EC kSecAttrTokenIDSecureEnclave public key
                                                                                Ezonw Level 1 Level 1 (0 points)

                                                                                Hi Ronny,

                                                                                 

                                                                                I see that you figured out how to validate the signature using Java. Can you please shed some light what I could be doing wrong.

                                                                                I'm using BouncyCastle libraries. Here is the code to parse the public key. Does this look right?

                                                                                 

                                                                                public PublicKey decodePublicKey(byte[] encodedPublicKey) throws U2FException {
                                                                                    try {
                                                                                      X9ECParameters curve = SECNamedCurves.getByName("secp256r1");
                                                                                      ECPoint point;
                                                                                      try {
                                                                                        point = curve.getCurve().decodePoint(encodedPublicKey);
                                                                                      } catch (RuntimeException e) {
                                                                                        throw new U2FException("Couldn't parse user public key", e);
                                                                                      }

                                                                                      return KeyFactory.getInstance("ECDSA").generatePublic(
                                                                                          new ECPublicKeySpec(point,
                                                                                              new ECParameterSpec(
                                                                                                  curve.getCurve(),
                                                                                                  curve.getG(),
                                                                                                  curve.getN(),
                                                                                                  curve.getH())));
                                                                                    } catch (InvalidKeySpecException e) {
                                                                                      throw new U2FException("Error when decoding public key", e);
                                                                                    } catch (NoSuchAlgorithmException e) {
                                                                                      throw new U2FException("Error when decoding public key", e);
                                                                                    }
                                                                                  }