SecKeyDecrypt returns errSecParam

Hi,


I am migrating my project from Xcode 10.1 to Xcode 11.4. One of the function in the project is to perform RSA encryption/decryption. It works well in Xcode 10.1. However in Xcode 11.4, it starts to return errSecParam randomly. No error when I run the app in debug mode. Also, in release mode, I have tried to turn off all optimizations but the same issue still occurs. The following is the details of the issue.


let status = SecKeyDecrypt(privateKey, SecPadding.PKCS1, chunkData, length, &decryptedDataBuffer, &decryptedDataLength)

where the legnth of chunkData is 256, legnth = 256, the length of decryptedDataBuffer is 256 with all 0 in the array and decryptedDataLength 256.


I would like to ask what will cause SecKeyDecrypt function returns errSecParam? And is there any change for this function in iOS 13 SDK?


Thanks for your help!

During a migration, the most common cause of any cryptography API to be producing an error is an issue with one of your inputs that wasn't set like you expected. This is very common is Swift. Unless there is more information to share I would go back and triple check that your inputs are what you expect them to be.


Matt Eaton

DTS Engineering, CoreOS

meaton3 at apple.com

Hi Matt,


Thanks for your reply. I would like to supplement the parameters I pass to the function SecKeyDecrypt.


privateKey: I use SecKeyCopyExternalRepresentation(_, _) and then base64 to encode the data and show it here. The value is

MIIEpAIBAAKCAQEAxBcSSEHU7kyc03AnrNOzPyPs4nXb/37GehrqVOrIgFJSU6RRaHagVRj6Ujw8Ldo+i0UhoR6KrP7RB7utgJVpgIIXfoYkyxpKXtfPq+JfwNbDF18OwzKlEd+56oPVi9SFP8GkJBIEx2zZux1KnmnePFe4ho15+tOFZfvrDwByTlUid5czHLIv/2n4qKc/8LeQIyFNOzKTz9kB4aAB/yyERwsnafo5hD5AFWFvCkdXvh8NhbAg0pJs4Zit7gEcH+alq9QYuxhmQp1BqInzLlrM05vouxWTXKCC8WGjdEHd15aZ+YgW7exjcEx/gGLm3X

chunkData: 256 bytes. I encode it to base64 string and show it here. The value is

vkl6vlEcTZwlgo/i8XZ1h1f93Er0QMOWusjulE+pFSN98LTqOB1IMYRop+0jWTbeQ0cIO6uFsN8QMvvdIZXNb3Jyvmb+XPh4eThXhPqfbCaefMDHgx0dLR1wjiQVeYhbxvPGGPzb9hcRtkHXWPDgA9lb376YAmWvuWB5fpeyVfTTfr3gsotnghPYQKdGQ7zYldNpkbkdYLbrMvx8SJvuC9rCWB5fUaxW8JdPYDWy/TL6hpuOZEsJwsR0tap32JZrYlRKfI3KcFmvp+MWDbjBBYdVBEWU/FE6kEwBhRVfHMKxec6wj5IJ0B8/LbMOL3ORGJs1qx61h31I+zTpoQwSAA=

length: 256

decryptedDataBuffer: [UInt8](repeating: 0, count: 256)

decryptedDataLength: 256

which 256 is retrieved by SecKeyGetBlockSize(privateKey)


Return status: -50 errSecParam


Thanks!


Regards,

Oscar

Oscar,


It looks like your base64 encoded values may have been corrupted when importing them into your reply. This can happen unfortunately. If you have a hex dump I can also try that. Thanks.


Matt Eaton

DTS Engineering, CoreOS

meaton3 at apple.com

Hi Matt,


Sorry about that. Seems the code block cannot contain a long text.


The private key in base 64 is:

