Questions about certificates and private key

I am trying to sign app with Developer ID Application certificate. So I make .certSigningRequest file and send to Account Holder to generate certificate on portal. 

And he do it using Windows. Now when I download certificate and install it I miss the private key.

Isn't that private key already on my Mac because when I created CSR the private and public keys appear in my keychain?

Does creating CSR with option "Let me specify key pair information" be the solution to have private key on my Mac even if he create certificates again on Windows?

Is there any other option to retrieve this private key and how is that private key actually created on the device(lets say Mac) that is used to generate certificates on portal?

Answered by DTS Engineer in 677484022

IMPORTANT Normally in situations like this my general advice is “revoke everything and try again”. However, in the case of a Developer ID signing identity it’s important that you not do that. Developer ID identities are precious, as I explain in this post.

Isn't that private key already on my Mac because when I created CSR the private and public keys appear in my keychain?

That’s the way things are meant to work. Here’s the expected workflow:

  1. You create a certificate signing request (CSR) using Certificate Assistant.

  2. It generates a key pair and stores both the private and public key in your keychain.

  3. It wraps the public key in the CSR.

  4. You send the CSR to a CA (in this case you send it to your colleague who uploads it to the developer web site which acts as the CA).

  5. The CA takes the information from the CSR (in this case that’s basically just the public key), embeds that in a certificate, and signs that.

  6. You add the certificate to your keychain and it pairs with the private key to form a digit identity.

but Keychain won't pair it with certificates for some reason

This pairing is done via the hash of the public key. For more background on this, see SecItem attributes for keys.

You can use the security dump-keychain command to investigate this. Consider this:

% security dump-keychain
…
keychain: "/Users/quinn/Library/Keychains/MouseCA.keychain-db"
version: 512
class: 0x00000010 
attributes:
    0x00000000 <uint32>=0x00000010 
    0x00000001 <blob>=0x536C61727469626172746661737400  "Slartibartfast\000"
    …
    0x00000006 <blob>=0x981B27B6AAF1D5B5054CB67178B99B76F43D5D53  …
    …
…
keychain: "/Users/quinn/Library/Keychains/MouseCA.keychain-db"
version: 512
class: 0x0000000F 
attributes:
    0x00000000 <uint32>=0x0000000F 
    0x00000001 <blob>="Slartibartfast"
    …
    0x00000006 <blob>=0x981B27B6AAF1D5B5054CB67178B99B76F43D5D53  …
    …
…
keychain: "/Users/quinn/Library/Keychains/MouseCA.keychain-db"
version: 512
class: 0x80001000 
attributes:
    …
    "hpky"<blob>=0x981B27B6AAF1D5B5054CB67178B99B76F43D5D53  …
    …

The order of these items is not defined but in this case:

  • The first item is private key (class is 0x00000010, or kSecPrivateKeyItemClass).

  • The second item is the public key (class is 0x0000000F, or kSecPublicKeyItemClass).

  • The third item is the certificate, (class is 0x80001000, or kSecCertificateItemClass).

Note how the hpky attribute (kSecPublicKeyHashItemAttr, aka kSecAttrPublicKeyHash) in the certificate matches the 0x00000006 attribute (kSecKeyLabel, aka kSecAttrApplicationLabel) in the other two.


You can also match this up with the CSR. Consider this CSR:

