使用PHP Sodium库验证Ed25519签名时遇CRYPTO_SIGN_PUBLICKEYBYTES错误求解
解决Ed25519签名验证的CRYPTO_SIGN_PUBLICKEYBYTES长度错误
问题根源:你使用的公钥
zGmof8SeyvHKnSEWv4i2mVv7MYe85D3zZqsTBjsKXSV是Base58编码后的字符串,而PHP Sodium的sodium_crypto_sign_verify_detached函数要求第三个参数必须是32字节的原始二进制公钥,编码后的字符串长度远大于32,因此触发了参数长度错误。解决步骤:
- 实现Base58解码函数,将编码后的公钥转换为32字节的二进制数据。
- 用解码后的二进制公钥替换原代码中的
$wallet_address参数。
具体代码实现
首先添加Base58解码函数(适配Ed25519常用的Base58编码规则):
function base58_decode(string $input): string|false { $alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; $base = strlen($alphabet); // 过滤无效字符 $input = preg_replace('/[^' . preg_quote($alphabet, '/') . ']/', '', $input); if ($input === '') { return false; } $decoded = gmp_init(0); foreach (str_split($input) as $char) { $pos = strpos($alphabet, $char); if ($pos === false) { return false; } $decoded = gmp_mul($decoded, $base); $decoded = gmp_add($decoded, $pos); } $decoded_str = gmp_strval($decoded, 16); // 处理Base58前导'1'对应的二进制前导零 $leading_zeros = strspn($input, $alphabet[0]); $decoded_str = str_repeat('00', $leading_zeros) . $decoded_str; // 补全十六进制字符串长度为偶数 if (strlen($decoded_str) % 2 !== 0) { $decoded_str = '0' . $decoded_str; } $binary = hex2bin($decoded_str); // 验证解码后是否为标准32字节公钥 return strlen($binary) === 32 ? $binary : false; }
然后修改签名验证代码:
// 解码Base58格式的公钥 $public_key_bin = base58_decode($wallet_address); if ($public_key_bin === false) { error_log('Invalid Base58 public key'); exit; } // 使用二进制公钥执行验证 if (sodium_crypto_sign_verify_detached(hex2bin($wallet_signature), $description, $public_key_bin)) { error_log('The message is authentic.'); } else { error_log('The message has been tampered with!'); }
注意事项
- 若前端公钥采用Base58Check编码(带校验和),需先截取前32字节数据再解码,避免校验和干扰。
- 测试解码后的公钥长度是否严格为32字节,确保前后端编码解码规则一致。
内容的提问来源于stack exchange,提问作者lsbtqba555
相关产品推荐
相关产品推荐