MIIEpAIBAAKCAQEAxBcSSEHU7kyc03AnrNOzPyPs4nXb/37GehrqVOrIgFJSU6RRaHagVRj6Ujw8Ldo+i0UhoR6KrP7RB7utgJVpgIIXfoYkyxpKXtfPq+JfwNbDF18OwzKlEd+56oPVi9SFP8GkJBIEx2zZux1KnmnePFe4ho15+tOFZfvrDwByTlUid5czHLIv/2n4qKc/8LeQIyFNOzKTz9kB4aAB/yyEYn+ixbKyRwsnafo5hD5AFWFvCkdXvh8NhbAg0pJs4Zit7gEcH+alq9QYuxhmQp1BqInzLlrM05vouxWTXKCC8WGjdEHd15aZ+YgW7exjcEx/gGLm3XCsdikBhFq/fp++fwIDAQABAoIBACONlVSBjyRXwrV5Z9AnoB/KGTikKuvDpiPcfnFAEnNluaSNre71VzOmsDg50KV1pRJmSecnHPipyOeQiTA/pt3UeIMJumES1rokgfRFupvBtKSnqQsOQyye9lwFZDISqcg6CVM66x/nZgDNmkpnopIpS30l6KBoOo8XonJZX+BAsRsYoYZZV2HCP8fypAPbT2pCvSzykgYJ5slfMqXWHpfuraBTKC7sgSK2spZ6pEtCM0SaiiKdH+7s04xmf5thK+GRzz2iTsQUV6f9vq9k6gwXrM2OKPzATFL0aO+ZHpQlDRWjJq51pO2zglx3iKuW9NxrNX+CcdIOTXuB5LGGQbkCgYEA4qbVuk5tAWEEpwmB6sL1cj03Ve0F8w7RbWGIBnrQ/xbjB51vkF6q1wB86VXK7eBj62wScDZYt0Lp25ksb43EAWTekLLWsbIQ3nkAVWlFlXcDeo355c8kaaUVdWbH4u2mTy0yKtzOGPhJbpQhDyVfQwYX/ZLnIev3R3JoPP/GnEkCgYEA3XsqfKbeXidvqMOZtzzVDyOEOXmBX354W3/ClJwofprQPxk1G2DIgEHaAwRLS/YXhHvmylspZYYdyKv7DeiORbtfQrGX+ZylVy9aRIJa8tkJwOnS5G8dmey3cJJdP3VJ9U5p4y69s64r29/bGMNeCEzVKavrLvzDnPfGDz8AtIcCgYEA4ewG8vSlt1ArwQy7EXVQD5VxiRcAbS0CGJO1cTAPD5aSqs6FnFSg2jtER5oa805OiyZooq2pdNMgJKLkJjYbgIA5UebSFsI2Cmx2pqyOjdPaBdlrlWULGLtTTnkrGcplklqsvQjn7v/ewnipE13fPQHf/ATom2mITRF/Uf5qzPECgYBmiJKOyqG/bPpgYs1S6zKno4WfJgUitGX71hW6vuBwXDN0QrM3TYME/Xd0VhSVwgP5LXMW00VC3ugC8rtzIOpclfXv5k+5Gy/gQOfFN6PrMVc2nAkp/9bwX4o870WED0SB8B9NMWJZQJTFYckImcl6hjdY52Ilipb0oDBs9nnZGQKBgQCxHwiN6c4ks/wKdAbVvzFr6HgXhbbJtBcYzDaLISzWB5/sRoC7C0/j9RX+sGtsgmOAxLdC3VoAeduBFXsAWcaJh7p6Qqjnaf3EbSMuOkP9hPjjOBIGmx57tl9FHGazPYqCV9L/JV2Q2uXvsu8vMY9koEjpZ2/andTAidMf+/pg6w==


The hex dump of the private key is:

