wiki:CertChainVerification
Last modified 2 years ago Last modified on 01/25/12 15:46:56

Overview

  • Pulp's support for certificate verification against a CA Chain
  • A CA Chain in this context refers to a ROOT CA issuing a SUB CA.
    • The SUB CA is used to issue certificates.
    • Example: ROOT_CA -> SUB_CA -> CLIENT_CERT
  • Pulp needs to verify the entire chain is valid.
    • Verify that:
      • CLIENT_CERT was issued by SUB_CA and dates are all valid
      • SUB_CA was issued by ROOT_CA and dates are all valid
      • This chain could continue deeper if needed. Essential point is all certificates in the chain depth must be used for a full verification

Associated Bugzillas

Pulp Changes

  • Implementation as of 1/23/2012 we are only able to verify against a ROOT CA
    • src/pulp/repo_auth/repo_cert_utils.py
          def x509_verify_cert(self, cert, ca_cert, crl_stack=None):
              store = X509.X509_Store()
              store.add_cert(ca_cert) 
              if crl_stack and len(crl_stack) > 0:
                  store.set_flags(X509.m2.X509_V_FLAG_CRL_CHECK |
                             X509.m2.X509_V_FLAG_CRL_CHECK_ALL)
              store_ctx = X509.X509_Store_Context()
              store_ctx.init(store, cert)
              if crl_stack and len(crl_stack) > 0:
                  store_ctx.add_crls(crl_stack)
              return store_ctx.verify_cert()
      
  • Planned change is to leverage the X509_STORE_load_locations() call from Openssl's crypto/x509/x509_d2.c
    • This adds a X509_Lookup_file, it expects a string of the file path to the CA or the concatenated CA Chain.
    • This could also be patched to allow a hash_directory, but the current patch doesn't expose this.
  • New version:
        def x509_verify_cert(self, cert, ca_chain, crl_stack=None):
            store = X509.X509_Store()
            store.load_info(ca_chain) 
            if crl_stack and len(crl_stack) > 0:
                store.set_flags(X509.m2.X509_V_FLAG_CRL_CHECK |
                           X509.m2.X509_V_FLAG_CRL_CHECK_ALL)
            store_ctx = X509.X509_Store_Context()
            store_ctx.init(store, cert)
            if crl_stack and len(crl_stack) > 0:
                store_ctx.add_crls(crl_stack)
            return store_ctx.verify_cert()
    

M2Crypto Specifics

  • M2Crypto as of 0.21.1 does not support a X509_Store_Context::verify_cert() method. This method is essential to allowing the openssl C APIs to handle all verification.
  • Pulp developers have patched M2Crypto:
    • During our work on adding support for CRLs we exposed the verify_cert() method on a X509_Store_Context
    • Our patch to submit to upstream
      • Patch was submitted to this bugzilla: https://bugzilla.osafoundation.org/show_bug.cgi?id=12954
        def test_with_single_chain_file(self):
            test_cert = X509.load_cert(TEST_CERT)
            store = X509.X509_Store()
            self.assertEquals(store.load_info(CA_CHAIN), 1)
            store_ctx = X509.X509_Store_Context()
            store_ctx.init(store, test_cert)
            self.assertTrue(store_ctx.verify_cert())
        

Misc

