function valida_sello() { /* * Todos los archivos que se requieren para generar la cadena original * fueron descargados del portal del SAT pero los tengo localmente * almacenados en mi maquina para que el proceso sea mas rapido. * * Todos los archivos estan modificacion por el numero de version 2 a 1, * para que no mande warning PHP * * */ global $data, $xml; $xsl = new DOMDocument(); if ($data['tipo'] == "retenciones") { switch ($data['version']) { case "1.0": $xsl->load('xslt/retenciones.xslt'); $algo = OPENSSL_ALGO_SHA1; break; default: echo "version incorrecta " . $data['tipo'] . " " . $data['version'] . "\n"; break; } } else { switch ($data['version']) { case "2.0": $xsl->load('xslt/cadenaoriginal_2_0.xslt'); if (substr($data['fecha'], 0, 4) < 2011) { echo "md5 \n"; $algo = OPENSSL_ALGO_MD5; } else { echo "sha1 \n"; $algo = OPENSSL_ALGO_SHA1; } break; case "2.2": echo "2.2\n"; $xsl->load('xslt/cadenaoriginal_2_2.xslt'); echo "sha1 \n"; $algo = OPENSSL_ALGO_SHA1; break; case "3.0": $xsl->load('xslt/cadenaoriginal_3_0.xslt'); if (substr($data['fecha'], 0, 4) < 2011) { echo "md5 \n"; $algo = OPENSSL_ALGO_MD5; } else { echo "sha1 \n"; $algo = OPENSSL_ALGO_SHA1; } break; case "3.2": echo "3.2\n"; $xsl->load('xslt/cadenaoriginal_3_2.xslt'); echo "sha1 \n"; $algo = OPENSSL_ALGO_SHA1; break; default: echo "version incorrecta " . $data['tipo'] . " " . $data['version'] . "\n"; break; } } $proc = new XSLTProcessor(); $proc->importStyleSheet($xsl); $cadena = $proc->transformToXML($xml); echo "Cadena Original<br><p align=left>{$cadena}</p><br>"; if ($algo == OPENSSL_ALGO_SHA1) { $sha1 = sha1($cadena); echo "hash sha1={$sha1}<br>"; } else { $md5 = md5($cadena); echo "hash md5={$md5}<br>"; } if (!mb_check_encoding($cadena, "utf-8")) { echo "<h3>Error no esta en UTF-8!</h3>"; } /* * El domicilio es opcional, pero si no lo ponemos el xslt del SAT genera * doble pip en el pais ..., dice que el sello es correcto pero los PACs * que validan bien lo rechazan ... * */ $doble = preg_match('/.\\|\\|./', $cadena); if ($doble === 1) { echo "<h3><font color=red>La cadena tiene doble pipes en medio ...</font></h3>"; } // Primer certificado (o unico) del emisor // Los demas certificados es del PAC, Timbre, etc. $pem = sizeof($data['cert']) <= 1 ? $data['cert'] : $data['cert'][0]; $pem = eregi_replace("[\n|\r|\n\r]", '', $pem); $pem = preg_replace('/\\s\\s+/', '', $pem); // Si no incluye el certificado bajarlo del FTP del sat .... if (strlen($pem) == 0) { echo "No incluye certificado interno, descargarlo del FTP del sat ...<br>"; $pem = get_sat_cert($data['no_cert']); } $cert = "-----BEGIN CERTIFICATE-----\n" . chunk_split($pem, 64) . "-----END CERTIFICATE-----\n"; $pubkeyid = openssl_get_publickey(openssl_x509_read($cert)); if (!$pubkeyid) { echo "Certificado interno Incorrecto, descargarlo del FTP del sat ...<br>"; $pem = get_sat_cert($data['no_cert']); $cert = "-----BEGIN CERTIFICATE-----\n" . chunk_split($pem, 64) . "-----END CERTIFICATE-----\n"; $pubkeyid = openssl_get_publickey(openssl_x509_read($cert)); } valida_ca($pubkeyid); $ok = openssl_verify($cadena, base64_decode($data['sell']), $pubkeyid, $algo); if ($ok == 1) { echo "<h3>Sello ok</h3>"; } else { echo "<h3>Sello incorrecto</h3>"; while ($msg = openssl_error_string()) { echo $msg . "\n"; } } openssl_free_key($pubkeyid); echo "<hr>"; $paso = openssl_x509_parse($cert); $serial = convierte($paso['serialNumber']); if ($serial != $data['no_cert']) { echo "Serie reportada " . $data['no_cert'] . " serie usada {$serial}<br>"; } }
function checa_sello($texto, $xslt, $nodo) { $xml = new DOMDocument("1.0", "UTF-8"); $xml->loadXML($texto); $root = $xml->getElementsByTagName($nodo)->item(0); $sello = $root->getAttribute("Sello"); $no_cert = $root->getAttribute("noCertificado"); $cert = $root->getAttribute("Certificado"); if ($sello == "" && $no_cert == "" && $cert == "") { echo "No tiene datos de sello, NO valida<br>"; return; } if ($sello == "" || $no_cert == "" && $cert == "") { echo "<h3>No tiene datos de sello completo, ignorando</h3>"; return; } $paso = new DOMDocument("1.0", "UTF-8"); $paso->loadXML($texto); $xsl = new DOMDocument("1.0", "UTF-8"); $xsl->load($xslt); $proc = new XSLTProcessor(); $proc->importStyleSheet($xsl); $cadena_original = $proc->transformToXML($paso); $pem = "-----BEGIN CERTIFICATE-----\n" . chunk_split($cert, 64) . "-----END CERTIFICATE-----\n"; $paso = openssl_x509_parse($pem); $serial = convierte($paso['serialNumber']); if ($serial != $no_cert) { echo "Numero de certificado reportado {$no_cert} serie del certificado {$serial}<br>"; } $pubkeyid = openssl_get_publickey(openssl_x509_read($pem)); if ($pubkeyid === FALSE) { echo "<h3>El atributo 'Certificado' no contiene un certificado x509 valido </h3>"; return; } $algo = OPENSSL_ALGO_SHA1; $ok = openssl_verify($cadena_original, base64_decode($sello), $pubkeyid, $algo); if ($ok == 1) { echo "<h3>Sello ok, sha1</h3>"; } else { $algo = OPENSSL_ALGO_SHA256; $ok = openssl_verify($cadena_original, base64_decode($sello), $pubkeyid, $algo); if ($ok == 1) { echo "<h3>Sello ok, sha256</h3>"; echo "<h3>OJO el SAT no lo acepta, usa SHA1</h3>"; } else { echo "<h3>Sello incorrecto, ni sha1 ni sha256</h3>"; echo "{$cadena_original}<br>"; while ($msg = openssl_error_string()) { echo $msg . "\n"; } } } openssl_free_key($pubkeyid); }