308204A40201000282010100C417124841D4EE4C9CD37027ACD3B33F23ECE275DBFF7EC67A1AEA54EAC880525253A4516876A05518FA523C3C2DDA3E8B4521A11E8AACFED107BBAD8095698082177E8624CB1A4A5ED7CFABE25FC0D6C3175F0EC332A511DFB9EA83D58BD4853FC1A4241204C76CD9BB1D4A9E69DE3C57B8868D79FAD38565FBEB0F00724E55227797331CB22FFF69F8A8A73FF0B79023214D3B3293CFD901E1A001FF2C84627FA2C5B2B2470B2769FA39843E4015616F0A4757BE1F0D85B020D2926CE198ADEE011C1FE6A5ABD418BB1866429D41A889F32E5ACCD39BE8BB15935CA082F161A37441DDD79699F98816EDEC63704C7F8062E6DD70AC762901845ABF7E9FBE7F020301000102820100238D9554818F2457C2B57967D027A01FCA1938A42AEBC3A623DC7E7140127365B9A48DADEEF55733A6B03839D0A575A5126649E7271CF8A9C8E79089303FA6DDD4788309BA6112D6BA2481F445BA9BC1B4A4A7A90B0E432C9EF65C05643212A9C83A09533AEB1FE76600CD9A4A67A292294B7D25E8A0683A8F17A272595FE040B11B18A186595761C23FC7F2A403DB4F6A42BD2CF2920609E6C95F32A5D61E97EEADA053282EEC8122B6B2967AA44B4233449A8A229D1FEEECD38C667F9B612BE191CF3DA24EC41457A7FDBEAF64EA0C17ACCD8E28FCC04C52F468EF991E94250D15A326AE75A4EDB3825C7788AB96F4DC6B357F8271D20E4D7B81E4B18641B902818100E2A6D5BA4E6D016104A70981EAC2F5723D3755ED05F30ED16D6188067AD0FF16E3079D6F905EAAD7007CE955CAEDE063EB6C12703658B742E9DB992C6F8DC40164DE90B2D6B1B210DE79005569459577037A8DF9E5CF2469A5157566C7E2EDA64F2D322ADCCE18F8496E94210F255F430617FD92E721EBF74772683CFFC69C4902818100DD7B2A7CA6DE5E276FA8C399B73CD50F23843979815F7E785B7FC2949C287E9AD03F19351B60C88041DA03044B4BF617847BE6CA5B2965861DC8ABFB0DE88E45BB5F42B197F99CA5572F5A44825AF2D909C0E9D2E46F1D99ECB770925D3F7549F54E69E32EBDB3AE2BDBDFDB18C35E084CD529ABEB2EFCC39CF7C60F3F00B48702818100E1EC06F2F4A5B7502BC10CBB1175500F95718917006D2D021893B571300F0F9692AACE859C54A0DA3B44479A1AF34E4E8B2668A2ADA974D32024A2E426361B80803951E6D216C2360A6C76A6AC8E8DD3DA05D96B95650B18BB534E792B19CA65925AACBD08E7EEFFDEC278A9135DDF3D01DFFC04E89B69884D117F51FE6ACCF10281806688928ECAA1BF6CFA6062CD52EB32A7A3859F260522B465FBD615BABEE0705C337442B3374D8304FD7774561495C203F92D7316D34542DEE802F2BB7320EA5C95F5EFE64FB91B2FE040E7C537A3EB3157369C0929FFD6F05F8A3CEF45840F4481F01F4D3162594094C561C90899C97A863758E762258A96F4A0306CF679D91902818100B11F088DE9CE24B3FC0A7406D5BF316BE8781785B6C9B41718CC368B212CD6079FEC4680BB0B4FE3F515FEB06B6C826380C4B742DD5A0079DB81157B0059C68987BA7A42A8E769FDC46D232E3A43FD84F8E33812069B1E7BB65F451C66B33D8A8257D2FF255D90DAE5EFB2EF2F318F64A048E9676FDA9DD4C089D31FFBFA60EB


The data to decrypt in base 64 is:

vkl6vlEcTZwlgo/i8XZ1h1f93Er0QMOWusjulE+pFSN98LTqOB1IMYRop+0jWTbeQ0cIO6uFsN8QMvvdIZXNb3Jyvmb+XPh4eThXhPqfbCaefMDHgx0dLR1wjiQVeYhbxvPGGPzb9hcRtkHXWPDgA9lb376YAmWvuWB5fpeyVfTTfr3gsotnghPYQKdGQ7zYldNpkbkdYLbrMvx8SJvuC9rCWB5fUaxW8JdPYDWy/TL6hpuOZEsJwsR0tap32JZrYlRKfI3KcFmvp+MWDbjBBYdVBEWU/FE6kEwBhRVfHMKxec6wj5IJ0B8/LbMOL3ORGJs1qx61h31I+zTpoQwSAA==


The hex dump of the data to decrypt is:

