PHP openssl_sign生成与Crypto签名不同的签名

时间:2010-04-23 14:29:11

标签: php objective-c security openssl rsa

我正在为一个用PHP编写的软件编写OS X客户端。该软件使用简单的RPC接口来接收和执行命令。 RPC客户端必须对他发送的命令进行签名,以确保没有MITM可以修改它们中的任何一个。

但是,由于服务器不接受我从OS X客户端发送的签名,我开始调查并发现PHP的openssl_sign函数为给定的私钥/数据组合生成不同于Objective-C {{{ 3}} framework(它只是openssl lib的包装器):

SSCrypto *crypto = [[SSCrypto alloc] initWithPrivateKey:self.localPrivKey];
NSData *shaed = [self sha1:@"hello"];
[crypto setClearTextWithData:shaed];
NSData *data = [crypto sign];

生成类似CtbkSxvqNZ+mAN ...

的签名

PHP代码

openssl_sign("hello", $signature, $privateKey);

生成一个像6u0d2qjFiMbZ+这样的签名...... (对于我的某些键,当然.base64编码)

我不太清楚为什么会发生这种情况,而且我尝试使用不同的哈希算法也没有成功。由于PHP文档声明默认使用SHA1。

那么为什么这两个函数会生成不同的签名?如何让Objective-C部分生成PHP openssl_verify将接受的签名?

注意:我仔细检查了密钥和数据是否正确!

4 个答案:

答案 0 :(得分:8)

好的,花了不少时间。这是正在发生的事情:

当您调用openssl_sign函数时,PHP在内部使用openssl lib提供的EVP API。 EVP是RSA_private_encrypt等基础函数的“高级”API。因此,当您使用openssl二进制文件在命令行上调用base64_encode(openssl_sign('hello', $signature, $privKey))类似于执行此类操作时:

echo -n "hello"| openssl dgst -sha1 -sign priv.key | openssl enc -base64

echo -n "hello" | openssl dgst -sha1 | openssl rsautl -encrypt priv.key | openssl enc -base64

我不知道为什么会产生不同的输出,但确实如此。如果有人知道他们为什么不同:请分享!
但是,当我使用SSCrypto框架时,我用EVP调用重写了-sign(和-verify)函数(摘要):

OpenSSL_add_all_digests();
EVP_MD_CTX_init(&md_ctx);
EVP_SignInit(&md_ctx, mdtype);
EVP_SignUpdate(&md_ctx, input, inlen);
if (EVP_SignFinal(&md_ctx, (unsigned char*) outbuf, (unsigned int *)&outlen, pkey)) {
    NSLog(@"signed successfully.");
}

瞧:我得到了与PHP相同的签名。哦,为了记录:PHP使用PKCS填充。

谢谢你们指点我正确的方向!

答案 1 :(得分:2)

您的代码段是一个很大的帮助,我遇到了同样的问题,我根据您的行提出了以下方法,如果这可以帮助任何有相同问题的人。

- (NSData *)getSignature {
NSString * signString = [self getSignatureText];
NSData * privateKeyData = [self getPrivateKey];

BIO *publicBIO = NULL;
EVP_PKEY *privateKey = NULL;

if (!(publicBIO = BIO_new_mem_buf((unsigned char *)[privateKeyData bytes], [privateKeyData length]))) {
    NSLog(@"BIO_new_mem_buf() failed!");
    return nil;
}

if (!PEM_read_bio_PrivateKey(publicBIO, &privateKey, NULL, NULL)) {
    NSLog(@"PEM_read_bio_PrivateKey() failed!");
    return nil;
}   

const char * data = [signString cStringUsingEncoding:NSUTF8StringEncoding];
unsigned int length = [signString length];
int outlen;
unsigned char * outbuf[EVP_MAX_MD_SIZE];
const EVP_MD * digest = EVP_md5();
EVP_MD_CTX md_ctx;

EVP_MD_CTX_init(&md_ctx);
EVP_SignInit(&md_ctx, digest);
EVP_SignUpdate(&md_ctx, data, length);
if (EVP_SignFinal(&md_ctx, (unsigned char*) outbuf, (unsigned int *) &outlen, privateKey)) {
    NSLog(@"Signed successfully.");
}
EVP_MD_CTX_cleanup(&md_ctx);
EVP_PKEY_free(privateKey);

NSData * signature = [NSData dataWithBytes:outbuf length:outlen];

return signature;

}

答案 2 :(得分:0)

我认为您的PHP正在签署"hello",而您的Objective-C正在签署sha1("hello")。也就是说,我阅读文档的方式,PHP的openssl_sign默认在签名的内部机制中使用sha1,而不是在签名之前将sha1应用于数据。

答案 3 :(得分:0)

不是它修复了,但你对rsautl的使用似乎是不正确的。您可能应该使用rsautl -sign而不是rsautl -encrypt