% cat Slartibartfast.certSigningRequest
-----BEGIN CERTIFICATE REQUEST-----
MIICmjCCAYICAQAwVTEvMC0GCSqGSIb3DQEJARYgcXVpbm4tZmFtaWx5QGFuYXJj
aGlzdHR1cnRsZS5jb20xFTATBgNVBAMMDEZhbWlseSBRdWlubjELMAkGA1UEBhMC
R0IwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuKH1jylIC7vW3TROm
XirEY2go0+jrSrV0ZY/cu5pOV9HJW71ZwpcrwyVV5TXthvj6JArIfKUtEUFmJNXg
X1CW8O57Bf9LxLnVFvgAgaXa4d0m79spBs0B3yAngN26lMkPemhibTpAEnNzfe5f
k90CTZSUNOGkZzsR7l+yH52//WFPAx8fMHS67S3UisIOug1feAh6qCUKy9AihWBf
AMOgXd+4Lx3LsZpNiYTcmKICGbHs8U8utAKhGGNyCzcmKId8faYOWGDNONIRrn5N
Gz5HnqPtYkrzQDCygiudKG4H4kJj+QWn4saSmTtOl3TofZs7PHOsIHEi0U4rMa8R
1am5AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAGFAJ1mpjHG57WAa2slQzxxcz
j9Fqj2HxnuMbMnANFOUh6lKz8/kZDq4oHtgjcRQOZfS/uXQM3wMuw6e2f7xa2J+P
Kx3+FWUnzQGSQ5T+YWP1N3iVMV+e9xB5JLQmrn/AXX9Bqy3Sh+Q7TnK9CZ2COzkJ
FW3mprSQ91+2XMvOt6hJTpEHagpO/wS4UXBIrZy25ktU1XFUhRvO8gddY479EIGP
Gxg+h5o0htzF1hVSnAX+hsA82Xp1aQEl5uyOqaNzEi1oXBjKP35/aJ8piDWp7C10
zApZKyqX0iqoFOQsO/0n4/qAyMeUjVY7yN5xTRNUzSqW/bgDuh/tg74qm1LBdQ==
-----END CERTIFICATE REQUEST-----

I then decoded the Base64:

% base64 -D > Slartibartfast.asn1   
MIICmjCCAYICAQAwVTEvMC0GCSqGSIb3DQEJARYgcXVpbm4tZmFtaWx5QGFuYXJj
aGlzdHR1cnRsZS5jb20xFTATBgNVBAMMDEZhbWlseSBRdWlubjELMAkGA1UEBhMC
R0IwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuKH1jylIC7vW3TROm
XirEY2go0+jrSrV0ZY/cu5pOV9HJW71ZwpcrwyVV5TXthvj6JArIfKUtEUFmJNXg
X1CW8O57Bf9LxLnVFvgAgaXa4d0m79spBs0B3yAngN26lMkPemhibTpAEnNzfe5f
k90CTZSUNOGkZzsR7l+yH52//WFPAx8fMHS67S3UisIOug1feAh6qCUKy9AihWBf
AMOgXd+4Lx3LsZpNiYTcmKICGbHs8U8utAKhGGNyCzcmKId8faYOWGDNONIRrn5N
Gz5HnqPtYkrzQDCygiudKG4H4kJj+QWn4saSmTtOl3TofZs7PHOsIHEi0U4rMa8R
1am5AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAGFAJ1mpjHG57WAa2slQzxxcz
j9Fqj2HxnuMbMnANFOUh6lKz8/kZDq4oHtgjcRQOZfS/uXQM3wMuw6e2f7xa2J+P
Kx3+FWUnzQGSQ5T+YWP1N3iVMV+e9xB5JLQmrn/AXX9Bqy3Sh+Q7TnK9CZ2COzkJ
FW3mprSQ91+2XMvOt6hJTpEHagpO/wS4UXBIrZy25ktU1XFUhRvO8gddY479EIGP
Gxg+h5o0htzF1hVSnAX+hsA82Xp1aQEl5uyOqaNzEi1oXBjKP35/aJ8piDWp7C10
zApZKyqX0iqoFOQsO/0n4/qAyMeUjVY7yN5xTRNUzSqW/bgDuh/tg74qm1LBdQ==
^D

And dumped the ASN.1:

% dumpasn1 -p -a -e Slartibartfast.asn1
SEQUENCE {
  SEQUENCE {
    INTEGER 0
    SEQUENCE {
      SET {
        SEQUENCE {
          OBJECT IDENTIFIER emailAddress (1 2 840 113549 1 9 1)
          IA5String 'redacted@redacted.com'
          }
        }
      SET {
        SEQUENCE {
          OBJECT IDENTIFIER commonName (2 5 4 3)
          UTF8String 'redacted'
          }
        }
      SET {
        SEQUENCE {
          OBJECT IDENTIFIER countryName (2 5 4 6)
          PrintableString 'GB'
          }
        }
      }
    SEQUENCE {
      SEQUENCE {
        OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
        NULL
        }
      BIT STRING
        30 82 01 0A 02 82 01 01 00 AE 28 7D 63 CA 52 02
        EE F5 B7 4D 13 A6 5E 2A C4 63 68 28 D3 E8 EB 4A
        B5 74 65 8F DC BB 9A 4E 57 D1 C9 5B BD 59 C2 97
        2B C3 25 55 E5 35 ED 86 F8 FA 24 0A C8 7C A5 2D
        11 41 66 24 D5 E0 5F 50 96 F0 EE 7B 05 FF 4B C4
        B9 D5 16 F8 00 81 A5 DA E1 DD 26 EF DB 29 06 CD
        01 DF 20 27 80 DD BA 94 C9 0F 7A 68 62 6D 3A 40
        12 73 73 7D EE 5F 93 DD 02 4D 94 94 34 E1 A4 67
        3B 11 EE 5F B2 1F 9D BF FD 61 4F 03 1F 1F 30 74
        BA ED 2D D4 8A C2 0E BA 0D 5F 78 08 7A A8 25 0A
        CB D0 22 85 60 5F 00 C3 A0 5D DF B8 2F 1D CB B1
        9A 4D 89 84 DC 98 A2 02 19 B1 EC F1 4F 2E B4 02
        A1 18 63 72 0B 37 26 28 87 7C 7D A6 0E 58 60 CD
        38 D2 11 AE 7E 4D 1B 3E 47 9E A3 ED 62 4A F3 40
        30 B2 82 2B 9D 28 6E 07 E2 42 63 F9 05 A7 E2 C6
        92 99 3B 4E 97 74 E8 7D 9B 3B 3C 73 AC 20 71 22
        D1 4E 2B 31 AF 11 D5 A9 B9 02 03 01 00 01
      }
    [0]
      Error: Object has zero length.
    }
  SEQUENCE {
    OBJECT IDENTIFIER sha256WithRSAEncryption (1 2 840 113549 1 1 11)
    NULL
    }
  BIT STRING
    …
  }

Note I’m using the dumpasn1 tool here, from www.cs.auckland.ac.nz/~pgut001/dumpasn1.c.

This is an ASN.1 CertificationRequest structure per RFC 2986. The BIT STRING starting on line 30 is the public key. I extracted that into a file (go go gadget Hex Fiend!) and calculated the SHA-1 of that:

% shasum Slartibartfast-public.asn1 
981b27b6aaf1d5b5054cb67178b99b76f43d5d53  Slartibartfast-public.asn1

Note how this matches the publish key hash from earlier.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I suppose I should already have the private key but Keychain won't pair it with certificates for some reason

Accepted Answer

IMPORTANT Normally in situations like this my general advice is “revoke everything and try again”. However, in the case of a Developer ID signing identity it’s important that you not do that. Developer ID identities are precious, as I explain in this post.

Isn't that private key already on my Mac because when I created CSR the private and public keys appear in my keychain?

That’s the way things are meant to work. Here’s the expected workflow:

  1. You create a certificate signing request (CSR) using Certificate Assistant.

  2. It generates a key pair and stores both the private and public key in your keychain.

  3. It wraps the public key in the CSR.

  4. You send the CSR to a CA (in this case you send it to your colleague who uploads it to the developer web site which acts as the CA).

  5. The CA takes the information from the CSR (in this case that’s basically just the public key), embeds that in a certificate, and signs that.

  6. You add the certificate to your keychain and it pairs with the private key to form a digit identity.

but Keychain won't pair it with certificates for some reason

This pairing is done via the hash of the public key. For more background on this, see SecItem attributes for keys.

You can use the security dump-keychain command to investigate this. Consider this:

% security dump-keychain
…
keychain: "/Users/quinn/Library/Keychains/MouseCA.keychain-db"
version: 512
class: 0x00000010 
attributes:
    0x00000000 <uint32>=0x00000010 
    0x00000001 <blob>=0x536C61727469626172746661737400  "Slartibartfast\000"
    …
    0x00000006 <blob>=0x981B27B6AAF1D5B5054CB67178B99B76F43D5D53  …
    …
…
keychain: "/Users/quinn/Library/Keychains/MouseCA.keychain-db"
version: 512
class: 0x0000000F 
attributes:
    0x00000000 <uint32>=0x0000000F 
    0x00000001 <blob>="Slartibartfast"
    …
    0x00000006 <blob>=0x981B27B6AAF1D5B5054CB67178B99B76F43D5D53  …
    …
…
keychain: "/Users/quinn/Library/Keychains/MouseCA.keychain-db"
version: 512
class: 0x80001000 
attributes:
    …
    "hpky"<blob>=0x981B27B6AAF1D5B5054CB67178B99B76F43D5D53  …
    …

The order of these items is not defined but in this case:

  • The first item is private key (class is 0x00000010, or kSecPrivateKeyItemClass).

  • The second item is the public key (class is 0x0000000F, or kSecPublicKeyItemClass).

  • The third item is the certificate, (class is 0x80001000, or kSecCertificateItemClass).

Note how the hpky attribute (kSecPublicKeyHashItemAttr, aka kSecAttrPublicKeyHash) in the certificate matches the 0x00000006 attribute (kSecKeyLabel, aka kSecAttrApplicationLabel) in the other two.


You can also match this up with the CSR. Consider this CSR:

% cat Slartibartfast.certSigningRequest
-----BEGIN CERTIFICATE REQUEST-----
MIICmjCCAYICAQAwVTEvMC0GCSqGSIb3DQEJARYgcXVpbm4tZmFtaWx5QGFuYXJj
aGlzdHR1cnRsZS5jb20xFTATBgNVBAMMDEZhbWlseSBRdWlubjELMAkGA1UEBhMC
R0IwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuKH1jylIC7vW3TROm
XirEY2go0+jrSrV0ZY/cu5pOV9HJW71ZwpcrwyVV5TXthvj6JArIfKUtEUFmJNXg
X1CW8O57Bf9LxLnVFvgAgaXa4d0m79spBs0B3yAngN26lMkPemhibTpAEnNzfe5f
k90CTZSUNOGkZzsR7l+yH52//WFPAx8fMHS67S3UisIOug1feAh6qCUKy9AihWBf
AMOgXd+4Lx3LsZpNiYTcmKICGbHs8U8utAKhGGNyCzcmKId8faYOWGDNONIRrn5N
Gz5HnqPtYkrzQDCygiudKG4H4kJj+QWn4saSmTtOl3TofZs7PHOsIHEi0U4rMa8R
1am5AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAGFAJ1mpjHG57WAa2slQzxxcz
j9Fqj2HxnuMbMnANFOUh6lKz8/kZDq4oHtgjcRQOZfS/uXQM3wMuw6e2f7xa2J+P
Kx3+FWUnzQGSQ5T+YWP1N3iVMV+e9xB5JLQmrn/AXX9Bqy3Sh+Q7TnK9CZ2COzkJ
FW3mprSQ91+2XMvOt6hJTpEHagpO/wS4UXBIrZy25ktU1XFUhRvO8gddY479EIGP
Gxg+h5o0htzF1hVSnAX+hsA82Xp1aQEl5uyOqaNzEi1oXBjKP35/aJ8piDWp7C10
zApZKyqX0iqoFOQsO/0n4/qAyMeUjVY7yN5xTRNUzSqW/bgDuh/tg74qm1LBdQ==
-----END CERTIFICATE REQUEST-----

I then decoded the Base64:

% base64 -D > Slartibartfast.asn1   
MIICmjCCAYICAQAwVTEvMC0GCSqGSIb3DQEJARYgcXVpbm4tZmFtaWx5QGFuYXJj
aGlzdHR1cnRsZS5jb20xFTATBgNVBAMMDEZhbWlseSBRdWlubjELMAkGA1UEBhMC
R0IwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuKH1jylIC7vW3TROm
XirEY2go0+jrSrV0ZY/cu5pOV9HJW71ZwpcrwyVV5TXthvj6JArIfKUtEUFmJNXg
X1CW8O57Bf9LxLnVFvgAgaXa4d0m79spBs0B3yAngN26lMkPemhibTpAEnNzfe5f
k90CTZSUNOGkZzsR7l+yH52//WFPAx8fMHS67S3UisIOug1feAh6qCUKy9AihWBf
AMOgXd+4Lx3LsZpNiYTcmKICGbHs8U8utAKhGGNyCzcmKId8faYOWGDNONIRrn5N
Gz5HnqPtYkrzQDCygiudKG4H4kJj+QWn4saSmTtOl3TofZs7PHOsIHEi0U4rMa8R
1am5AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAGFAJ1mpjHG57WAa2slQzxxcz
j9Fqj2HxnuMbMnANFOUh6lKz8/kZDq4oHtgjcRQOZfS/uXQM3wMuw6e2f7xa2J+P
Kx3+FWUnzQGSQ5T+YWP1N3iVMV+e9xB5JLQmrn/AXX9Bqy3Sh+Q7TnK9CZ2COzkJ
FW3mprSQ91+2XMvOt6hJTpEHagpO/wS4UXBIrZy25ktU1XFUhRvO8gddY479EIGP
Gxg+h5o0htzF1hVSnAX+hsA82Xp1aQEl5uyOqaNzEi1oXBjKP35/aJ8piDWp7C10
zApZKyqX0iqoFOQsO/0n4/qAyMeUjVY7yN5xTRNUzSqW/bgDuh/tg74qm1LBdQ==
^D