BE497ABE511C4D9C25828FE2F176758757FDDC4AF440C396BAC8EE944FA915237DF0B4EA381D48318468A7ED235936DE4347083BAB85B0DF1032FBDD2195CD6F7272BE66FE5CF87879385784FA9F6C269E7CC0C7831D1D2D1D708E241579885BC6F3C618FCDBF61711B641D758F0E003D95BDFBE980265AFB960797E97B255F4D37EBDE0B28B678213D840A74643BCD895D36991B91D60B6EB32FC7C489BEE0BDAC2581E5F51AC56F0974F6035B2FD32FA869B8E644B09C2C474B5AA77D8966B62544A7C8DCA7059AFA7E3160DB8C1058755044594FC513A904C0185155F1CC2B179CEB08F9209D01F3F2DB30E2F7391189B35AB1EB5877D48FB34E9A10C1200


Also, I would like to supplement how I create and retrieve the private key.


func generateRSAKeyPair() throws -> (publicKey: SecKey, privateKey: SecKey)? {
    let privateAttributes = [String(kSecAttrIsPermanent): true,
                             String(kSecAttrApplicationTag): "my_private_key_tag"] as [String : Any]
    let publicAttributes = [String(kSecAttrIsPermanent): true,
                            String(kSecAttrApplicationTag): "my_public_key_tag"] as [String : Any]

    let pairAttributes = [String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
                          String(kSecAttrKeySizeInBits): 2048,
                          String(kSecPublicKeyAttrs): publicAttributes,
                          String(kSecPrivateKeyAttrs): privateAttributes] as [String : Any]

    var publicRef: SecKey?
    var privateRef: SecKey?
    let status = SecKeyGeneratePair(pairAttributes as CFDictionary, &publicRef, &privateRef)
    if status == noErr {
        return (publicRef!, privateRef!)
    } else {
        throw RSAError.failToGenerateRSAKey
    }
}


func retrievePrivateKey() throws -> Data? {
    var keyRef: AnyObject?
    let query: Dictionary<String, AnyObject> = [
           String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
           String(kSecReturnData): kCFBooleanTrue as CFBoolean,
           String(kSecClass): kSecClassKey as CFString,
           String(kSecAttrApplicationTag): "my_private_key_tag" as CFString,
    ]

    let status = SecItemCopyMatching(query as CFDictionary, &keyRef)
    if status == noErr { return keyRef as? Data }
    else { throw RSAError.failToRetrievePrivateKey }
}

I would also like to chime in that we've noticed this issue occuring lately (possibly after upgrading to 11.4). There's also several people using a Security wrapper that noticed this started occuring around the same time

https://github.com/TakeScoop/SwiftyRSA/issues/183


I would also like to reiterate that this is a very often used code on our side for the last several years and it has only started failing with the last (or possibly second to last version of Xcode) so it's a regression either introduced in 11.3 or 11.4. It's also tricky as it doesn't occur every single time a chunk of data is encrypted so it's a bit tough to reproduce, otherwise I would provide more specific examples.

I’ve been working with Matt on Oscar Fung’s original question and we hope to post a response to that soon. However, I wanted to address this point:

so it's a regression either introduced in 11.3 or 11.4.

You are free to file a bug about this regression, of course, but my experience is that

SecKeyDecrypt
itself is pretty stable and that most weird bugs like this are caused by folks calling it incorrectly, typically because they misunderstand how Swift pointers work. For example, I just downloaded the TOT SwiftyRSA and opened it in Xcode 11.4.1 and it immediately issued a warning on line 56 of
EncryptedMessage.swift
:
/Users/quinn/Desktop/SwiftyRSA-master/Source/EncryptedMessage.swift:56:41: warning: initialization of 'UnsafePointer<UInt8>' results in a dangling pointer
        let decryptedData = Data(bytes: UnsafePointer<UInt8>(decryptedDataBytes), count: decryptedDataBytes.count)
                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Not a promising start.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Oscar,


Taking a look at what is happening in SecKeyDecrypt; you mentioned that:

| However in Xcode 11.4, it starts to return errSecParam randomly.


