书接上回,在研究 XML 签名之前先研究一下 OpenSSL 库。
签名和原理
PHP 签名的函数原型是:
bool openssl_sign ( string $data , string &$signature , mixed $priv_key_id [, int $signature_alg ] )
大四时学的密码学全忘光了,用 Google CodeSearch 搜了一气 OpenSSL 的源代码才搞明白了签名的原理。
生成签名:
- 对 $data 按照 $signature_alg 设置的算法(默认是OPENSSL_ALGO_SHA1)进行 hash 运算
- 用私钥加密($priv_key_id)
- $signature 就是产生的签名数据,连同 $data 发送给接收方
可用的 hash 算法包括:
- OPENSSL_ALGO_SHA1
- OPENSSL_ALGO_MD5
- OPENSSL_ALGO_MD4
- OPENSSL_ALGO_MD2
验证:
- 用公钥解密签名数据
- 解密后的结果是 $data 的 hash 值,接受方再生成 $data 的 hash 值与之对比
签名验证算法
最常见的有两种:基于大数因子分解问题的数字签名(RSA),基于离散对数问题的数字签名(DSA),公私钥分别对应 ~/.ssh/id_rsa & ~/.ssh/id_rsa.pub 和 ~/.ssh/id_dsa & ~/.ssh/id_dsa.pub
二者区别在于 RSA 还可以用于加密,而 DSA 只能用于签名验证,二者签名效率相近,但 RSA 的验证要远快于 DSA。
所以 PHP 中使用 RSA,公私钥可以用 OpenSSL 工具包生成:
openssl genrsa 512 >id_rsa openssl rsa -puboutid_rsa.pub
我在 .ssh 目录下的 RSA 公私钥是用 ssh-keygen 生成的,但在在这里做试验失败,可能于密钥长度有关系。
验证函数原型:
int openssl_verify ( string $data , string $signature , mixed $pub_key_id [, int $signature_alg = OPENSSL_ALGO_SHA1 ] )
很好理解,$data 是接受方获取的数据,$signature 是一起发过来的签名,$pub_key_id 是校验用的公钥,$signature_alg 就是前面制定的 hash 算法了。
加密
函数原型:
int openssl_seal ( string $data , string &$sealed_data , array &$env_keys , array $pub_key_ids )
- 用随机字符串加密 data ( RC4 加密算法)
- 用 pub_key_ids 里的公钥对共享密钥进行加密,将结果保存在 env_keys
- 将 env_keys 和 sealed_data 发送给接收方
解密
函数原型:
bool openssl_open ( string $sealed_data , string &$open_data , string $env_key , mixed $priv_key_id )
- 私钥揭开 env_key
- 用 env_key 打开加密消息
用 PHP 创建公私钥
function get_key_pair() {
$ret = array();
$res = openssl_pkey_new();
openssl_pkey_export($res, $ret['private_key']);
$pub_key = openssl_pkey_get_details($res);
$ret['public_key'] = $pub_key['key'];
return $ret;
}