margusk / openssl-wrapper
PHP OpenSSL 扩展的面向对象包装器
Requires
- php: ^8.0
- ext-openssl: *
- margusk/accessors: ^0.1
- margusk/warbsorber: ^0.1
Requires (Dev)
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-09-10 13:55:12 UTC
README
PHP OpenSSL 包装器
PHP OpenSSL 扩展的面向对象包装器
此库解决的问题
PHP 的 OpenSSL 扩展的接口是过时的,只能以过程式的方式使用,这使得使用起来很笨拙,并且需要大量的代码来处理错误和结果。此库试图解决以下不足
- 错误不是通过异常处理:失败应始终通过返回
false
或-1
的过程式方式来报告,而不是使用异常。 - 意外的 PHP 警告:PHP OpenSSL 函数不应发出 PHP 警告。所有警告都应静默收集,并通过异常或返回值传递。这样,调用者可以程序化地决定如何处理它们(例如,将警告记录到特定位置)。
- 函数结果不在返回值中:OpenSSL 函数产生的(主要)结果也应作为返回值,而不是通过其中一个函数参数(例如
openssl_sign
)返回结果。最不自然的例子是openssl_seal
或openssl_csr_new
,它们都返回多个输入参数中的值。 - 密钥/证书/CSR 对象只是简单的内部 DTO:尽管在 PHP 8 中,OpenSSL 资源被替换为对象(
OpenSSLCertificate
、OpenSSLCertificateSigningRequest
和OpenSSLAsymmetricKey
),但它们在内部仍然只是简单的 DTO(数据传输对象)。没有公开方法和属性,并且只能通过将它们指定为openssl_*
函数的参数以过程式方式使用。此库将这些对象包装起来,以提供对特定对象的 OpenSSL 方法的直接访问。
与扩展的功能几乎完全保留,这意味着所有内部 openssl_*
函数(除已弃用或重复的以外)都被包装。区别在于方法签名:包装器的签名可以更短,并且它的参数从不按引用指定。
此库不做什么:它不会添加或更改任何 OpenSSL 加密功能。唯一目的是提供 OpenSSL 函数的方便的面向对象接口。
要求
- PHP >= 8.0
- OpenSSL 扩展
安装
使用 composer 安装
composer require margusk/openssl-wrapper
使用方法
静态方法与实例方法
包装器可以使用两种不同的方式使用
- 静态地:最简单的方法是使用静态
OpenSSL
类
use margusk\OpenSSL\Wrapper\OpenSSL; $result = OpenSSL::pkeyNew([ 'private_key_type' => OPENSSL_KEYTYPE_RSA ]);
- 使用 实例:为了能够自定义/拦截异常,请使用
Proxy
实例(有关自定义/拦截异常的说明见下文)
use margusk\OpenSSL\Wrapper\Proxy as OpenSSLProxy; $proxy = new OpenSSLProxy(); $result = $proxy->pkeyNew([ 'private_key_type' => OPENSSL_KEYTYPE_RSA ]);
将 OpenSSL 函数映射到包装器方法名称
大多数 OpenSSL 函数在包装器类中都有对应的方法(但请参见下面的例外)。
包装器名称的推导方法
- 从内部函数名称中删除
openssl_
前缀 - 将剩余部分从 snake-case 转换为 camel-case 格式
例如,openssl_get_cipher_methods
转换为 getCipherMethods
,可以使用以下方式调用
OpenSSL::getCipherMethods()
或(new OpenSSLProxy())->getCipherMethods()
以下函数没有被包装
openssl_free_key
- 在 PHP 8 中已弃用openssl_pkey_free
- 在 PHP 8 中已弃用openssl_x509_free
- 在 PHP 8 中已弃用openssl_get_privatekey
- 使用openssl_pkey_get_private
代替openssl_get_publickey
- 使用openssl_pkey_get_public
代替
返回值
对包装方法的每次调用都返回从 Result
类派生出的对象。
该对象封装了从开始到结束与调用相关的所有内容。具体来说
$result->value()
返回结果的实际值。数据类型取决于调用的方法,可以是int
、bool
、string
、array
或像AsymmetricKey
、CSR
或Certificate
这样的复杂类型。$result->inParameters()
返回内部函数调用所需的输入参数数组。请注意,可选参数始终提供$result->outParameters()
返回在内部函数可能被修改后的参数数组$result->warnings()->openSSL()
返回在调用过程中 OpenSSL 库报告的警告数组$result->warnings()->php()
返回在调用过程中 PHP (通过发出警告) 报告的警告/错误数组
一些内部函数也通过引用输入参数返回值。包装器不取此类参数,但将这些值返回到 Result
对象中
openssl_csr_new
:内部生成的 &$private_key 可以通过$result->privateKey()
获取openssl_encrypt
:内部生成的 &$tag 值可以通过$result->tag()
获取openssl_random_pseudo_bytes
:标志 &$strong_result 可以通过$result->strongResult()
获取openssl_seal
:内部生成的 &$encrypted_keys 和 &$iv 可以通过$result->encryptedKeys()
和$result->iv()
获取
如 OpenSSLAsymmetricKey、OpenSSLCertificateSigningRequest 和 OpenSSLCertificate 这样的复杂数据类型
一些函数(例如 openssl_pkey_new
)返回特殊的 OpenSSL 对象,不幸的是,它们单独使用时完全无用。这个库使它们变得更有用。因此,每次 openssl_*
函数返回一个对象
OpenSSLAsymmetricKey
被包装在AsymmetricKey
中OpenSSLCertificateSigningRequest
被包装在CSR
中OpenSSLCertificate
被包装在Certificate
中
所有这些对象包装器都提供了与 OpenSSL 对象类型相关的功能。例如
AsymmetricKey
对象涵盖了openssl_pkey_*
方法CSR
对象涵盖了openssl_x509_*
方法Certificate
对象涵盖了openssl_csr_*
方法
异常处理
当底层的 openssl_*
函数失败时,默认会抛出异常 OpenSSLCallFailedException
。这个特殊的异常包含了失败的具体信息,以便稍后检查。具体来说
$exception->funcName()
返回内部函数的名称$exception->result()
返回内部函数的确切返回代码$exception->errors()->openSSL()
返回 OpenSSL 库报告的警告/错误数组$exception->errors()->php()
返回 PHP(通过发出警告)报告的警告/错误数组
自定义/拦截异常
有时需要提供自定义异常而不是内置的 OpenSSLCallFailedException
。有几种方法可以做到这一点。
最简单的方法是将调用放在 try/catch
块中,然后重新抛出自定义异常,如下所示
use margusk\OpenSSL\Wrapper\Exception\OpenSSLCallFailedException; use margusk\OpenSSL\Wrapper\OpenSSL; class MyCustomException extends Exception {} try { $pkey = OpenSSL::pkeyNew([ 'private_key_type' => OPENSSL_KEYTYPE_RSA ])->value(); } catch (OpenSSLCallFailedException $e) { throw new MyCustomException('Something went wrong', 0, $e); }
然而,为了提供更灵活的失败拦截方式(例如,用于日志记录),我们可以注册失败处理程序并将其与特定的或任何类型的 openssl_*
函数关联起来
use margusk\OpenSSL\Wrapper\Exception\OpenSSLCallFailedException; use margusk\OpenSSL\Wrapper\Proxy as OpenSSLProxy; use margusk\OpenSSL\Wrapper\Proxy\Options as OpenSSLProxyOptions; class MyCustomException extends Exception { // ... } function myLoggingFunc(string $msg) { // ... } // Create Proxy options and register failure handler for "openssl_pkey_new" $options = (new OpenSSLProxyOptions()) ->registerFailureHandler('openssl_pkey_new', function(OpenSSLCallFailedException $exception): Throwable { myLoggingFunc($exception->funcName() . ': ' $exception->getMessage()); return new MyCustomException( $exception->getMessage(), $exception->getCode(), $exception ); }); // Create private wrapper instance $proxy = new OpenSSLProxy($options); // If openssl_pkey_new fails, then error is logged and MyOpenSSLException is thrown instead of OpenSSLCallFailedException try { $pkey = $proxy->pkeyNew([ 'private_key_type' => OPENSSL_KEYTYPE_RSA ])->value(); } catch (MyCustomException $e) { echo "MyCustomException: " . $e->getMessage() . "\n"; }
失败处理程序是通过 OpenSSLProxyOptions::registerFailureHandler(string $pattern, Closure $cb)
注册的,其中
-
$pattern 表示执行处理程序的内部函数名称。如果以 regex: 前缀,则其余部分被解释为正则表达式。
例如,regex:openssl_.* 或 regex:.* 将捕获所有失败的
openssl_*
函数 -
$cb 是接受一个参数(
OpenSSLCallFailedException
)并返回Throwable
的回调函数。在回调函数中直接抛出异常而不返回任何内容也是完全可以的。
请注意,OpenSSLProxyOptions
是不可变类,其中对 registerFailureHandler
的每次调用都会返回一个新的克隆实例,其中包含了新添加的处理程序。
要一次性注册多个处理程序而无需克隆并丢弃大量对象,请使用 OpenSSLProxyOptions::registerFailureHandlers(array $callbacks)
,其中 $callbacks
包含以关联方式指定的 $pattern
和 $cb
参数
use margusk\OpenSSL\Wrapper\Proxy\Options as OpenSSLProxyOptions; use margusk\OpenSSL\Wrapper\Exception\OpenSSLCallFailedException; // Register specific handler for "openssl_pkey_new" and another handler for the rest of "openssl_*" functions $options = (new OpenSSLProxyOptions()) ->registerFailureHandlers([ 'openssl_pkey_new' => function(OpenSSLCallFailedException $exception): Throwable { myLoggingFunc1($exception->funcName() . ': ' $exception->getMessage()); return new MyCustomException( $exception->getMessage(), $exception->getCode(), $exception ); }, 'regex:openssl_.*' => function(OpenSSLCallFailedException $exception): Throwable { myLoggingFunc2($exception->funcName() . ': ' $exception->getMessage()); return $exception; } ]);
许可证
此库根据 MIT 许可证发布。