On one of the successful decryption attempts are you able to check the first few bytes for the presence of PKCS#1 padding? For example, when encrypting a plaintext value with PKCS#1 padding and performing a raw decrypt you will see something that looks like this:

00000000: 023f fa40 53ec cdf8 7516 4fa8 a465 bf39  .?.@S...u.O..e.9


The thing to pay attention to is 02 at the start of the encoded message that indicates PKCS#1 padding. From Section 7.2.1 of RFC 8017 you can see the encoded message is formatted with (minus the 00 that sometimes get omitted):

EM = 0x00 || 0x02 || PS || 0x00 || M.


The reason I bring this up is that when performing a raw decrypt on the cipher text you provided there is an output that starts with 8482:

00000000: 8482 9389 ....


Is the plaintext value being encrypted with PKCS#1 padding?


Matt Eaton

DTS Engineering, CoreOS

meaton3 at apple.com

Hi Matt,


I am not familiar with detail implementation of RSA encryption, but I can show you the data that can be decrypted successfully with the same private key I posted before.


The encrypted data in base64 encoded format: YZN/adu7+OSJLJ0p2pgFOVZRI+0W2YY0sxVKJtmz/YP/4cvev35VBU3Gh26/6+9EqmR968OWZs4NqFIj+bLFlnPkK+sZ0k++9xvhebNM+ZaG/m+4fcEdqDRIQqxZK/0XHniZ4ejb0Y57o+kX5BhVid2kPf7kPJJ0KUsRXZXxN2wuREDFZHRw0pWUS9CXt9ePkCeIONRAIoukgR044pQdnqtF+lDz2YHoPm5To2Fmj5U9rcAV9Odg8blIZFeJYNgsDjLEmvlXa+wes1fsSr7+UXTjsNQYb279a3waS03vZRgAUs8znb/6QTwm4tScCnqnjdYv++kKAXSYO4lWOfI+/w==


The decrypted payload in hex dump:

[51, 50, 67, 67, 49, 53, 51, 50, 50, 57, 52, 53, 57, 52, 55, 55, 55, 56, 69, 68, 53, 70, 54, 50, 66, 48, 48, 53, 69, 52, 70, 69, 66, 53, 54, 65, 67, 67, 49, 51, 66, 57, 56, 48, 51, 54, 66, 57, 57, 51, 55, 55, 48, 52, 48, 70, 67, 48, 70, 66, 52, 55, 69, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]


Since I cannot see the server side code, I cannot guarantee it encrypts the data with PKCS1 padding although the engineers of server side claims they are using PKCS1 padding to encrypt the data. I have also checked with Android side code and it decrypts the data with PKCS1 padding and it always decrypts successfully.


Finally, after I read the post from jannemecek, I tried to use Xcode 11.3.1 to build the app and this issue no longer exists.


Regards,

Oscar

Oscar,


Thanks for the follow up. Something interesting I noticed about the decrypted bytes you posted is that when I try and encrypt them using PKCS#1 padding with your public key (derived from your private key : SecKeyCopyPublicKey), I receive a -50 from SecKeyEncrypt. Now, performing a raw encrypt / decrypt round trip without any padding results back in your decrypted payload.


51506767 49535150 50575253 57525555 55566968 53705450 66484853 
69527069 66535465 67674951 66575648 51546657 57515555 48524870 
67487066 52556968 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000 00000000 00000000 00000000 00000000 00000000 00000000 
00000000


This suggests that the original value could have been encrypted without padding as well. Something to consider.


Matt Eaton

DTS Engineering, CoreOS

meaton3 at apple.com

Hi Matt,


Thanks for the update! I will check with server side to confirm the padding first.


Regards,

Oscar

Hi all,


I would like to summarize the issue. My code is based on SwiftRSA library which was mentioned in the previous post. As eskimo mentioned, there is a warning about UnsafePointer. This warning causes my app encrypted the data using server public key incorrectly sometimes. Therefore, server decrypted the wrong data causing it returned the incorrect data to my app. After I modified my code to remove the warnings (https://github.com/TakeScoop/SwiftyRSA/pull/184/files), the issue solved.


Thanks all for the help! 🙏-1F3FB;


Regards,

Oscar

SecKeyDecrypt returns errSecParam
 
 
Q