phprsaphp-7.1php-openssl

Validating an RSA PKCS#1v1.5 SHA 256 signature with an RSA 2048 public key


I have a public key stored in the database as a string. The generating library says that it is an RSA 2048 public key. Here is an example of the public key as it is stored:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHPAou1FpNAoLwF/mL0rei8QcC+LvKIYteEHOpxGibI6PNQ8kSpB7WNPJT2wM+B0+DT04L5LBi78PSo1UQO3umYEhHbAsKuNBV3CR8NGfUsWzSkgIzkbTCsELWkJT50foBT4YmBKEe6W8Ai2+3yzvMtic8RtBel73zS3dIdUUGaf7SMPtva+XDZTozRNiyligPh0nDgHcj6OQAtCxfTogLKukwaEPyWA06nQ68pMsxzzHqUZl5HJuDJRphAtOc2ZRFXZ1xaxVMO2D7UnkFA5q00UBW/i+KzkhgtNkLI8j2jiBim3IozRwpjQJvFsKoi29WDWPXcgtTU6k9kSmJBIqQIDAQAB

I have a data string and signature generated by the same library. According to the documentation, the signature is an RSA PKCS#1v1.5 SHA 256 signature.

Data string:

{"username":"user@example.com","requestTime":"1679669234"}

Signature:

PiXcXFykgtOZY+tpmmXqt39VHwURd5NSIGL0U+TEj+qS6QwIT4Eh4W3fKYfIXwZGDu6Rdsu2PQxqBMhEQe7H7C0nq6ssih74MLavxpLuGPxmlZJ/EN2/m2fhLweerjS6U1uMIDcKE6cZgSD6z+KfkuWnKssM5yuMEkdztL5ef0Ay6Il0jXGayp+tLMGxGZaJLv31PRmYoK3c68buGphKV0hnj2y6+rKsoPfn+sVDxInH40oNcxF2zU+Y3ed9pW+/VOpnkTE9MoSNthNvwjNpRFJr4Awx7mKmOGe3dvC3yZ+iZ/lT892pTSwqo1O2mtXskbsUe1+xEfrzbi1/0ejy/g==

I have been trying to use openssl_verify to validate this signature, but have not even been able to successfully parse the public key using openssl_pkey_get_public. I've tried variations on the following:

$publickey = "-----BEGIN RSA PUBLIC KEY-----\n".$publickey."\n-----END RSA PUBLIC KEY-----";
$pkey = openssl_pkey_get_public($publickey);

I've tried variations on both -----BEGIN RSA PUBLIC KEY----- AND -----BEGIN PUBLIC KEY-----, though I believe the RSA version to be correct. I've also tried using chunk_split($publickey, 64, "\n") to split the lines before passing to openssl_pkey_get_public.

After each attempt, openssl_error_string() returns the following:

error:0909006C:PEM routines:get_name:no start line

I don't understand why this is the case. Can anyone help me to validate this signature?

While I would prefer to avoid additional PHP libraries, if I must install a PHP library such as phpseclib I will.


Solution

  • This is a so called SPKI (subject public key identifier). That's the one without RSA in the PEM header line, i.e. just PUBLIC KEY.

    Maybe the problem is that the PEM is not structured correctly.

    You could e.g. try the following:

    $publickey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHPAou1FpNAoLwF/mL0rei8QcC+LvKIYteEHOpxGibI6PNQ8kSpB7WNPJT2wM+
    B0+DT04L5LBi78PSo1UQO3umYEhHbAsKuNBV3CR8NGfUsWzSkgIzkbTCsELWkJT50foBT4YmBKEe6W8Ai2+3yzvMtic8RtBel73zS3dIdUUGaf7SMPtva+XD
    ZTozRNiyligPh0nDgHcj6OQAtCxfTogLKukwaEPyWA06nQ68pMsxzzHqUZl5HJuDJRphAtOc2ZRFXZ1xaxVMO2D7UnkFA5q00UBW/i+KzkhgtNkLI8j2jiBi
    m3IozRwpjQJvFsKoi29WDWPXcgtTU6k9kSmJBIqQIDAQAB";
    preg_replace("(.{64})", "\1\r\n", $publickey);
    $publickey = "-----BEGIN PUBLIC KEY-----\r\n".$publickey."\r\n-----END PUBLIC KEY-----";
    $pkey = openssl_pkey_get_public($publickey);
    

    Note that PEM files do require base64, but that officially they have a line limit of 64 characters precisely. Older parser may have a requirement to use CR/LF instead of just LF, hence the \r\n in the strings.


    Because you were nice about it, the signature verification:

    $data = '{"username":"user@example.com","requestTime":"1679669234"}';
    $sig = "PiXcXFykgtOZY+tpmmXqt39VHwURd5NSIGL0U+TEj+qS6QwIT4Eh4W3fKYfIXwZGDu6Rdsu2PQxqBMhEQe7H7C0nq6ssih74MLavxpLuGPxmlZJ/EN2/m2fhLweerjS6U1uMIDcKE6cZgSD6z+KfkuWnKssM5yuMEkdztL5ef0Ay6Il0jXGayp+tLMGxGZaJLv31PRmYoK3c68buGphKV0hnj2y6+rKsoPfn+sVDxInH40oNcxF2zU+Y3ed9pW+/VOpnkTE9MoSNthNvwjNpRFJr4Awx7mKmOGe3dvC3yZ+iZ/lT892pTSwqo1O2mtXskbsUe1+xEfrzbi1/0ejy/g==";
    $res = openssl_verify($data, base64_decode($sig), $pkey, OPENSSL_ALGO_SHA256);