Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get PHP to sign an input exactly the same as C#?

Tags:

c#

php

signing

I'm working with an older API that requires XMLs to be signed. There is no proper documentation and the only code example was given in C#. I need to port this example to PHP. However, the code I've written in PHP gets a different output even when provided with the same input, causing the API call to fail.

I've narrowed it to this function in C#:

public byte[] CreateSignature(byte[] hash)
{
    RSAPKCS1SignatureFormatter signatureFormatter = new RSAPKCS1SignatureFormatter(pfxCert.PrivateKey);
    signatureFormatter.SetHashAlgorithm("SHA1");
    return signatureFormatter.CreateSignature(hash);
}

Here's the same operation in PHP:

public function createSignature($hashByteArray, $certArray) {
    $hash = implode(array_map("chr", $hashByteArray));
    $hashEncoded = base64_encode($hash);
    openssl_sign($hashEncoded,$signature, $certArray);
    return unpack("C*", $signature);
}

Note, the input in openssl_sign can't take a byte array, so this is a possible point of difference. I've tried all algorithms provided by openssl_get_md_methods() and also phpseclib, neither have matched the output.

I've created a GitHub gist of the same example input and certificate in both C# and PHP to better illustrate the issue.

How can I get the same signing output in PHP as C#, given the same input?

like image 538
Adam Brown Avatar asked Dec 01 '20 17:12

Adam Brown


1 Answers

There's a fundamental difference between them.

The RSAPKCS1SignatureFormatter.CreateSignature method expects a data hash. openssl_sign on the other hand expects the data itself.

From PHP: openssl_sign - Manual

openssl_sign() computes a signature for the specified data by generating a cryptographic digital signature using the private key associated with priv_key_id. Note that the data itself is not encrypted.

Apparently you generate a hash of some data to use with the API written in C#. Do not do it with openssl_sign, instead call with the original data. openssl_sign will hash it before signing, this is by design.

like image 79
Kul-Tigin Avatar answered Sep 23 '22 12:09

Kul-Tigin