And dumped the ASN.1:

% dumpasn1 -p -a -e Slartibartfast.asn1
SEQUENCE {
  SEQUENCE {
    INTEGER 0
    SEQUENCE {
      SET {
        SEQUENCE {
          OBJECT IDENTIFIER emailAddress (1 2 840 113549 1 9 1)
          IA5String 'redacted@redacted.com'
          }
        }
      SET {
        SEQUENCE {
          OBJECT IDENTIFIER commonName (2 5 4 3)
          UTF8String 'redacted'
          }
        }
      SET {
        SEQUENCE {
          OBJECT IDENTIFIER countryName (2 5 4 6)
          PrintableString 'GB'
          }
        }
      }
    SEQUENCE {
      SEQUENCE {
        OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
        NULL
        }
      BIT STRING
        30 82 01 0A 02 82 01 01 00 AE 28 7D 63 CA 52 02
        EE F5 B7 4D 13 A6 5E 2A C4 63 68 28 D3 E8 EB 4A
        B5 74 65 8F DC BB 9A 4E 57 D1 C9 5B BD 59 C2 97
        2B C3 25 55 E5 35 ED 86 F8 FA 24 0A C8 7C A5 2D
        11 41 66 24 D5 E0 5F 50 96 F0 EE 7B 05 FF 4B C4
        B9 D5 16 F8 00 81 A5 DA E1 DD 26 EF DB 29 06 CD
        01 DF 20 27 80 DD BA 94 C9 0F 7A 68 62 6D 3A 40
        12 73 73 7D EE 5F 93 DD 02 4D 94 94 34 E1 A4 67
        3B 11 EE 5F B2 1F 9D BF FD 61 4F 03 1F 1F 30 74
        BA ED 2D D4 8A C2 0E BA 0D 5F 78 08 7A A8 25 0A
        CB D0 22 85 60 5F 00 C3 A0 5D DF B8 2F 1D CB B1
        9A 4D 89 84 DC 98 A2 02 19 B1 EC F1 4F 2E B4 02
        A1 18 63 72 0B 37 26 28 87 7C 7D A6 0E 58 60 CD
        38 D2 11 AE 7E 4D 1B 3E 47 9E A3 ED 62 4A F3 40
        30 B2 82 2B 9D 28 6E 07 E2 42 63 F9 05 A7 E2 C6
        92 99 3B 4E 97 74 E8 7D 9B 3B 3C 73 AC 20 71 22
        D1 4E 2B 31 AF 11 D5 A9 B9 02 03 01 00 01
      }
    [0]
      Error: Object has zero length.
    }
  SEQUENCE {
    OBJECT IDENTIFIER sha256WithRSAEncryption (1 2 840 113549 1 1 11)
    NULL
    }
  BIT STRING
    …
  }

Note I’m using the dumpasn1 tool here, from www.cs.auckland.ac.nz/~pgut001/dumpasn1.c.

This is an ASN.1 CertificationRequest structure per RFC 2986. The BIT STRING starting on line 30 is the public key. I extracted that into a file (go go gadget Hex Fiend!) and calculated the SHA-1 of that:

% shasum Slartibartfast-public.asn1 
981b27b6aaf1d5b5054cb67178b99b76f43d5d53  Slartibartfast-public.asn1

Note how this matches the publish key hash from earlier.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Questions about certificates and private key
 
 
Q