简体   繁体   中英

Private Certificate Authority in PHP?

I am setting up a private CA, and I want to interface with it using PHP. I have tried with PHP's built-in openssl library . So I create a CSR, and to sign it I use openssl_csr_sign .

This does sign the CSR, but that's it. In OpenSSL's CLI it would be something like

openssl x509 -req -days 360 -in file.csr -CA ca.crt -CAkey ca.key ...

Whereas I want something like

openssl ca -cert ca.crt -keyfile ca.key -in file.csr -out file.crt ...

Basically it uses the x509 module to sign it, instead of the ca module. So it doesn't write it into the database specified in openssl.cnf , it doesn't use or update the serial number; it's more "I trust this guy so I'll sign his public key with my private key" than an actual CA. Is there a way to manage a private CA in PHP, with openssl or not?

Yes and no.

Despite using a provided openssl.conf file, PHP's OpenSSL extension doesn't automatically manage the certificate database and/or serial numbers, and it doesn't provide any utilities to help with that.

On the other hand, the database itself has a relatively simple format , so you can implement it yourself using the primitive file-system functions. Here are some hints if you actually go with that route:

  • Since each certificate record sits on a separate line, fgets() comes handy while parsing it.
    • fscanf() looks better at first glance, but it treats all whitespace the same and tabs are an essential part of the format, so ...
    • file() is even easier, but only for reading. Chances are that you'll need read+write at the same time, and you'll need to obtain a lock on the file to avoid race conditions.
  • The DN string elements can be in arbitrary order, not all are mandatory and the delimiter isn't escaped when present inside a value, so you'll find it hard to produce it in the same way that the OpenSSL CLI tool does. You're better off doing a openssl_x509_parse() on the just signed certificate and reading the value from there.
    • I don't remember what it was, but that function had some minor difference in the result between PHP 5 and 7 and it was kind of important for the DN string.
  • PHP (properly) handles the serial number as an integer, but it is stored in hexadecimal notation, so you'll need to convert it back and forth.
    • The serial file stores the next serial number, so you can do $serial = hexdec(file_get_contents($pathToSerial)) , pass that variable to openssl_csr_sign() and then write sprintf("%X\\n", $serial + 1) to the file.
  • Revocation timestamps sit in the 3rd column of the database, but since you don't revoke a certificate right while signing it, it won't be present - that's why there's 2 tabs between the expiration date and serial number, don't forget that while writing.
  • Contrary to what one would expect, The OpenSSL CLI tool doesn't actually operate on the same database file. It reads the current one, renames it to <filename>.old and then creates an entirely new one as <filename> . What this means is that any file-system ownership, permissions that give your PHP script access to it are lost whenever you use the CLI tool.
    • Put runtime checks for access to the file; on failure - abort generation/signing and log/print a message (possibly with chown , chmod instructions) to notify you of that.
    • Same thing applies to the serial and all other files.
  • While I've never seen it happen, it's not impossible that any of key generation, CSR creation and signing fails.
    • The resulting pKey, CSR, certificate are all interdependent and of type resource (which should be closed after use). To throw an exception and close the resources only when necessary, I like to pre-define the variables holding them and use them in a closure that handles all conditional resource-free routines before throwing an exception.
    • Only write to the database after all 3 have succeeded, and write to the serial file last.
  • You've probably already figured that one out, but they pKey resource holds both the private and public keys.

As you can see, it is manageable if you know what you're doing, but it has a lot of gotchas and not really worth it for a simple PoC. Calling the CLI tool via exec() (and siblings) is a simpler choice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM