How to extract certificates from app attestation object using php?

1k views Asked by At

I tried to set up app attestation between my app and php but I rarely find any other source of explaination than Apple's own documentation, which let me stuck quite at an early state. So far I got the following steps:

On the client side, following https://developer.apple.com/documentation/devicecheck/establishing_your_app_s_integrity, I creted my attestation as a base64 encoded string:

attestation.base64EncodedString()

I then send that string to the server, following https://developer.apple.com/documentation/devicecheck/validating_apps_that_connect_to_your_server from now on.

The documentation says, that the attestation is in the CBOR format. I therefor first decode the base64 encoded string and parse it using (https://github.com/Spomky-Labs/cbor-php).

<?php
use CBOR\Decoder;
use CBOR\OtherObject;
use CBOR\Tag;
use CBOR\StringStream;

$otherObjectManager = new OtherObject\OtherObjectManager();
$tagManager = new Tag\TagObjectManager();

$decoder = new Decoder($tagManager, $otherObjectManager);
$data = base64_decode(/* .. base64 encoded attestation string as send from the client (see swift snippet above) */);

$stream = new StringStream($data);
$object = $decoder->decode($stream);

$norm = $object->getNormalizedData();
$fmt = $norm['fmt'];
$x5c = $norm['attStmt']['x5c'];

From the documentation, the normalized object should have the following format:

{
   fmt: 'apple-appattest',
   attStmt: {
     x5c: [
       <Buffer 30 82 02 cc ... >,
       <Buffer 30 82 02 36 ... >
     ],
     receipt: <Buffer 30 80 06 09 ... >
   },
   authData: <Buffer 21 c9 9e 00 ... >
 }

which it does:

$fmt == "apple-appattest" // true

Then the next according to the documentation is described as:

Verify that the x5c array contains the intermediate and leaf certificates for App Attest, starting from the credential certificate in the first data buffer in the array (credcert). Verify the validity of the certificates using Apple’s App Attest root certificate.

However, I don't know how to proceed further on this. The content of e.g. $norm['attStmt']['x5c'][0] is a mix of readable chars and glyphs. To give you an idea, this is a random substring from the content of $norm['attStmt']['x5c'][0]: "Certification Authority10U Apple Inc.10 UUS0Y0*�H�=*�H�=B��c�}�". That's why I'm not really sure wheather I have to perform any further encodeing/decoding steps.

I tried parsing the certificate but without any luck (both var_dump return false):

 $cert = openssl_x509_read($x5c[0]);
 var_dump($cert); // false - indicating that reading the cert failed
 
 $parsedCert = openssl_x509_parse($cert, false);
 var_dump($parsedCert); // false - of course, since the prior step did not succeed

Any ideas, guidance or alternative ressources are highly appreciated. Thank you!

1

There are 1 answers

0
Leo On BEST ANSWER

After a while I came up with the following solution. The $x5c field contains a list of certificates, all in binary form. I wrote the folowing converter to create a ready-to-use certificate in PEM format, which does the following:

  1. base64 encode the binary data
  2. break lines after 64 bytes
  3. add BEGIN and END markers (also note the trailing line-break on the end certificate line)

function makeCert($bindata) {
     $beginpem = "-----BEGIN CERTIFICATE-----\n";
    $endpem = "-----END CERTIFICATE-----\n";

    $pem = $beginpem;
    $cbenc = base64_encode($bindata);
    for($i = 0; $i < strlen($cbenc); $i++) {
        $pem .= $cbenc[$i];
        if (($i + 1) % 64 == 0)
            $pem .= "\n";
    }
    $pem .= "\n".$endpem;

    return $pem;
}

the following then works:

openssl_x509_read(makeCert($x5c[0]))