'I want to get data from API provided by Apple and I can't generate jwt token?

There is an API provided by Apple and I want to generate jwt tokens to use it. I will explain step by step what I did.

$pem_content = <<<EOD
        -----BEGIN RSA PRIVATE KEY----- 
       Private key provided by Apple
        -----END RSA PRIVATE KEY-----
        EOD;
        $header = json_encode(array(
            'alg' => 'ES256',
            'kid' => 'Keyid given by Apple',
            'typ' => 'JWT'
        ));

        $payload = json_encode(array(
            'iss' => ' issuer ID given by Apple',
            'iat' => 1623085200,
            'exp' => 1623086400,
            'aud' => 'appstoreconnect-v1',
            'bid' => 'My app’s bundle ID'
        ));
        $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
        $base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));

There is no problem in the above part, when I print it with dd(), token is generated and when I try it on jwt.io site, header and payload parts come

$signature = '';
        openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature,  openssl_get_privatekey($pem_content), OPENSSL_ALGO_SHA1);

When I print this part with dd() it returns true.

$signature = $this->FromDER($signature, 64);
        $base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
        $jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;

This is where the problem starts. What I learned is to convert the DER encoded signature to a raw combination of R and S values. In this library a function "fromDER" is present which perform such a conversion:

 /**
     * @param string $der
     * @param int    $partLength
     *
     * @return string
     */
    public static function fromDER(string $der, int $partLength)
    {
        $hex = unpack('H*', $der)[1];
        if ('30' !== mb_substr($hex, 0, 2, '8bit')) { // SEQUENCE
            throw new \RuntimeException();
        }
        if ('81' === mb_substr($hex, 2, 2, '8bit')) { // LENGTH > 128
            $hex = mb_substr($hex, 6, null, '8bit');
        } else {
            $hex = mb_substr($hex, 4, null, '8bit');
        }
        if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER
            throw new \RuntimeException();
        }
        $Rl = hexdec(mb_substr($hex, 2, 2, '8bit'));
        $R = self::retrievePositiveInteger(mb_substr($hex, 4, $Rl * 2, '8bit'));
        $R = str_pad($R, $partLength, '0', STR_PAD_LEFT);
        $hex = mb_substr($hex, 4 + $Rl * 2, null, '8bit');
        if ('02' !== mb_substr($hex, 0, 2, '8bit')) { // INTEGER
            throw new \RuntimeException();
        }
        $Sl = hexdec(mb_substr($hex, 2, 2, '8bit'));
        $S = self::retrievePositiveInteger(mb_substr($hex, 4, $Sl * 2, '8bit'));
        $S = str_pad($S, $partLength, '0', STR_PAD_LEFT);
        return pack('H*', $R.$S);
    }
    /**
     * @param string $data
     *
     * @return string
     */
    private static function preparePositiveInteger(string $data)
    {
        if (mb_substr($data, 0, 2, '8bit') > '7f') {
            return '00'.$data;
        }
        while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') <= '7f') {
            $data = mb_substr($data, 2, null, '8bit');
        }
        return $data;
    }
    /**
     * @param string $data
     *
     * @return string
     */
    private static function retrievePositiveInteger(string $data)
    {
        while ('00' === mb_substr($data, 0, 2, '8bit') && mb_substr($data, 2, 2, '8bit') > '7f') {
            $data = mb_substr($data, 2, null, '8bit');
        }
        return $data;
    }

But when I include it, I get RuntimeException. Where am I going wrong while generating tokens?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source