Test Data

  • Look at our git repo playpen/certs/chain_example
    1. Run playpen/certs/chain_example/create_chain_data.sh
      • This will generate:
        $ tree certs/
        certs/
        |-- ca_chain
        |-- ROOT_CA
        |   |-- root_ca_key.pem
        |   |-- root_ca.pem
        |   `-- serial
        |-- SUB_CA
        |   |-- serial
        |   |-- sub_ca.csr
        |   |-- sub_ca_key.pem
        |   `-- sub_ca.pem
        |-- test_cert.pem
        |-- test.csr
        `-- test_key.pem
        
        2 directories, 11 files
        
      • Note: ca_chain is a concatenation of certs/ROOT_CA/root_ca.pem and certs/SUB_CA/sub_ca.pem
    2. Run verify_cert.sh
      • This uses the normal openssl verify -CAfile certs/ca_chain certs/test_cert.pem CLI tool to verify the generated test cert is issued by the sub_CA and the sub_CA is issued by the root_CA
    3. Now let's test the M2Crypto python calls. Run playpen/certs/chain_example/test_chain_verify.py
      • This tests 2 basic ways to verify the certificate was issued by the chain.
        • Use a separate file for root_CA and sub_CA
        • Use a single chain file, which is the concatenation of root_CA and sub_CA
      • Example output:
        $ ./test_chain_verify.py 
        ......
        ----------------------------------------------------------------------
        Ran 6 tests in 0.003s
        
        OK
        

Terms

  • 'CA Chain' - A file containing concatenated root CA and sub CA PEM encoded certificates
    • Example certs/ca_chain is an example of a 'CA Chain' file
      $ cat certs/ca_chain 
      -----BEGIN CERTIFICATE-----
      MIIDDzCCAfegAwIBAgIJAPZBwtauIY2DMA0GCSqGSIb3DQEBBQUAMB4xHDAaBgNV
      BAMME1Jvb3QgQ0EgQ29tbW9uIE5hbWUwHhcNMTIwMTIzMTUxNTAwWhcNMTUwMTIy
      MTUxNTAwWjAeMRwwGgYDVQQDDBNSb290IENBIENvbW1vbiBOYW1lMIIBIjANBgkq
      hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnnM6S4KgTEs0wayPNZI/fTcY+zobAej6
      /PMRDBSsGGbQTHTxDu+WarW8Ld8otrQ8mspy47ifsQqnyxoLXR+7tVoogeGfDMnH
      cSEuuMrXzjwEmAGE1D++iHr4DIx8Jt68bBNI6P44kF6hzmRq+q4k/z7KP0ng56T3
      lZWbyXSTrGifDZZI0HJq/0/XYOyQ1ceAQyUwQQaFYVgrP0oIinl+O9fKtcPpIcYT
      HqZtqlL7IkFNgFRYMc31QWwiM9cuZTg5UWiqXQUFzpLhImn0vZtskxwqfZm3f5sg
      3CNB3qquKeVM5rzHLiAPlYT62Nx8KwZvvLw0RZheDad0K/JqFe+GxQIDAQABo1Aw
      TjAdBgNVHQ4EFgQUThQBA5C5pMqVIh7FPotRHdOAhHowHwYDVR0jBBgwFoAUThQB
      A5C5pMqVIh7FPotRHdOAhHowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOC
      AQEAPeXsEfaFhpUL3PDfpNWNNtYapKV1fBqqnQkxGhozj8T6jLdXNSEZeTaiR8C/
      ijuwacm831O830Z/koj1ohvxe5i2UGqwioza6oWTKE7JE5VDB/6hIDYFgvfHDcKi
      OAQKDo8l8lgXsozm6lyl9ebY0wucMH0lIUWtMm0hnWb71wEjgks8XxS7z/ECZ3/q
      Ec1QjfFZH2BZf1b5Yx1q67V8719O2JMPu0hF8iep9asCz6LGnCInSqxpwfVZau9Z
      ZOozHNyxUI/GGjTEqkP4SIJZjiyiw5Dxt8WxM7wiB5F2QyZniIQY2tOLHE5IroJV
      /t6Bd2H2NMIHt1To4OsaCm8AYw==
      -----END CERTIFICATE-----
      -----BEGIN CERTIFICATE-----
      MIIDBjCCAe6gAwIBAgIBAjANBgkqhkiG9w0BAQUFADAeMRwwGgYDVQQDDBNSb290
      IENBIENvbW1vbiBOYW1lMB4XDTEyMDEyMzE1MTUwMVoXDTE1MDEyMjE1MTUwMVow
      HTEbMBkGA1UEAwwSU3ViIENBIENvbW1vbiBOYW1lMIIBIjANBgkqhkiG9w0BAQEF
      AAOCAQ8AMIIBCgKCAQEAxTlAiRcgc8fv+9jRV6gRKwSPOsgkqhLb76oz90GqeASt
      5cmE1iiBsqlIMTOz4A0yNpDDqMGYfpQNe0Op4zfwzSeUDxIyiapjykmTdR8K+MNA
      7RBM59YAk1eB7xWc7SWY7sfLu5soFek5wSagNiYlmy1LwXBgkDBOE6UYlHWl0m5j
      uJBIvxNhBPosKPncgmYNjW6gNEJDAnrFtVKE3z86UjaK4PQZPm381Go38aevuOM3
      6ZIYGH+dVrAlr0LE9AFYigSrYtyFGW2F5mliH5yT/QImBVO/Lawfqg/LLQ3BANAh
      kddyax3KPeNi0IAuk5r0MzQH1jrv0PFicjCDte/ZVQIDAQABo1AwTjAdBgNVHQ4E
      FgQUobdRW+YtKLVr8ppstKYkqRbS3sIwHwYDVR0jBBgwFoAUThQBA5C5pMqVIh7F
      PotRHdOAhHowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEANh2A3bAV
      n18vFq/wNOjxOVgSoFZkLl34oSVRp9Q0g/4Zrivm1ohds+DdI5A6h6oTzxxppXdC
      1lhuYrHhVSsK4BrO4eq83D0NmG8dQJJQq3bgg4NRtPal5Clrif+/0oEsyc0+QiGX
      8UlpxjVUQXhVjABWwlymAcxIWcS6lAlURjbg7t8eljFnRcwqhurwpin3IZ2AsKq/
      pfncNaKFCeFEQiu90buRBdQlLqlzqYlv7aXSotypiRSdGWVcZ4K/sPtjqF6i6bJi
      tWJ4WVyeJihbhkN+PPbyGityFCfRExgVjd0Iz3Avv2lNWI6NOv8+V4TFRYa6O57+
      VhYGPxM3LoQu4g==
      -----END CERTIFICATE-----