phpjwtx509storekit

What fields do I verify in a x509 (as x5c header in a JWS) to prove legitimacy of the Certificate?


I've already posted a similar question here, but I've realized that my issue could have more to do with x509 certificate rather than JWS in general.

Here's the thing, I'm pretty new to JWS, and Apple now transmits them as part of their server-to-server communication. I'm trying to understand how to fully guarantee the validity of the JWS, because from what I understand, the signature verification only implies that the entire JWS wasn't tampered with. I don't know how to actually verify that this payload is indeed coming from a trusted source (aka Apple).

Here's what I got so far (PHP):

//1. explode jws and decode what's needed
$components = explode('.', $jws);
$headerJson = json_decode(base64_decode($components[0]),true);
$signature = base64Url_decode($components[2]);

//2. extract all certificates from 'x5c' header
foreach ($headerJson['x5c'] as $x5c){
    $c = '-----BEGIN CERTIFICATE-----'.PHP_EOL;
    $c .= chunk_split($x5c,64,PHP_EOL);
    $c .= '-----END CERTIFICATE-----'.PHP_EOL;
    $certificates[] = openssl_x509_read($c);
}

//3. verify validity of certificate chain (each one is signed by the next, except root cert)
for($i = 0; $i < count($certificates); $i++){
    if ($i == count($certificates) - 1){
        if (openssl_x509_verify($certificates[$i], $certificates[$i]) != 1){
            throw new Exception("Invalid Root Certificate");
        }
    }
    else{
        if (openssl_x509_verify($certificates[$i], $certificates[$i+1]) != 1){
            throw new Exception("Invalid Certificate");
        }
    }
}

//4. get public_key from first certificate
$public_key = openssl_pkey_get_public($certificates[0]);

//5. verify entire token, including signature (using the Firebase library)
$parsed_token = (array) \Firebase\JWT\JWT::decode($jws, $public_key, ['ES256']);

//helper function: a simple base64 url decoder
function base64Url_decode($data){
    return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}

Is there a specific field to check against inside the certificates (one that couldn't be spoofed) to verify the identity/source of the JWS?

Thanks!


Solution

  • Thanks to @IMSoP for pointing me in the right direction. When a JWS contains a chain of certificates (x5c header), we're supposed to have a copy of these valid certificates (or at least the root, since it validates the rest of the chain). In my case, I could find them on Apple's website.

    Once you download them, it's as easy as:

    openssl_x509_verify($jws_root_cert, $downloaded_apple_root_cert);