PHP 中的私有证书颁发机构?
Private Certificate Authority in PHP?
我正在设置私有 CA,我想使用 PHP 与其交互。我试过 PHP 的 built-in openssl library。所以我创建了一个 CSR,并使用 openssl_csr_sign
.
对其进行签名
这确实签署了 CSR,仅此而已。在 OpenSSL 的 CLI 中,它类似于
openssl x509 -req -days 360 -in file.csr -CA ca.crt -CAkey ca.key ...
而我想要的是
openssl ca -cert ca.crt -keyfile ca.key -in file.csr -out file.crt ...
基本上它使用 x509 模块对其进行签名,而不是 ca 模块。所以它不会将它写入openssl.cnf
中指定的数据库,它不会使用或更新序列号;它比实际的 CA 多 "I trust this guy so I'll sign his public key with my private key"。有没有办法在 PHP 中使用 openssl 管理私有 CA?
是也不是。
尽管使用提供的 openssl.conf 文件,PHP 的 OpenSSL 扩展不会自动管理证书数据库 and/or 序列号,并且它不提供任何实用程序来帮助解决这个问题。
另一方面,数据库本身有一个 relatively simple format,因此您可以使用原始文件系统函数自己实现它。如果您真的选择了这条路线,这里有一些提示:
- 由于每个证书记录都位于单独的一行中,因此
fgets()
在解析它时会派上用场。
fscanf()
乍一看看起来更好,但它对所有空格都一视同仁,制表符是格式的重要组成部分,所以 ...
file()
更简单,但仅供阅读。有可能您需要同时读取和写入,并且您需要获取文件的锁以避免竞争条件。
- DN 字符串元素可以按任意顺序排列,并非所有元素都是强制性的,并且分隔符出现在值中时不会转义,因此您会发现很难以与 OpenSSL CLI 相同的方式生成它工具确实如此。您最好在刚刚签署的证书上执行 openssl_x509_parse() 并从那里读取值。
- 我不记得它是什么了,但是那个函数在 PHP 5 和 7 之间的结果有一些细微差别,这对 DN 字符串来说很重要。
- PHP(正确地)将序列号作为整数处理,但它以十六进制表示法存储,因此您需要来回转换它。
- 序列文件存储下一个序列号,所以你可以做
$serial = hexdec(file_get_contents($pathToSerial))
,将该变量传递给openssl_csr_sign()
,然后写入sprintf("%X\n", $serial + 1)
到文件。
- 吊销时间戳位于数据库的第 3 列,但由于您在签署证书时没有立即吊销证书,因此它不会出现 - 这就是为什么在到期日期和序列号之间有 2 个选项卡,写作时不要忘记这一点。
- 与人们的预期相反,OpenSSL CLI 工具实际上并不对同一个数据库文件进行操作。它读取当前的一个,将其重命名为
<filename>.old
,然后创建一个全新的 <filename>
。这意味着只要您使用 CLI 工具,任何文件系统所有权和授予 PHP 脚本访问权限的权限都会丢失。
- 对文件的访问进行运行时检查;失败时 - 中止 generation/signing 和 log/print 一条消息(可能带有
chown
、chmod
说明)以通知您。
- 同样适用于序列号和所有其他文件。
- 虽然我从未见过这种情况,但密钥生成、CSR 创建和签名中的任何一个都可能失败。
- 生成的pKey、CSR、证书都是相互依赖的,类型为
resource
(使用后应关闭)。要抛出异常并仅在必要时关闭资源,我喜欢预先定义保存它们的变量,并 use
在抛出异常之前处理所有无条件资源例程的闭包中。
- 3个都成功后才写入数据库,最后写入串口文件
- 您可能已经想到了这一点,但他们的 pKey 资源同时拥有私钥和 public 密钥。
如您所见,如果您知道自己在做什么,它是可以管理的,但是它有很多陷阱,对于一个简单的 PoC 来说并不值得。通过 exec()
(和兄弟姐妹)调用 CLI 工具是一个更简单的选择。
我正在设置私有 CA,我想使用 PHP 与其交互。我试过 PHP 的 built-in openssl library。所以我创建了一个 CSR,并使用 openssl_csr_sign
.
这确实签署了 CSR,仅此而已。在 OpenSSL 的 CLI 中,它类似于
openssl x509 -req -days 360 -in file.csr -CA ca.crt -CAkey ca.key ...
而我想要的是
openssl ca -cert ca.crt -keyfile ca.key -in file.csr -out file.crt ...
基本上它使用 x509 模块对其进行签名,而不是 ca 模块。所以它不会将它写入openssl.cnf
中指定的数据库,它不会使用或更新序列号;它比实际的 CA 多 "I trust this guy so I'll sign his public key with my private key"。有没有办法在 PHP 中使用 openssl 管理私有 CA?
是也不是。
尽管使用提供的 openssl.conf 文件,PHP 的 OpenSSL 扩展不会自动管理证书数据库 and/or 序列号,并且它不提供任何实用程序来帮助解决这个问题。
另一方面,数据库本身有一个 relatively simple format,因此您可以使用原始文件系统函数自己实现它。如果您真的选择了这条路线,这里有一些提示:
- 由于每个证书记录都位于单独的一行中,因此
fgets()
在解析它时会派上用场。fscanf()
乍一看看起来更好,但它对所有空格都一视同仁,制表符是格式的重要组成部分,所以 ...file()
更简单,但仅供阅读。有可能您需要同时读取和写入,并且您需要获取文件的锁以避免竞争条件。
- DN 字符串元素可以按任意顺序排列,并非所有元素都是强制性的,并且分隔符出现在值中时不会转义,因此您会发现很难以与 OpenSSL CLI 相同的方式生成它工具确实如此。您最好在刚刚签署的证书上执行 openssl_x509_parse() 并从那里读取值。
- 我不记得它是什么了,但是那个函数在 PHP 5 和 7 之间的结果有一些细微差别,这对 DN 字符串来说很重要。
- PHP(正确地)将序列号作为整数处理,但它以十六进制表示法存储,因此您需要来回转换它。
- 序列文件存储下一个序列号,所以你可以做
$serial = hexdec(file_get_contents($pathToSerial))
,将该变量传递给openssl_csr_sign()
,然后写入sprintf("%X\n", $serial + 1)
到文件。
- 序列文件存储下一个序列号,所以你可以做
- 吊销时间戳位于数据库的第 3 列,但由于您在签署证书时没有立即吊销证书,因此它不会出现 - 这就是为什么在到期日期和序列号之间有 2 个选项卡,写作时不要忘记这一点。
- 与人们的预期相反,OpenSSL CLI 工具实际上并不对同一个数据库文件进行操作。它读取当前的一个,将其重命名为
<filename>.old
,然后创建一个全新的<filename>
。这意味着只要您使用 CLI 工具,任何文件系统所有权和授予 PHP 脚本访问权限的权限都会丢失。- 对文件的访问进行运行时检查;失败时 - 中止 generation/signing 和 log/print 一条消息(可能带有
chown
、chmod
说明)以通知您。 - 同样适用于序列号和所有其他文件。
- 对文件的访问进行运行时检查;失败时 - 中止 generation/signing 和 log/print 一条消息(可能带有
- 虽然我从未见过这种情况,但密钥生成、CSR 创建和签名中的任何一个都可能失败。
- 生成的pKey、CSR、证书都是相互依赖的,类型为
resource
(使用后应关闭)。要抛出异常并仅在必要时关闭资源,我喜欢预先定义保存它们的变量,并use
在抛出异常之前处理所有无条件资源例程的闭包中。 - 3个都成功后才写入数据库,最后写入串口文件
- 生成的pKey、CSR、证书都是相互依赖的,类型为
- 您可能已经想到了这一点,但他们的 pKey 资源同时拥有私钥和 public 密钥。
如您所见,如果您知道自己在做什么,它是可以管理的,但是它有很多陷阱,对于一个简单的 PoC 来说并不值得。通过 exec()
(和兄弟姐妹)调用 CLI 工具是一个更简单的选择。