简体   繁体   English

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

[英]PHP's openssl_sign generates different signature than SSCrypto's sign

I'm writing an OS X client for a software that is written in PHP. 我正在为一个用PHP编写的软件编写OS X客户端。 This software uses a simple RPC interface to receive and execute commands. 该软件使用简单的RPC接口来接收和执行命令。 The RPC client has to sign the commands he sends to ensure that no MITM can modify any of them. RPC客户端必须对他发送的命令进行签名,以确保没有MITM可以修改它们中的任何一个。

However, as the server was not accepting the signatures I sent from my OS X client, I started investigating and found out that PHP's openssl_sign function generates a different signature for a given private key/data combination than the Objective-C SSCrypto framework (which is only a wrapper for the openssl lib): 但是,由于服务器不接受我从OS X客户端发送的签名,我开始调查并发现PHP的openssl_sign函数为给定的私钥/数据组合生成与Objective-C SSCrypto框架不同的签名(这是只是openssl lib的包装器:

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

generates a signature like CtbkSxvqNZ+mAN ... 生成像CtbkSxvqNZ+mAN这样的签名......

The PHP code PHP代码

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

generates a signature like 6u0d2qjFiMbZ+ ... (For my certain key, of course. base64 encoded) 生成一个像6u0d2qjFiMbZ+ ...的签名(对于我的某些键,当然6u0d2qjFiMbZ+编码)

I'm not quite shure why this is happening and I unsuccessfully experimented with different hash-algorithms. 我不太清楚为什么会发生这种情况而且我没有成功地尝试使用不同的哈希算法。 As the PHP documentation states SHA1 is used by default. 由于PHP文档声明默认使用SHA1。

So why do these two functions generate different signatures and how can I get my Objective-C part to generate a signature that PHPs openssl_verify will accept? 那么为什么这两个函数会生成不同的签名?如何让Objective-C部分生成PHP openssl_verify接受的签名?

Note: I double checked that the keys and the data is correct! 注意:我仔细检查了密钥和数据是否正确!

Okay, it took quite a few hours. 好的,花了不少时间。 Here's what's happening: 这是发生了什么:

When you call the openssl_sign function, PHP internally uses the EVP API that is provided by the openssl lib. 当您调用openssl_sign函数时,PHP在内部使用openssl lib提供的EVP API。 EVP is a "high level" API to the underlying functions like RSA_private_encrypt . EVP是RSA_private_encrypt等底层函数的“高级”API。 So when you call base64_encode(openssl_sign('hello', $signature, $privKey)) that's similar to do something like this on the command line using the openssl binary: 因此,当您调用base64_encode(openssl_sign('hello', $signature, $privKey)) ,使用openssl二进制文件在命令行上执行类似的操作:

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

and NOT 不是

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

I don't know why this produces different output , but it does. 我不知道为什么会产生不同的输出,但确实如此。 If someone has an idea why they differ: please share! 如果有人知道他们为什么不同:请分享!
However, as I'm using the SSCrypto framework I rewrote the -sign (and -verify) function with EVP calls (abstract): 但是,当我使用SSCrypto框架时,我用EVP调用重写了-sign(和-verify)函数(abstract):

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.");
}

And voila: I get the same signatures as I got from PHP. 瞧:我得到了与PHP相同的签名。 Oh and for the record: PHP uses the PKCS padding. 哦,为了记录:PHP使用PKCS填充。

Thank you guys for pointing me in the right direction! 谢谢你们指点我正确的方向!

Your code snippet was a big help, I had the same problem, and I came up with the following method based on your lines, if that helps anyone with the same issue. 你的代码片段是一个很大的帮助,我有同样的问题,我想出了基于你的行的以下方法,如果这有助于任何人有同样的问题。

- (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;

} }

It looks to me like your PHP is signing "hello" , while your Objective-C is signing sha1("hello") . 它看起来像你的PHP正在签署"hello" ,而你的Objective-C正在签署sha1("hello") That is, the way I read the docs, PHP's openssl_sign is using sha1 by default in the internal mechanics of signing, as opposed to applying sha1 to the data before signing. 也就是说,我阅读文档的方式,PHP的openssl_sign在签名的内部机制中默认使用sha1 ,而不是在签名之前将sha1应用于数据。

Not that it fixes, but your use of rsautl seems to be incorrect. 不是它修复,但你使用rsautl似乎是不正确的。 You probably should be using rsautl -sign instead of rsautl -encrypt 您可能应该使用rsautl -sign而不是rsautl -encrypt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM