function valida_sello_tfd() { global $data, $texto; if ($data['sell'] != $data['sellocfd']) { echo "<h3>sello Comprobante diferente que sello TFD!, manipulado?</h3>"; } // Quita la parte del CFDI $texto_tfd = preg_replace('{<cfdi:Comprobante.*<tfd:}is', '<tfd:', $texto); $texto_tfd = preg_replace('{<retenciones:Retenciones.*<tfd:}is', '<tfd:', $texto_tfd); $texto_tfd = trim(preg_replace('{/>.*$}is', '/>', $texto_tfd)); // Si no tiene el namespace definido, se agrega if (strpos($texto_tfd, "xmlns:tfd") === FALSE) { $texto_tfd = substr($texto_tfd, 0, -2) . ' xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" />'; } // echo htmlspecialchars($texto_tfd); // Solo se quedo el tfd: $xml_tfd = new DOMDocument(); $ok = $xml_tfd->loadXML($texto_tfd); $xsl = new DOMDocument(); $xsl->load('xslt/cadenaoriginal_TFD_1_0.xslt'); $proc = new XSLTProcessor(); $proc->importStyleSheet($xsl); $cadena = $proc->transformToXML($xml_tfd); echo "Cadena Original TFD<br><p align=left>{$cadena}</p><br>"; if (!mb_check_encoding($cadena, "utf-8")) { echo "<h3>Error no esta en UTF-8!</h3>"; } // Certificado del PAC $pem = get_sat_cert($data['no_cert_sat']); $cert = "-----BEGIN CERTIFICATE-----\n" . chunk_split($pem, 64) . "-----END CERTIFICATE-----\n"; // file_put_contents("/tmp/llave.cer.pem",$cert); $pubkeyid = openssl_get_publickey(openssl_x509_read($cert)); $ok = openssl_verify($cadena, base64_decode($data['sellosat']), $pubkeyid, OPENSSL_ALGO_SHA1); if ($ok == 1) { echo "<h3>Sello TFD ok</h3>"; } else { echo "<h3>Sello TFD incorrecto</h3>"; while ($msg = openssl_error_string()) { echo $msg . "\n"; } } openssl_free_key($pubkeyid); echo "<hr>"; }
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 * * La version de mi maquina los pueden obtener de la misma URL * * http://www.lacorona.com.mx/fortiz/sat/cadenaoriginal_TFD_1_0.xslt * http://www.lacorona.com.mx/fortiz/sat/ecc.xslt * http://www.lacorona.com.mx/fortiz/sat/... * * [dev@www sat]$ ls *xslt * Divisas.xslt cfdiregistrofiscal.xslt nomina11.xslt * TuristaPasajeroExtranjero.xslt detallista.xslt pfic.xslt * cadenaoriginal_2_0.xslt donat11.xslt spei.xslt * cadenaoriginal_2_2.xslt ecc.xslt terceros11.xslt * cadenaoriginal_3_0.xslt iedu.xslt utilerias.xslt * cadenaoriginal_3_2.xslt implocal.xslt ventavehiculos.xslt * cadenaoriginal_TFD_1_0.xslt leyendasFisc.xslt * * * */ global $data, $xml; $xsl = new DOMDocument(); if ($data['tipo'] == "retenciones") { switch ($data['version']) { case "1.0": $xsl->load('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('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('cadenaoriginal_2_2.xslt'); echo "sha1 \n"; $algo = OPENSSL_ALGO_SHA1; break; case "3.0": $xsl->load('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('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)); } $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>"; } }