我正在为一个用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将接受的签名?
注意:我仔细检查了密钥和数据是否正确!
答案 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