I'm working on replacing a legacy system that (among other things) receives SHA1 hashes of arbitrary files and signs them using a private key with a simple PHP web service.
It should look something like that:
$providedInput = '13A0227580C5DE137C2EBB2907A3F2D7F00CA71D';
// pseudo "= sha1(somefile.txt); file not available server side!
$expectedOutput = 'DBC9CC4CB0BECEE313BB100DD1AD39AEC045714D72767211FD574E3E3546EB55E77D2EBFE33BA2974BB74CE051608BFF45A73A52612C5FC418DD3A76CAC0AE0C8FB3FC6CE4F7A516013A9743A36424DDACFE889B3D45E86E6853FD9A55B5B4F0F0D8A574A0B244C0946A99B81CCBD1A7AF7C11072745B11C06AD680BE8AC4CB4';
// pseudo: "= openssl_sign(file_get_contents(somefile.txt), signature, privateKeID);
For the sake of simplicity I'm using PHP's built in openssl extention. The problem I'm running into is that openssl_sign seems to SHA1 hash the input data again internally according to this German manual entry on openssl_sign. The English entry is missing that info for some reason.
This produces the expected output ...
$privateKeyID = openssl_get_privatekey(file_get_contents($privateKey));
openssl_sign(file_get_contents("x.txt"), $signature, $privateKeyID);
var_dump(bin2hex($signature));
... but since I don't have access to the actual input files on server side it's not very helpful.
Is there a way around the additional hashing without 3rd party libs? I already tried to simply encrypt the hash received, but from How to compute RSA-SHA1(sha1WithRSAEncryption) value I understand encrypting and signing produce different output.
Update to make things more clear:
I'm recieving an SHA1 hash as input and the service has to convert it to a valid signature (using a private key) that can simply be verified using openssl_verify. The clients are out of reach, so changing their implementation is not possible.
From How to compute RSA-SHA1(sha1WithRSAEncryption) value:
If you reproduce this EM and use RSA_private_encrypt, then you will get the correct PKCS#1 v1.5 signature encoding, the same you would get with RSA_sign or even better, using the generic EVP_PKEY_sign.
I figured I could simply implement the DER encoding myself according to this specification, but the result (EM) seems too long to be encrypted with my key
// 1. Apply the hash function to the message M to produce a hash value H
$H = hex2bin($input); // web service receives sha1 hash of an arbitrary file as input
$emLen = 128; // 1024 rsa key
// 2. Encode the algorithm ID for the hash function and the hash value into
// an ASN.1 value of type DigestInfo
$algorithmIdentifier = pack('H*', '3021300906052b0e03021a05000414');
$digest = $H;
$digestInfo = $algorithmIdentifier.$digest;
$tLen = strlen($digestInfo);
// 3. error checks omitted ...
// 4. Generate an octet string PS consisting of emLen - tLen - 3 octets
// with hexadecimal value 0xff. The length of PS will be at least 8
// octets.
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
//5. Concatenate PS, the DER encoding T, and other padding to form the
// encoded message EM as
$em = "\0\1$ps\0$digestInfo";
if(!openssl_private_encrypt($em, $signature, $privateKeyID)) {
echo openssl_error_string();
}
else {
echo bin2hex($signature);
}
Output:
Error:0406C06E:rsa routines:RSA_padding_add_PKCS1_type_1:data too large for key size
Any hints?