wiki:actividades_2018_pruebas_cer_dig_lib_hwcrypto

Version 4 (modified by pbuitrago, 6 years ago) (diff)

--

Pruebas con la librería "hwcrypto"

La librería hwcrypto es la que se utiliza para gestionar la información en los dispositivos criptográfico necesaria para realizar el proceso de firma electrónica en el servicio Murachí. El problema que se esta presentando es en el momento de firmar el hash, respuesta de este proceso no corresponde al hash del documento a firmar. Como esta es la librería que se utiliza para dispositivo criptográfico la prueba consiste en tratar de usar la función de firma de esta librería para firmar el hash obtenido.

La función sign de la librería hwcripto firma el hash con la clave privada asociada con el certificado. Esta función requiere el certificado, el hash a firmar

//cert -> certificado del firmante
//hash -> hash a firmar con la clave asociada al certificado
//signature -> resultado de la operación (hash firmado)

window.hwcrypto.sign(cert, {type: hashtype, hex: hash}, {lang: lang}).then(function(signature) { 

}

El certificado que requiere la función sign es un objeto y tiene la siguiente forma:

objeto certificado requerido en la función hash

Cuyo objeto de certificado es obtenido por la función hwcripto getCertificate. Ahora esta función solo funciona para dispositivo criptográfico, por lo que se intento construir el objeto de certificado con los datos del certificado firmante del .p12, se uso la librería javascript Ramda en la siguiente función:

//certBytesHex -> certificado firmante extraído del .p12

certifiedObject() {

    const hexadecimalToUint8Array = string =>
    new Uint8Array(R.map(byte => parseInt(byte, 16), R.splitEvery(2, string)))

    const uint8ArrayToHexadecimal = array =>
    R.reduce((string, byte) => string + byte.toString(16), '', array)
    const badass = hexadecimalToUint8Array(certBytesHex);
    Cert = ({
	"hex":certBytesHex,
	"encoded":badass,
    });
}

El objeto certificado construido quedo de la siguiente manera:

objeto certificado cosntruido con Ramda

Pero no es reconocido por la función hash. al parecer es un objeto especifico construido por la función getCertificate y para probar esto se procedió a realizar al siguiente prueba, cargar tanto el certificado del archivo .p12 usando la librería Forge como el certificado extraído de un dispositivo criptografía usando la función getCerticate de la librería hwcripto, una vez obtenido el objeto de certificado, modificarlo colocando la información del certificado de software e firmar el hash.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
    <title>Javascript firma p12 Demo</title>
    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src="gitversion.js"></script>
    <script type="text/javascript" src="hwcrypto-legacy.js"></script>
    <script type="text/javascript" src="hwcrypto.js"></script>
    <script type="text/javascript" src="hex2base.js"></script>
    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript" src="forge.min.js"></script>
    <script type="text/javascript" src="ramda.min.js"></script>
</head>

<script>

//variable globales

var pkcs12 = null;
var pkcs12Der = null;
var pkcs12B64 = null;
var digestToSignB64 = "";
var privateKey = null;
var certP12bag = null;
var certBytesHex = null;
var signatureHEX = "";
var signatureBytesHEX = "";
var certCount = 0;
var certpem = "";
var asn1Cert = null;
var p12Der = null;
var cerToDer = null;
var p12Asn = null;
var certT = null;

function stringToArrayBuffer(data)
	{
	var arrBuff = new ArrayBuffer(data.length);
	var writer = new Uint8Array(arrBuff);
	for (var i = 0, len = data.length; i < len; i++) 
		{
		writer[i] = data.charCodeAt(i);
		}
	return arrBuff;
	}
function arrayBufferToString( buffer ) 
	{
	var binary = '';
	var bytes = new Uint8Array( buffer );
	var len = bytes.byteLength;
	for (var i = 0; i < len; i++) 
		{
		binary += String.fromCharCode( bytes[ i ] );
		}
	return binary;
	}
// From Private Key to a PKCS#8
function privateKeyToPkcs8(privateKey) 
 	{
	var rsaPrivateKey = forge.pki.privateKeyToAsn1(privateKey);
	var privateKeyInfo = forge.pki.wrapRsaPrivateKey(rsaPrivateKey);
	var privateKeyInfoDer = forge.asn1.toDer(privateKeyInfo).getBytes();
	var privateKeyInfoDerBuff = stringToArrayBuffer(privateKeyInfoDer);
	return privateKeyInfoDerBuff;
	}

// Create a CryptoKey from  from a PKCS#12 Private Key
function importCryptoKeyPkcs8(privateKey,extractable) 
	{
	var privateKeyInfoDerBuff = privateKeyToPkcs8(privateKey);
	//Importa la clave en la webcrypto
	return crypto.subtle.importKey(
		'pkcs8',
		privateKeyInfoDerBuff,
		{ name: "RSASSA-PKCS1-v1_5", hash:{name:"SHA-256"}},
		extractable,
		["sign"]);
	}
//function que obtengo la clave privada y el certificado del archivo .p12
function getCertificate(e)
	{
	console.log("*... getCertificate ...*");
	// Get PFX
	var fileInput = document.getElementById('pfx');
	var file = fileInput.files[0];
	// Read it
	var reader = new FileReader();
	reader.onload = function(e)  {
		console.log("*.... cargo el archivo .p12 ....*");
		var contents = e.target.result;
		pkcs12Der = arrayBufferToString(contents)
		pkcs12B64 = forge.util.encode64(pkcs12Der);
		var pkcs12Asn1 = forge.asn1.fromDer(pkcs12Der);
		var password = $('#pfxp').val();
		//var password = "123456";
		
		pkcs12 = forge.pkcs12.pkcs12FromAsn1(pkcs12Asn1, false, password);
		// load keys
		for(var sci = 0; sci < pkcs12.safeContents.length; ++sci) 
			{
			var safeContents = pkcs12.safeContents[sci];
			for(var sbi = 0; sbi < safeContents.safeBags.length; ++sbi) 
				{
				var safeBag = safeContents.safeBags[sbi];
				if(safeBag.type === forge.pki.oids.keyBag) 
					{
					//Found plain private key
					privateKey = safeBag.key;
					} 
				else 
				if(safeBag.type === forge.pki.oids.pkcs8ShroudedKeyBag) 
					{
					// found encrypted private key
					privateKey = safeBag.key;
					} 
				else 
				if(safeBag.type === forge.pki.oids.certBag) 
					{
					// this bag has a certificate...
					//certCount = certCount +1;						
					//console.log("GET_CERTIFICATE___"+ certCount + "...certBag Ok");  

					if(certP12bag == null) 
						{
						certP12bag = safeBag.cert;
				               
						certpem = forge.pki.certificateToPem(certP12bag);
						certBytesHex = forge.util.bytesToHex(certpem);
					   }
					}	
				}
			}
		}
		reader.readAsArrayBuffer(file);
	}
//Función que firmar el hash que devuelve el servicio Murachí
function SingFile()
	{
	console.log("*.... SingFile ....*");
	importCryptoKeyPkcs8(privateKey,true).then(function(cryptoKey) 
		{
		var digestToSign = forge.util.decode64(digestToSignB64);
		var digestToSignBuf = stringToArrayBuffer(digestToSign);
		crypto.subtle.sign(
			{name: "RSASSA-PKCS1-v1_5"},
			cryptoKey,
			digestToSignBuf)
			.then(function(signature){
				sign = arrayBufferToString(signature);
				signatureB64 = forge.util.encode64(sign);
				signatureHEX = forge.util.bytesToHex(signatureB64);
				signatureBytesHex = forge.util.bytesToHex(signature); //hash firmado
			});
		});
	}
//funcion que ejecuta el proceso de firma electrónica usando el servicio Murachí
function SignFilePDF() {
	//cargar el archivo .p12
	console.log("*.... SignFilePDF ....*");
	getCertificate();
    console.log("luego de getCertificate");
    var fileInput = document.getElementById("file-sign");
    var list = fileInput.files;
    var form = $('firmar')[0];
    var data = new FormData();
    data.append('upload', $("#file-sign")[0].files[0]);
    $.ajax({              
        url: "https://murachi.cenditel.gob.ve/Murachi/0.1/archivos",
        type: "post",
        dataType: "json",
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        xhrFields: {withCredentials: true},
        headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
        success: function(response) {
            var responseString = JSON.stringify(response);                                      
            document.getElementById("seccion1").innerHTML = responseString;
            var fileId = response.fileId.toString();
            alert("antes de leer el certificado");
            if(certBytesHex != null) {
            	console.log("tengo certificado");
	        var cert = certBytesHex;
	        console.log("certGet:"+ cert);
	        var parameters = JSON.stringify({
	            "fileId":fileId,
		    "certificate":cert,
		    "reason":"Certificado",
		    "location":"CENDITEL",
		    "contact":"582746574336",
		    "signatureVisible":"true"
		});
		console.log("cert...3");
		$.ajax({
		    type: 'POST',
		    contentType: 'application/json',                
		    url:"https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/pdfs",
		    dataType: "json",
		    data: parameters,
		    xhrFields: {withCredentials: true},        
		    headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
		    success: function(data, textStatus, jqXHR) {
		    var responseString = JSON.stringify(data);
		    document.getElementById("seccion2").innerHTML = responseString;
                    var json_x = data;
		    var hash = json_x['hash'];
		    alert("hash recibido del servidor " + hash);
                    // función para obtener el certificado del dispositivo criptografico 
		    window.hwcrypto.getCertificate({lang: "en"}).then(function(response) { 
			certT = response;
			//cambio por la información del certificado firmante .p12
			certT.hex = certBytesHex;
			certT.encoded = P12aSN;
		        var hashtype = "SHA-256";
		        var lang = "eng";
                        //función que firma el hash y le paso el objeto firmante modificado con la información del .p12
		        window.hwcrypto.sign(certT, {type: hashtype, hex: hash}, {lang: lang}).then(function(signature) {
		        
		        $.ajax({
		            type: 'POST',
		            contentType: 'application/json',
		            url:"https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/pdfs/resenas",
		            dataType: 'json',
		            data: JSON.stringify({"signature":signature.hex}),
		            xhrFields: {withCredentials: true},
		            headers: {"Authorization":"Basic YWRtaW46YWRtaW4="},
		            success: function(data, textStatus, jqXHR){
		                var responseString = JSON.stringify(data);
		                document.getElementById("seccion3").innerHTML = responseString;
		                alert('Archivo firmado correctamente: ' + data['signedFileId']);
		                document.getElementById("seccion4").innerHTML = "Descargar archivo firmado: https://murachi.cenditel.gob.ve/Murachi/0.1/archivos/descargas/" + data['signedFileId'];    
		            }, error: function(jqXHR, textStatus, errorThrown){
				alert('error en pdfs/resenas: ' + textStatus);
				$("#respuesta").html("error en pdfs/resenas: " + textStatus);
			 } // repuesta del ajax 3
		        }) //ajax 3
                    }) // cierre del window.hwcrypto.sign() 
		//})// cierre de window.getcertificate()
		} // respuesta ajax 2
            });//ajax 2
	    console.log("cert 4");
        }
    } // respuesta ajax 1
 }) //ajax 1
}
</script>
<div>
<label for="pfx">Seleccione el archivo PFX / P12::</label><br>
<input name="pfx" class="form-control" type="file" id="pfx" accept=".pfx,.p12" required /><br
<label for="pfxp">Ingrese la contraseña de clave privada:</label><br>
<input name="pfxp" class="form-control" type="password" id="pfxp" /><br>
</div>
<form enctype="multipart/form-data" method="post" id="firmar" name="SignFormat">
    <h2>Firmar electr&oacute;nica (PDF)</h2>
    <h3>Seleccione el archivo que va a firmar electr&oacute;nicamente</h2>        
    <br>
    <input id="file-sign" class="file" type="file" data-min-file-count="1" name="upload" accept=".pdf" >
    <br>
</form>
<button type="button" name="buttonsubmit3" id="buttonsubmit3" onclick="SignFilePDF();" class="btn btn-primary btn-lg">Firmar</button>
<button type="reset" id="reset"  class="btn btn-default">Limpiar</button>
<br>
<br>
<div id="seccion1"> </div>
<br>
<div id="seccion2"> </div>
<br>
<div id="seccion3"> </div>
<br>
<div id="seccion4"> </div>

</body>
</html>
</body>

LA prueba resulta positivo se logro firmar usando el certificado firmante del archivo .p12, claro esta en esta prueba se debe usar un dispositivo criptográfico y hacer la debida manipulación para que termine firmando con .p12

Attachments (2)

Download all attachments as: .zip