Apple website push: Signature verification of push package failed, but apple certificates are ok

2.2k views Asked by At

I'm trying to implement Web push notifications to Safari using Codeigniter, I'm following the Apple Guide

and I created a library to create a Push Package based in this connorlacombe github project.

This is my library:

<?php if ( ! defined('BASEPATH')) exit("No direct script access allowed");class Apple_utils {

var $certificate_path;
var $certificate_password;
var $dir;
var $raw_files = array(
    "icon.iconset/icon_16x16.png",
    "icon.iconset/[email protected]",
    "icon.iconset/icon_32x32.png",
    "icon.iconset/[email protected]",
    "icon.iconset/icon_128x128.png",
    "icon.iconset/[email protected]",
    "website.json"
);

public function __construct($config = array())
{
    if (count($config) != 0){
        $this->initialize($config);
    }
    log_message("debug", "Apple Class Initialized");
}

public function initialize($config = array())
{
    if (empty($config["certificate_path"]) || empty($config["certificate_password"]) || empty($config["dir"])) return FALSE;
    $this->certificate_path = $config["certificate_path"];
    $this->certificate_password = $config["certificate_password"];
    $this->dir = $config["dir"];
    return $this;
}

// Creates the push package, and returns the path to the archive.
public function create_push_package($id) 
{        
    list($usec, $sec) = explode(" ", microtime());
$now = ((float)$usec + (float)$sec);

//DELETING OLD DIR
    $this->_delete_old_files($this->dir . "tmp/", $now);

    $package_dir = $this->dir . "tmp/" . $now;
    if (!mkdir($package_dir)) return FALSE;
    @chmod($package_dir, 0755);

    $this->_copy_raw_push_package_files($package_dir, $id);

    if (!$this->_create_manifest($package_dir)) return FALSE;

    if (!$this->_create_signature($package_dir)) return FALSE;

    $package_path = $this->_package_raw_data($package_dir);

    return $package_path;
}

private function _copy_raw_push_package_files($package_dir, $id) 
{
    mkdir($package_dir . "/icon.iconset");
    foreach ($this->raw_files as $raw_file) {
        copy($this->dir . "pushPackage/" . $raw_file, $package_dir . "/" . $raw_file);
        if ($raw_file == "website.json") {
    $wjson = file_get_contents($package_dir . "/". $raw_file);
    unlink($package_dir . "/". $raw_file);
    $ff = fopen($package_dir . "/". $raw_file, "x");
    fwrite($ff, str_replace(array("{BASE_URL}", "{AUTHTOKEN}"), array(rtrim(base_url(), "/"), "authenticationToken_".$id), $wjson)); 
    fclose($ff);
        }
    }
}

private function _create_manifest($package_dir) 
{
    // Obtain SHA1 hashes of all the files in the push package
    $manifest_data = array();
    foreach ($this->raw_files as $raw_file) {
        $manifest_data[$raw_file] = sha1(file_get_contents($package_dir . "/" . $raw_file));
    }
    file_put_contents($package_dir . "/manifest.json", json_encode( (object)$manifest_data ));
    return TRUE;
}

private function _create_signature($package_dir) 
{
    // Load the push notification certificate
    $pkcs12 = file_get_contents($this->certificate_path);
    $certs = array();
    if(!openssl_pkcs12_read($pkcs12, $certs, $this->certificate_password)){
        return FALSE;
    }

    $signature_path = $package_dir . "/signature";

    // Sign the manifest.json file with the private key from the certificate
    $cert_data = openssl_x509_read($certs["cert"]);
    $private_key = openssl_pkey_get_private($certs["pkey"], $this->certificate_password);
    openssl_pkcs7_sign($package_dir . "/manifest.json", $signature_path, $cert_data, $private_key, array(), PKCS7_BINARY | PKCS7_DETACHED);

    // Convert the signature from PEM to DER
    $signature_pem = file_get_contents($signature_path);
    $matches = array();
    if (!preg_match("~Content-Disposition:[^\n]+\s*?([A-Za-z0-9+=/\r\n]+)\s*?-----~", $signature_pem, $matches)){
        return FALSE;
    }
    $signature_der = base64_decode($matches[1]);
    file_put_contents($signature_path, $signature_der);
    return TRUE;
}

private function _package_raw_data($package_dir) 
{

    $CI = &get_instance();
    $CI->load->library("zip");

    $raw_files = $this->raw_files;
    $raw_files[] = "manifest.json";
    $raw_files[] = "signature";
    foreach ($raw_files as $raw_file) {
        $CI->zip->add_data($raw_file, file_get_contents($package_dir . "/" .$raw_file));
    }
    ob_end_clean(); //I HAVE TO PUT THIS HERE BECAUSE IF NOT THE ZIP CAN NOT BE OPENED
    $CI->zip->download( "Website.pushpackage.zip" );
}

function _delete_old_files($dir, $now)
{
    $expiration = 300; //seconds

    $current_dir = @opendir($dir);

while ($filename = @readdir($current_dir)){
        if ($filename != "." && $filename != ".." && $filename != "index.html"){
    $name = str_replace(".zip", "", $filename);

    if (($name + $expiration) < $now) $this->_delete_file($this->dir . "tmp/" . $filename);
        }
}

@closedir($current_dir);
}

function _delete_file($file)
{
    @chmod($file, 0755);
    if (is_dir($file)){
        $dir = @opendir($file); 
        while ($filename = @readdir($dir)){
    if ($filename != "." && $filename != ".."){
                $this->_delete_file($file . "/" . $filename);
    }
        }   
        @closedir($dir);
        @rmdir($file);
    }else{
        @unlink($file);
    }  
}}

The library works, create a .zip with, website.json, signature, etc. and was working perfect until yesterday, then not work anymore, when I check the logs always say "Signature verification of push package failed" and I has been created the certificates more than one time, but still the same error .

Please help.

1

There are 1 answers

5
Wilder Batista González On BEST ANSWER

Thanks to Jack, his solution has solved my problem:

openssl_pkcs7_sign("$package_dir/manifest.json", $signature_path, $cert_data, $private_key, array(), PKCS7_BINARY | PKCS7_DETACHED,"/path/to/certificate/AppleWWDRCA.pem");

To create AppleWWDRCA.pem you need:

1 - Download AppleWWDRCA.cer

2 - Execute the following command:

openssl x509 -inform der -in AppleWWDRCA.cer -out AppleWWDRCA.pem