【发布时间】:2017-08-15 11:18:44
【问题描述】:
在我参与的一个项目中,一位队友添加了phpseclib (for php5),我们使用下面的代码对加密字符串进行签名,并将其与内部 API 的请求一起发送到验证过程。
function GetSignature($message) {
$rsa = new Crypt_RSA();
$sPath = PRIVATE_KEY_FILE; //defined elsewhere
$sKey = file_get_contents($sPath);
$rsa->LoadKey($sKey);
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
return $rsa->sign($message);
};
对于包含与 URL 连接的时间戳的字符串(例如 1490239371+https://api-domain/api/endpoint),将生成如下所示的字符串。此字符串在请求标头中发送,下面的字符串是内部 API 控制台日志中的输出。
Rsh1dv5ZGaPjCb6pgEsMrXwZbAeVgxVK / + 5d5bxu7BfDzaILl ++ / PI / WxDP4H2qJ7Ayp6QnYXGckIIYX9l9fKOJoShOZOkB19RxaNdBL5vLjKk409XVRY / GKGz3kHmZmTcyBYDPQaT / VFOQTd7 + o0d1mBY4EbHadI3f + kahHz4U = EM>
在一个较新的项目中,我试图重新创建它,因为我们需要使用相同的内部 API。较新的项目正在使用composer(和laravel),因此将phpseclib添加到其中添加了version 2.0。常量已更改为类常量,但除此之外,方法似乎相同。下面是我添加的代码,用于在其他代码库中复制以前的代码。
use phpseclib\Crypt\RSA as Crypt_RSA;
class EncyptionController {
public static function GetSignature($message) {
$rsa = new Crypt_RSA();
$path = config('app.encryption_key_path') . '/certs/private.pem';
$rsa->loadKey(file_get_contents($path));
$rsa->setSignatureMode(Crypt_RSA::SIGNATURE_PKCS1);
return $rsa->sign($message);
}
使用较新的代码,相同格式的字符串将生成包含 unicode 字符的加密字符串。下面的字符串出现在内部 API 的控制台日志中(在记录请求标头中收到的值时)。
=���˭\u0003�uʂ\u000e_\u0013\u000bd)w�8�\u0018�����q
在 Laravels 日志中记录以 unicode 字符显示它(没有编码的实体 - 我在 notepad++ 中打开它):
=žšîË´uÊ‚_d)wË8ƒ”ñ‘¤úqýÁ‹þ:‹dºäÿLÈ?!F§?…ãIEìê*K®gUþèÎÅ
然后内部 API 使用公钥解密编码的字符串。对于较新的代码库,这将失败。内部 API 在 Nginx/NodeJS 中实现,使用 crypto 及其 verifier.verify() 方法。
更新:
基于Álvaro González 的the comment,发现2.0 版sign() 方法返回原始二进制哈希。我可以使用base64_encode() 对其进行base-64 编码,但该字符串比1.0 版生成的字符串(172 个字符)短很多(88 个字符)。
return base64_encode($rsa->sign($message));
1.0 版字符串 - 172 个字符长:
Rsh1dv5ZGaPjCb6pgEsMrXwZbAeVgxVK / + 5d5bxu7BfDzaILl ++ / PI / WxDP4H2qJ7Ayp6QnYXGckIIYX9l9fKOJoShOZOkB19RxaNdBL5vLjKk409XVRY / GKGz3kHmZmTcyBYDPQaT / VFOQTd7 + o0d1mBY4EbHadI3f + kahHz4U =” “Rsh1dv5ZGaPjCb6pgEsMrXwZbAeVgxVK / + 5d5bxu7BfDzaILl ++ / PI / WxDP4H2qJ7Ayp6QnYXGckIIYX9l9fKOJoShOZOkB19RxaNdBL5vLjKk409XVRY / GKGz3kHmZmTcyBYDPQaT / VFOQTd7 + o0d1mBY4EbHadI3f + kahHz4U = P>
2.0 版字符串 - 88 个字符长:
mYT/MSb9UOuDDQ1RV893Ix7xh21IRHINs6o1PnhdhffgTAIeX4le1rfd+EzoPPJN9pvvwirm3CAbkeubGjXgWQ==
有没有设置签名字符串长度的方法?
【问题讨论】:
-
你是如何打印这个的?当您将输出写入文件并在“好”的编辑器中打开它时,您是否看到同样的问题?
-
好问题@ArtjomB。我更新了帖子以包含该信息。原始字符串通过 console.log() 写入 nginx 的日志。我将新代码库中的输出包含到 laravel 的日志中(并在 notepad++ 中打开日志)。
-
@SamOnela 这与 Unicode 无关(Unicode 的全部目的是处理文本,这显然是二进制数据)。在 v1 中,您得到了一个编码为 base64 的哈希值。在 v2 中,您将获得原始二进制哈希。正如他们警告的那样,v2 API 不是最终版本,在 v3 之前可能随时更改。您应该首先比较两个哈希值,看看它们是否相同。
-
感谢@Álvaro González 指出这一点。在手动对原始哈希进行 base-64 编码后,我更新了问题以处理不同的字符串长度...
标签: php rsa phpseclib encryption-asymmetric