PHP openssl_sign generates different signature than SSCrypto sign

I am writing an OS X client for software written in PHP. This software uses a simple RPC interface to receive and execute commands. The RPC client must sign the commands it sends to ensure that no MITM can change any of them.

However, since the server did not accept the signatures sent by me from my OS X client, I started investigating and found out that the PHP 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 opensl lib):

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

      

creates a signature like CtbkSxvqNZ+mAN

...

PHP Code

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

      

creates a type signature 6u0d2qjFiMbZ+

... (For my specific key, of course base64 encoded)

I don't quite understand why this is happening and I have experimented with different hash algorithms unsuccessfully. As stated in the PHP documentation, SHA1 is the default.

So why are these two functions generating different signatures and how can I get the Objective-C piece to generate the signature that PHPs openssl_verify will accept?

Note. I double checked that the keys and data are correct!

+2


a source to share


4 answers


Ok, it took quite a few hours. Here's what's going on:

When you call a function openssl_sign

, PHP internally uses the EVP API provided by the openssl library. EVP is a "high level" API for basic functions such as RSA_private_encrypt

. So when you call base64_encode(openssl_sign('hello', $signature, $privKey))

which is similar to this on the command line using the openssl binary:

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 results, but it does. If anyone has an idea why they are different: please share! However, since I am using the SSCrypto framework, I rewrote the -sign (and -verify) function with (abstract) EVP calls:

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 PHP. Oh, and for the record: PHP uses the PKCS addon.

Thanks guys for pointing me in the right direction!

+7


a source


Your code snippet was a big help, I had the same problem and I came up with the following method based on your lines in case it helps someone with the same problem.

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


a source


It looks to me like your PHP is signing "hello"

and your Objective-C is signing sha1("hello")

. That is, as I read the docs, PHP openssl_sign

uses sha1

signatures internally by default as opposed to applying sha1

to data before signing.

0


a source


Not that this fixes it, but your use of rsautl seems to be wrong. You should probably use rsautl -sign instead of rsautl -encrypt

0


a source







All Articles