Certificate-private key combo failure

App creates an RSA key pair suitable for sign/verify & encrypt/decrypt and stores them in a private keychain. A CSR is generated via openssl, supplying the private key from above. The CSR is sent to a server for registration. The server returns a certificate, which is added to the keychain. Keychain associates the cert & private key as an identity. The app is built for 10.12+ and uses: SecKeyCreateSignature, SecKeyVerifySignature, SecKeyCreateEncryptedData, SecKeyCreateDecryptedData


Generating the key pair and returning the private key

SecKeyGeneratePair, private key is returned in PEM fomat

CFDataRef dataRef;

SecKeyCopyExternalRepresentation( privateKey, &error );

SecItemImportExportKeyParameters params = {};

SecItemExport( privateKey, kSecFormatOpenSSL, kSecItemPemArmour, &params, &dataRef );

NSString* pemString = [NSString stringWithUTF8String:[(__bridge NSData*)dataRef bytes]];

for use with openssl req -new -key <private key> -config <config file>


(In test code) I see different behaviors depending on which combination of keys is used.

Here are three scenarios...

1. 'original' key pair

public key = query keychain for kSecAttrKeyClassPrivate by kSecAttrLabel

private key = query keychain for kSecAttrKeyClassPublic by kSecAttrLabel


testSignAndVerify SUCCEEDED

testEncryptAndDecrypt SUCCEEDED


But of course, this scenario cannot be used since the server will need to use the public key associated with the certificate to encrypt data and verify signatures.



2. identity provides both keys

Fetch the identity by kSecAttrLabel

public key = SecIdentityCopyCertificate; SecCertificateCopyPublicKey

private key = SecIdentityCopyPrivateKey


testSignAndVerify FAILED - CSSMERR_CSP_OPERATION_AUTH_DENIED

testEncryptAndDecrypt FAILED - CSSMERR_CSP_OPERATION_AUTH_DENIED



3. hybrid

Fetch the identity by kSecAttrLabel

public key = SecIdentityCopyCertificate; SecCertificateCopyPublicKey

private key = query keychain for kSecAttrKeyClassPublic by kSecAttrLabel


testSignAndVerify FAILED - CSSMERR_CSP_VERIFY_FAILED

testEncryptAndDecrypt FAILED - CSSMERR_CSP_INVALID_DATA


So, I don't know what is going on, but it seems that whenever the certificate is involved, things don't work. Looks like there is some sort of mismatch between the keys, but I checked the modulus and exponent - they match.

Replies

App creates an RSA key pair suitable for sign/verify & encrypt/decrypt and stores them in a private keychain. A CSR is generated via openssl, supplying the private key from above.

Huh? CSRs are meant to include the public key.

Share and Enjoy

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

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

eskimo said, "Huh? CSRs are meant to include the public key."


Then I'm confused...

Creating CSR using openssl: /usr/bin/openssl req -new -key "/path/to/private/key" -config "path/to/config/file"


openssl says:

-key filename

This specifies the file to read the private key from. It also

accepts PKCS#8 format private keys for PEM format files.

I pulled code into a test application with some unit tests. The binary is signed. The tests create a keychain file on the desktop, password protected. The tests:

create Certificate Signing Request - succeeds

import identity - succeeds, identity is the one returned from a successful registration using the above CSR

sign - fails: CSSMERR_CSP_OPERATION_AUTH_DENIED

verify - not tested because it depends on signature above

encrypt - succeeds, returns cipherText

decrypt - falis: CSSMERR_CSP_OPERATION_AUTH_DENIED


I am at my wits end (easy for me to reach, apparently). The only googled-info I can find claims the CSSMERR_CSP_OPERATION_AUTH_DENIED error is due to an improperly signed binary. Mine is signed, verified by codesign. The keychain is unlocked (probably not necessary) when the private key is used. What am I missing?


Thanks for any insights.


PS FWIW foover == pch-ivanti Two dev account memeberships, two logins.

I’m not sure what’s going on with

openssl
(it’s possible that it’s deriving the public key from the private key, which is a relatively simple operation, at least for RSA) but CSRs definitely contain the public key. Here’s a quote from the found of all knowledge:

In public key infrastructure (PKI) systems, a certificate signing request (also CSR or certification request) is a message sent from an applicant to a certificate authority in order to apply for a digital identity certificate. It usually contains the public key for which the certificate should be issued, identifying information (such as a domain name) and integrity protection (e.g., a digital signature).

If you think about it this is the only option that makes sense. Here’s a typical CSR sequence:

  1. You generate a key pair.

  2. You keep the private key secret.

  3. You put the public key in a CSR.

  4. You send the CSR to the CA.

  5. The CA issues you a certificate, embedding the public key within that certificate.

  6. You import the certificate, which matches up with your private key to form a digital identity.

If you put your private key in the CSR in step 3 then the CA would get a copy of your private key in step 4. That undermines the whole point of this process, namely, that the CA never gets your private key [1].

Share and Enjoy

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

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

[1] If you were happy for the CA to have a copy of your private key, you can replace this process with something much simpler:

  1. CA generates a key pair.

  2. CA embeds the public key in a certificate.

  3. CA wraps private key and certificate into a PKCS#12.

  4. CA sends you the PKCS#12.

Yeah, I assumed that openssl used the private key to generate the public. I don't explicity send the private key to the server, only what openssl stuffs between -----BEGIN/END CERTIFICATE REQUEST-----. The server returns a (self-signed) certificate which does have a valid public key. Whether that key matches my private key, I don't know. I did check the modulus and exponent of the keys and they match. Is there more to check? Also, the root of trust chain is installed and trusted on the target machine. When the returned certificate is stashed in the keychain it is also trusted. The keychain associates the certificate with the private key used to create it. I can export that identity and import it into another keychain - but still cannot use the private key.


I have some swift code, that I got from somewhere, which will create a CSR. I will try that in place of openssl.


Are there any circumstances, other than signing, which would cause the CSSMERR_CSP_OPERATION_AUTH_DENIED to be emitted?


Thanks.

From above:

(1) I create an RSA key pair (SecKeyGeneratePair).

(2) The private key is in keychain.

(3-5) Using the Keychain Access app's Certificate Assistant, I create a certificate from the private key of the key pair - that's the way tool seems to work, I could not see a way to generate a certificate without selecting the private key.

(6) So, in my keychain I have the private key and the certificate which contains a public key - an identity.

I fetch the identity from the keychain (create a query, call SecItemCopyMatching, verify the typeID).

I use that idendityRef to get the public key (SecIdentityCopyCertificate, SecCertificateCopyPublicKey)

I use that idendityRef to get the private key (SecIdentityCopyPrivateKey).

No openssl involved. keys and certificate created using Keychain or api calls.

sign - fails: CSSMERR_CSP_OPERATION_AUTH_DENIED

verify - not tested because it depends on signature above

encrypt - succeeds, returns cipherText

decrypt - falis: CSSMERR_CSP_OPERATION_AUTH_DENIED

Any suggestions of how to debug this?

The fact that you’re getting CSSM errors is a strong indication that you’re doing this on the Mac. Is that right?

If so, please check that your app (or tool, or whatever code you’re using to test this) is code signed. The macOS keychain uses the code signature as part of its ACL mechanism; code without a code signature used to work (back before code signing was introduced) but these days it’s very much an edge case.

Share and Enjoy

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

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

Yes, it is a macOS (10.12+) command line tool that runs in the system context.

The binary is signed, signature verified by codesign. The signing cert is a valid Apple-generated cert.

The keychain file is created by the tool and not stored in /Library/Keychains - don't know if that makes any difference.

A generated (SecKeyGeneratePair) RSA key pair works - data can be signed and verified.

The issue seems to be related to using an identity...

When the private key is obtained from an identity, signing fails. The indentity is created when a certificate is imported into the keychain and the keychain matches it with the existing private key.

Any suggestions for how to debug what is going on?


Thanks.

So, do you know anything about kSecUseKeychain in a query not working? I doesn't seem to. If I create a SecKeychainCopySearchList with my keychain, that works. Of course, I set it back after I locate the identity.

So, do you know anything about

kSecUseKeychain
in a query not working?

Check out this thread.

Share and Enjoy

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

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