1 2if (document.all && !window.setTimeout.isPolyfill) { 3 var __nativeST__ = window.setTimeout; 4 window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) { 5 var aArgs = Array.prototype.slice.call(arguments, 2); 6 return __nativeST__(vCallback instanceof Function ? function () { 7 vCallback.apply(null, aArgs); 8 } : vCallback, nDelay); 9 }; 10 window.setTimeout.isPolyfill = true; 11} 12 13var originalXMLHttpRequest = window.XMLHttpRequest; 14 15var MiniApplet = ( function ( window, undefined ) { 16 17 var VERSION = "1.5"; 18 19 var JAR_NAME = 'miniapplet-full_1_5.jar'; 20 21 var JAVA_ARGUMENTS = '-Xms512M -Xmx512M '; 22 23 var SYSTEM_PROPERTIES = null; 24 25 var clienteFirma = null; 26 27 var codeBase = null; 28 29 var storageServletAddress = null; 30 31 var retrieverServletAddress = null; 32 33 var clientType = null; 34 35 var severeTimeDelay = false; 36 37 var selectedLocale = null; 38 39 var LOCALIZED_STRINGS = new Array(); 40 LOCALIZED_STRINGS["es_ES"] = { 41 checktime_warn: "Se ha detectado un desfase horario entre su sistema y el servidor. Se recomienda que se corrija antes de pulsar Aceptar para continuar.", 42 checktime_err: "Se ha detectado un desfase horario entre su sistema y el servidor. Debe corregir la hora de su sistema antes de continuar.", 43 checktime_local_time: "Hora de su sistema", 44 checktime_server_time: "Hora del servidor" 45 }; 46 LOCALIZED_STRINGS["gl_ES"] = { 47 checktime_warn: "Destectouse un desfase horario entre o seu sistema e o servidor. Recoméndase corrixilo antes de pulsar Aceptar para continuar.", 48 checktime_err: "Destectouse un desfase horario entre o seu sistema e o servidor. Debe corrixir a hora do seu sistema antes de continuar.", 49 checktime_local_time: "Hora do seu sistema", 50 checktime_server_time: "Hora do servidor" 51 }; 52 53 var DEFAULT_LOCALE = LOCALIZED_STRINGS["es_ES"]; 54 55 var currentLocale = DEFAULT_LOCALE; 56 57 var defaultKeyStore = null; 58 59 /* ------------------------------------------------ */ 60 /* Constantes para la operacion interna del Cliente */ 61 /* ------------------------------------------------ */ 62 63 /* Tamano del buffer con el que se pasa informacion al applet */ 64 var BUFFER_SIZE = 2 * 1024 * 1024; 65 66 /* Cadena que determina el fin de una respuesta */ 67 var EOF = "%%EOF%%"; 68 69 /* Identifica que se utilizara el MiniApplet. */ 70 var TYPE_APPLET = "APPLET"; 71 72 /* Identifica que se utilizara una aplicacion nativa de firma. */ 73 var TYPE_JAVASCRIPT_WEB_SERVICE = "JAVASCRIPT_WEB_SERVICE"; 74 var TYPE_JAVASCRIPT_SOCKET = "JAVASCRIPT_SOCKET"; 75 76 /* --------------------------------- */ 77 /* Constantes publicas */ 78 /* --------------------------------- */ 79 80 /* Almacenes de certificados */ 81 82 var KEYSTORE_WINDOWS = "WINDOWS"; 83 84 var KEYSTORE_APPLE = "APPLE"; 85 86 var KEYSTORE_PKCS12 = "PKCS12"; 87 88 var KEYSTORE_PKCS11 = "PKCS11"; 89 90 var KEYSTORE_MOZILLA = "MOZ_UNI"; 91 92 var KEYSTORE_SHARED_NSS = "SHARED_NSS"; 93 94 var KEYSTORE_JAVA = "JAVA"; 95 96 var KEYSTORE_JCEKS = "JCEKS"; 97 98 var KEYSTORE_JAVACE = "JAVACE"; 99 100 var KEYSTORE_DNIE = "DNIEJAVA"; 101 102 /* Valores para la configuracion de la comprobacion de tiempo */ 103 104 var CHECKTIME_NO = "CT_NO"; 105 106 var CHECKTIME_RECOMMENDED = "CT_RECOMMENDED"; 107 108 var CHECKTIME_OBLIGATORY = "CT_OBLIGATORY"; 109 110 // Tiempo de espera entre los intentos de conexion con autofirma. 111 var AUTOFIRMA_LAUNCHING_TIME = 2000; 112 113 // Reintentos de conexion totales para detectar que esta instalado AutoFirma 114 var AUTOFIRMA_CONNECTION_RETRIES = 10; 115 116 // Variable que se puede configurar para forzar el uso del modo de comunicacion por servidor intermedio 117 // entre la pagina web y AutoFirma 118 var forceWSMode = false; 119 120 /* ------------------------------------ */ 121 /* Funciones de comprobacion de entorno */ 122 /* ------------------------------------ */ 123 124 /** 125 * Determina con un boolean si nuestro cliente es Android 126 */ 127 function isAndroid() { 128 return navigator.userAgent.toUpperCase().indexOf("ANDROID") != -1 || 129 navigator.appVersion.toUpperCase().indexOf("ANDROID") != -1 || 130 // Para la deteccion de los Kindle Fire 131 navigator.userAgent.toUpperCase().indexOf("SILK/") != -1 || 132 navigator.userAgent.toUpperCase().indexOf("KFJWI") != -1 || 133 navigator.userAgent.toUpperCase().indexOf("KFJWA") != -1 || 134 navigator.userAgent.toUpperCase().indexOf("KFTT") != -1 || 135 navigator.userAgent.toUpperCase().indexOf("KFOT") != -1 || 136 navigator.userAgent.toUpperCase().indexOf("KINDLE FIRE") != -1 137 ; 138 } 139 140 /** 141 * Determina con un boolean si nuestro cliente es iOS. 142 */ 143 function isIOS() { 144 return (navigator.userAgent.toUpperCase().indexOf("IPAD") != -1) || 145 (navigator.userAgent.toUpperCase().indexOf("IPOD") != -1) || 146 (navigator.userAgent.toUpperCase().indexOf("IPHONE") != -1); 147 } 148 149 /** Determina con un boolean si nos encontramos en Windows 8/8.1 */ 150 function isWindows8() { 151 return navigator.userAgent.indexOf("Windows NT 6.2") != -1 || /* Windows 8 */ 152 navigator.userAgent.indexOf("Windows NT 6.3") != -1; /* Windows 8.1 */ 153 } 154 155 /** Determina con un boolean si nos encontramos en Windows RT */ 156 function isWindowsRT() { 157 return isWindows8() && navigator.userAgent.indexOf("ARM;") != -1; 158 } 159 160 /** Determina con un boolean si estamos en Internet Explorer */ 161 function isInternetExplorer() { 162 return !!(navigator.userAgent.match(/MSIE/)) /* Internet Explorer 10 o inferior */ 163 || !!(navigator.userAgent.match(/Trident/) && navigator.userAgent.match(/rv:11/)) /* Internet Explorer 11 o superior (Opcion 1) */ 164 || !!navigator.userAgent.match(/Trident.*rv[ :]*11\./); /* Internet Explorer 11 o superior (Opcion 2) */ 165 } 166 167 /** Indica si el navegador es Internet Explorer 10 o inferior. */ 168 function isOldInternetExplorer() { 169 return !!(navigator.userAgent.match(/MSIE/)); 170 } 171 172 function isFirefoxUAM() { 173 return navigator.userAgent.indexOf("UAM") > 0;
174 } 175 176 /** 177 * Determina con un boolean si se accede a la web con Firefox 178 */ 179 function isFirefox(){ 180 return navigator.userAgent.toUpperCase().indexOf("FIREFOX") != -1 181 } 182 183 /** 184 * Determina con un boolean si se accede a la web con Chrome 185 */ 186 function isChrome() { 187 return navigator.userAgent.toUpperCase().indexOf("CHROME") != -1 || 188 navigator.userAgent.toUpperCase().indexOf("CHROMIUM") != -1; 189 } 190 191 /** 192 * Determina con un boolean si el navegador es Microsoft Edge 193 */ 194 function isEdge() { 195 return !!navigator.userAgent.match(/Edge\/\d+/); 196 } 197 198 /** Indica si el navegador detecta Java. Este valor no es completamente fiable, ya que 199 * Internet Explorer siempre indica que si esta activado. */ 200 function isJavaEnabled() { 201 return navigator.javaEnabled(); 202 } 203 204 /** Comprueba si una cadena de texto es una URL (http/https). La alternativa implicaria ser un Base64. */ 205 function isValidUrl(data) { 206 return data != null && data.length > "https://".length && 207 ("http:" == data.substr(0, 5) || "https:" == data.substr(0, 6)); 208 } 209 210 var downloadSuccessFunction = null; 211 var downloadErrorFunction = null; 212 var downloadTypeData = null; 213 214 /** 215 * Realiza la descarga de datos de una URL y, una vez termina, llama al metodo 216 * successFunction pasandole los datos descargados en base 64, o errorFunction, 217 * si fallo en la descarga, al que se pasa el error que produjo el problema. 218 */ 219 function downloadRemoteData(url, successFunction, errorFunction) { 220 221 downloadSuccessFunction = successFunction; 222 downloadErrorFunction = errorFunction; 223 224 var httpRequest = getHttpRequest(); 225 httpRequest.open("GET", url, true); 226 227 // new browsers (XMLHttpRequest2-compliant) 228 if ('responseType' in httpRequest && !!Uint8Array) { 229 httpRequest.responseType = 'arraybuffer'; 230 downloadTypeData = 'arraybuffer'; 231 } 232 // old browsers (XMLHttpRequest-compliant) 233 else 234 if ('overrideMimeType' in httpRequest) { 235 httpRequest.overrideMimeType('text\/plain; charset=x-user-defined'); 236 downloadTypeData = 'string'; 237 } 238 // IE9 (Microsoft.XMLHTTP-compliant) 239 else { 240 httpRequest.setRequestHeader('Accept-Charset', 'x-user-defined'); 241 downloadTypeData = 'bytearray'; 242 } 243 244 // shim for onload for old IE 245 if (!('onload' in httpRequest)) { 246 httpRequest.onreadystatechange = function () { 247 if (this.readyState === 4) { 248 this.onload(); 249 } 250 }; 251 } 252 253 httpRequest.onload = function (evt) { 254 if (httpRequest.readyState == 4 && httpRequest.status == 200) { 255 if (downloadSuccessFunction) { 256 257 // emulating response field for IE9 258 if (!('response' in this)) { 259 this.response = new VBArray(this.responseBody).toArray(); 260 } 261 262 var b64; 263 if (downloadTypeData == 'arraybuffer') { 264 b64 = Base64.encodeArrayBuffer(this.response); 265 } 266 else if (downloadTypeData == 'bytearray') { 267 b64 = Base64.encodeByteArray(this.response); 268 } 269 else if (downloadTypeData == 'string') { 270 b64 = Base64.encode(this.response); 271 } 272 else { 273 this.onerror(new Error("No se conoce el modo usado para la descarga de los datos")); 274 } 275 downloadSuccessFunction(b64); 276 } 277// else { 278// console.log("Se termino la descarga de los datos. No se invoca a ninguna funcion."); 279// } 280 } 281 } 282 httpRequest.onerror = function(e) { 283 if (downloadErrorFunction) { 284 downloadErrorFunction(e); 285 } 286// else { 287// console.log("Error en la descarga de los datos. No se invoca a ninguna funcion."); 288// } 289 } 290 httpRequest.send(); 291 } 292 293 function getHttpRequest() { 294 var xmlHttp = null; 295 296 /* Si se cargo Ajax, puede haber modificado el funcionamiento de la clase XMLHttpRequest, 297 * asi que vemos si el propio Ajax (Sarissa) guarda la implementacion original; si no, 298 * usamos la implementacion por defecto que la habremos guardado al principio del script 299 * en la variable originalXMLHttpRequest. Es importante que se haya cargado este script 300 * antes que el de Ajax o esta opcion no tendra resultado */ 301 if (typeof XMLHttpRequest != "undefined") { // Navegadores actuales 302 if (typeof Sarissa !== 'undefined' && Sarissa.originalXMLHttpRequest) { 303 xmlHttp = new Sarissa.originalXMLHttpRequest(); 304 } else { 305 xmlHttp = new originalXMLHttpRequest(); 306 } 307 } else if (typeof window.ActiveXObject != "undefined") { // Internet Explorer antiguos 308 try { 309 xmlHttp = new ActiveXObject("Msxml2.XMLHTTP.4.0"); 310 } catch (e) { 311 try { 312 xmlHttp = new ActiveXObject("MSXML2.XMLHTTP"); 313 } catch (e) { 314 try { 315 xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); 316 } catch (e) { 317 xmlHttp = null; 318 } 319 } 320 } 321 } 322 return xmlHttp; 323 } 324 325 /** Permite establecer que la comunicacion con AutoFirma sea a traves 326 * del servidor intermedio. */ 327 var setForceWSMode = function (force) { 328 forceWSMode = force; 329 } 330 331 /** Permite habilitar la comprobacion de la hora local contra la hora del servidor y 332 * establecer un tiempo maximo permitido y el comportamiento si se supera. 333 * Parametros: 334 * - checkType: Tipo de comprobacion. Admite los valores CT_NO, CT_RECOMMENDED y CT_OBLIGATORY. 335 * - maxMillis: Tiempo maximo de desfase en milisegundos. 336 * - checkURL: URL contra la que se realizara la peticion para obtener la hora. Si no se 337 * indica, se usara una pagina web aleatoria dentro del propio dominio. 338 * Cuando el HTML es local, no se realiza ningun tipo de comprobacion. 339 */ 340 var checkTime = function (checkType, maxMillis, checkURL) { 341 try { 342 343 if (checkType == undefined || checkType == null || checkType == CHECKTIME_NO 344 || maxMillis == undefined || maxMillis == null || maxMillis <= 0) { 345 return; 346 } 347 348 // Si checkURL existe mandamos la peticion ahi, en caso contrario nos inventamos una url. 349 var URL; 350 if (checkURL) { 351 URL = checkURL; 352 } 353 else { 354 URL = document.URL + '/' + Math.random(); 355 } 356 // Hacemos una llamada al servidor para conocer su hora 357 var xhr = getHttpRequest(); 358 xhr.open('GET', URL, false); 359 xhr.send(); 360 361 // Recogemos la hora local, nada mas obtener la respuesta del servidor 362 var clientDate = new Date(); 363 364 // Tomamos la hora a partir de la respuesta del servidor. Si esta es 0, estamos en local 365 var serverDate = new Date(xhr.getResponseHeader("Date")); 366 if (serverDate == null || serverDate.getTime() == 0) { 367 // No hacemos nada si estamos en local 368 return; 369 } 370 371 // Evaluamos la desincronizacion
372 var delay = Math.abs(clientDate.getTime() - serverDate.getTime()); 373 if (delay > maxMillis) { 374 if (checkType == CHECKTIME_RECOMMENDED) { 375 alert(currentLocale.checktime_warn + 376 "\n" + currentLocale.checktime_local_time + ": " + clientDate.toLocaleString() + 377 "\n" + currentLocale.checktime_server_time + ": " + serverDate.toLocaleString()); 378 } 379 else if (checkType == CHECKTIME_OBLIGATORY) { 380 severeTimeDelay = true; 381 alert(currentLocale.checktime_err + 382 "\n" + currentLocale.checktime_local_time + ": " + clientDate.toLocaleString() + 383 "\n" + currentLocale.checktime_server_time + ": " + serverDate.toLocaleString()); 384 } 385 } 386 } 387 catch (e) { 388 // No hacemos nada si estamos en local 389 //console.log("Error en la obtencion de la hora del servidor: " + e); 390 return; 391 } 392 } 393 /** Carga el MiniApplet. */ 394 var cargarMiniApplet = function (base, keystore) { 395 396 // Antes que nada, comprobamos que no haya un desfase horario declarado como 397 // grave. 398 if (severeTimeDelay) { 399 return; 400 } 401 402 // Sincronizamos las variables que puedan haberse establecido de forma externa 403 // antes de la llamada al metodo de carga 404 JAVA_ARGUMENTS = MiniApplet.JAVA_ARGUMENTS; 405 SYSTEM_PROPERTIES = MiniApplet.SYSTEM_PROPERTIES; 406 407 // Establecemos cual sera el keystore por defecto 408 defaultKeyStore = keystore; 409 if (!defaultKeyStore) { 410 defaultKeyStore = getDefaultKeystore(); 411 } 412 413 // Configuramos los argumentos para la seleccion de almacen 414 configureKeyStore(); 415 416 // Si estamos claramente en un sistema movil o que no permite la ejecucion de Java, 417 // cargamos directamente el Cliente JavaScript 418 if (isAndroid() || isIOS() || isWindowsRT() || isChrome() || isEdge()) { 419 cargarAppAfirma(base, defaultKeyStore); 420 return; 421 } 422 423 // Si estamos en un entorno que permite Java, comprobamos si esta disponible 424 // y en caso de no estarlo, tambien cargamos el Cliente JavaScript. 425 if (!isJavaEnabled()) { 426 cargarAppAfirma(base, defaultKeyStore); 427 return; 428 } 429 430 // Incluso si el navegador informa que hay Java, puede no haberlo (Internet Explorer 431 // siempre dice que hay), asi que cargamos el applet, pero tenemos en cuenta que en 432 // caso de error debemos cargar el cliente JavaScript 433 codeBase = (base != undefined && base != null) ? base : './'; 434 435 var attributes = { 436 'id': 'miniApplet', 437 'name': 'MiniApplet @firma (Gobierno de Espa\u00F1a)', 438 'type': 'application/x-java-applet', 439 'width': 1, 440 'height': 1 441 }; 442 443 // Los argumentos de java no llegan al propio applet en las pruebas con Java 6 y 7, 444 // asi que (salvo los argumentos de carga) vamos a pasarlos como un parametro mas al 445 // applet para luego establecerlos internamente. 446 var parameters = { 447 'keystore': defaultKeyStore, 448 'userAgent': window.navigator.userAgent, 449 'archive': codeBase + '/' + JAR_NAME, 450 'code': 'es.gob.afirma.miniapplet.MiniAfirmaApplet', 451 'java-vm-args': JAVA_ARGUMENTS, 452 'java_arguments': JAVA_ARGUMENTS, 453 'system_properties': SYSTEM_PROPERTIES, 454 'codebase_lookup': false, 455 'separate_jvm': true, 456 'locale': selectedLocale 457 }; 458 459 loadMiniApplet(attributes, parameters); 460 461 if (isFirefox()) { 462 clienteFirma = document.getElementById("miniApplet"); 463 464 // Si no esta definido el cliente es porque se ha intentado cargar el applet 465 // y no se ha podido, asi que se usara la aplicacion nativa 466 if (clienteFirma == null) { 467 cargarAppAfirma(codeBase, defaultKeyStore); 468 } 469 } 470 else { 471 clienteFirma = document.getElementById("miniApplet"); 472 473 // Si no esta definido el cliente es porque se ha intentado cargar el applet 474 // y no se ha podido, asi que se usara la aplicacion nativa 475 if (clienteFirma == null) { 476 cargarAppAfirma(codeBase, defaultKeyStore); 477 } 478 } 479 } 480 481 /** Obtiene el nombre del almacen que corresponde al presente navegador o, si se debe acceder 482 * al almacen del sistema, se devuelve null. */ 483 function getDefaultKeystore() { 484 if(isFirefox()){ 485 return KEYSTORE_MOZILLA; 486 } 487 return null; 488 } 489 490 /** Establece los parametros de configuracion para la correcta seleccion del almacen 491 * de claves que se debe cargar. */ 492 function configureKeyStore() { 493 if (isFirefoxUAM()) { 494 if (SYSTEM_PROPERTIES == null) { 495 SYSTEM_PROPERTIES = ""; 496 } 497 SYSTEM_PROPERTIES += " -Des.gob.afirma.keystores.mozilla.UseEnvironmentVariables=true "; 498 } 499 } 500 501 /** Carga el MiniApplet. */ 502 var setKeyStore = function (ksType) { 503 504 forceLoad(); 505 506 clienteFirma.setKeyStore(ksType != null ? ksType : defaultKeyStore); 507 } 508 509 var selectCertificate = function (params, successCallback, errorCallback) { 510 511 forceLoad(); 512 513 if (clientType == TYPE_APPLET) { 514 try { 515 var certificate = clienteFirma.selectCertificate(params); 516 if (successCallback == undefined || successCallback == null) { 517 return certificate; 518 } 519 successCallback(certificate); 520 } catch(e) { 521 if (errorCallback == undefined || errorCallback == null) { 522 throw e; 523 } 524 errorCallback(clienteFirma.getErrorType(), clienteFirma.getErrorMessage()); 525 } 526 } 527 else { 528 clienteFirma.selectCertificate(params, successCallback, errorCallback); 529 } 530 } 531 532 var sign = function (dataB64, algorithm, format, params, successCallback, errorCallback) { 533 534 forceLoad(); 535 536 if (clientType == TYPE_APPLET) { 537 try { 538 setData(dataB64); 539 var certSignaturePair = buildData(clienteFirma.sign(algorithm, format, params)); 540 var sepPos = certSignaturePair.indexOf('|'); 541 if (successCallback == undefined || successCallback == null) { 542 return certSignaturePair.substring(sepPos + 1); 543 } 544 successCallback(certSignaturePair.substring(sepPos + 1), certSignaturePair.substring(0, sepPos)); 545 } catch(e) { 546 if (errorCallback == undefined || errorCallback == null) { 547 throw e; 548 } 549 errorCallback(clienteFirma.getErrorType(), clienteFirma.getErrorMessage()); 550 } 551 } 552 else { 553 clienteFirma.sign(dataB64, algorithm, format, params, successCallback, errorCallback); 554 } 555 } 556 557 var coSign = function (signB64, dataB64, algorithm, format, params, successCallback, errorCallback) { 558 559 forceLoad(); 560 561 if (clientType == TYPE_APPLET) { 562 try { 563 setData(signB64); 564 var certSignaturePair = buildData(clienteFirma.coSign(dataB64, algorithm, format, params)); 565 var sepPos = certSignaturePair.indexOf('|'); 566 if (successCallback == undefined || successCallback == null) { 567 return certSignaturePair.substring(sepPos + 1); 568 } 569 successCallback(certSignaturePair.substring(sepPos + 1), certSignaturePair.substring(0, sepPos)); 570 } catch(e) { 571 if (errorCallback == undefined || errorCallback == null) { 572 throw e; 573 } 574 errorCallback(clienteFirma.getErrorType(), clienteFirma.getErrorMessage()); 575 } 576 } 577 else { 578 clienteFirma.coSign(signB64, dataB64, algorithm, format, params, successCallback, errorCallback); 579 } 580 } 581 582 583 var counterSign = function (signB64, algorithm, format, params, successCallback, errorCallback) { 584 585 forceLoad(); 586 587 if (clientType == TYPE_APPLET) { 588 try { 589 setData(signB64); 590 var certSignaturePair = buildData(clienteFirma.counterSign(algorithm, format, params)); 591 var sepPos = certSignaturePair.indexOf('|'); 592 if (successCallback == undefined || successCallback == null) { 593 return certSignaturePair.substring(sepPos + 1); 594 } 595 successCallback(certSignaturePair.substring(sepPos + 1), certSignaturePair.substring(0, sepPos)); 596 } catch(e) { 597 if (errorCallback == undefined || errorCallback == null) { 598 throw e; 599 } 600 errorCallback(clienteFirma.getErrorType(), clienteFirma.getErrorMessage()); 601 } 602 } 603 else { 604 clienteFirma.counterSign(signB64, algorithm, format, params, successCallback, errorCallback); 605 } 606 } 607 608 var signAndSaveToFile = function (operationId, dataB64, algorithm, format, params, outputFileName, successCallback, errorCallback) { 609 610 forceLoad(); 611 612 if (clientType == TYPE_APPLET) { 613 try { 614 setData(dataB64); 615 var certSignaturePair = buildData(clienteFirma.signAndSaveToFile(operationId, algorithm, format, params, outputFileName)); 616 var sepPos = certSignaturePair.indexOf('|'); 617 if (successCallback == undefined || successCallback == null) { 618 return certSignaturePair.substring(sepPos + 1); 619 } 620 successCallback(certSignaturePair.substring(sepPos + 1), certSignaturePair.substring(0, sepPos)); 621 } catch(e) { 622 if (errorCallback == undefined || errorCallback == null) { 623 throw e; 624 } 625 errorCallback(clienteFirma.getErrorType(), clienteFirma.getErrorMessage()); 626 } 627 } 628 else { 629 clienteFirma.signAndSaveToFile(operationId, dataB64, algorithm, format, params, outputFileName, successCallback, errorCallback); 630 } 631 } 632 633 var signBatch = function (batchB64, batchPreSignerUrl, batchPostSignerUrl, params, successCallback, errorCallback) { 634 635 forceLoad(); 636 637 if (clientType == TYPE_APPLET) { 638 try { 639 if (successCallback == undefined || successCallback == null) { 640 return clienteFirma.signBatch(batchB64, batchPreSignerUrl, batchPostSignerUrl, params); 641 } 642 successCallback(clienteFirma.signBatch(batchB64, batchPreSignerUrl, batchPostSignerUrl, params)); 643 } 644 catch (e) { 645 if (errorCallback == undefined || errorCallback == null) { 646 throw e; 647 } 648 errorCallback(clienteFirma.getErrorType(), clienteFirma.getErrorMessage()); 649 return; 650 } 651 } 652 else { 653 clienteFirma.signBatch(batchB64, batchPreSignerUrl, batchPostSignerUrl, params, successCallback, errorCallback); 654 } 655 } 656 657 var getBase64FromText = function (plainText, charset) { 658 forceLoad(); 659 return clienteFirma.getBase64FromText(plainText, charset); 660 } 661 662 var getTextFromBase64 = function (dataB64, charset) { 663 forceLoad(); 664 return clienteFirma.getTextFromBase64(dataB64, charset); 665 } 666 667 var saveDataToFile = function (dataB64, title, fileName, extension, description, successCallback, errorCallback) { 668 forceLoad(); 669 if (clientType == TYPE_APPLET) { 670 setData(dataB64); 671 return clienteFirma.saveDataToFile(title, fileName, extension, description); 672 } 673 else { 674 clienteFirma.saveDataToFile(dataB64, title, fileName, extension, description, successCallback, errorCallback); 675 } 676 } 677 678 var getFileNameContentBase64 = function (title, extensions, description, filePath) { 679 forceLoad(); 680 return buildData(clienteFirma.getFileNameContentBase64(title, extensions, description, filePath)); 681 } 682 683 var getMultiFileNameContentBase64 = function (title, extensions, description, filePath) { 684 forceLoad(); 685 return buildData(clienteFirma.getMultiFileNameContentBase64(title, extensions, description, filePath)); 686 } 687 688 var echo = function () { 689 forceLoad(); 690 return clienteFirma.echo(); 691 } 692 693 var setStickySignatory = function (sticky) { 694 forceLoad(); 695 return clienteFirma.setStickySignatory(sticky); 696 } 697 698 var setLocale = function (locale) { 699 selectedLocale = locale;
700 currentLocale = (locale == null || LOCALIZED_STRINGS[locale] == null ? DEFAULT_LOCALE : LOCALIZED_STRINGS[locale]); 701 } 702 703 var getErrorMessage = function () { 704 forceLoad(); 705 return clienteFirma.getErrorMessage(); 706 } 707 708 var getErrorType = function () { 709 forceLoad(); 710 return clienteFirma.getErrorType(); 711 } 712 713 var getCurrentLog = function () { 714 forceLoad(); 715 return " === JAVASCRIPT INFORMATION === " + 716 "\nnavigator.appCodeName: " + navigator.appCodeName + 717 "\nnavigator.appName: " + navigator.appName + 718 "\nnavigator.appVersion: " + navigator.appVersion + 719 "\nnavigator.platform: " + navigator.platform + 720 "\nnavigator.userAgent: " + navigator.userAgent+ 721 "\nnavigator.javaEnabled(): " + navigator.javaEnabled() + 722 "\nscreen.width: " + (window.screen ? screen.width : 0) + 723 "\nscreen.height: " + (window.screen ? screen.height : 0) + 724 "\n\n === CLIENTE LOG === \n" + 725 clienteFirma.getCurrentLog(); 726 } 727 728 var setServlets = function (storageServlet, retrieverServlet) { 729 730 storageServletAddress = storageServlet; 731 retrieverServletAddress = retrieverServlet; 732 733 if (clienteFirma && clienteFirma.setServlets) { 734 clienteFirma.setServlets(storageServlet, retrieverServlet); 735 } 736 } 737 738 /************************************************************* 739 * FUNCIONES PARA EL DESPLIEGUE DEL APPLET * 740 **************************************************************/ 741 742 function loadMiniApplet(attributes, parameters) { 743 // Internet Explorer se carga mediante un 744 // elemento <object>. El resto con un <embed>. 745 if (isInternetExplorer()) { 746 747 var appletTag = "<object classid='clsid:8AD9C840-044E-11D1-B3E9-00805F499D93' width='" + attributes["width"] + "' height='" + attributes["height"] + "' id='" + attributes["id"] + "'>"; 748 749 if (attributes != undefined && attributes != null) { 750 for (var attribute in attributes) { 751 appletTag += "<param name='" + attribute + "' value='" + attributes[attribute] + "' />"; 752 } 753 } 754 755 if (parameters != undefined && parameters != null) { 756 for (var parameter in parameters) { 757 appletTag += "<param name='" + parameter + "' value='" + parameters[parameter] + "' />"; 758 } 759 } 760 761 appletTag += "</object>"; 762
763 // Al agregar estos nodos con append() no se carga automaticamente el applet en IE10 e inferiores, asi que 764 // hay que usar document.write() o innerHTML. Para asegurarnos de no pisar HTML previo, crearemos un <div> 765 // en la pagina, lo recogeremos e insertaremos dentro suyo el codigo del applet. 766 var divElem = document.createElement("div"); 767 var idAtt = document.createAttribute("id"); 768 idAtt.value = 'divAfirmaApplet'; 769 divElem.setAttributeNode(idAtt); 770 771 document.body.appendChild(divElem); 772 773 document.getElementById("divAfirmaApplet").innerHTML = appletTag; 774 } 775 else { 776 var embed = document.createElement("embed"); 777 778 if (attributes != undefined && attributes != null) { 779 for (var attribute in attributes) { 780 var att = document.createAttribute(attribute); 781 att.value = attributes[attribute]; 782 try { 783 embed.setAttributeNode(att); 784 } 785 catch (e) { 786 // Probamos este como alternativa en caso de error. Caso detectado en: 787 // - IE10 sin modo de compabilidad con el Document Mode de IE7. 788 // - Firefox en Mac OS X 789 // Este intento no soluciona el error, pero evita que se propague 790 embed.setAttribute(attribute, attributes[attribute]); 791 } 792 } 793 } 794 795 if (parameters != undefined && parameters != null) { 796 for (var parameter in parameters) { 797 var att = document.createAttribute(parameter); 798 att.value = parameters[parameter]; 799 embed.setAttributeNode(att); 800 } 801 } 802 803 document.body.appendChild(embed); 804 } 805 } 806 807 /** 808 * Establece los datos que debera procesar el applet MiniApplet. 809 */ 810 function forceLoad() { 811 812 // Antes que nada, comprobamos que no haya un desfase horario declarado como 813 // grave. 814 if (severeTimeDelay) { 815 return; 816 }
817 if (clientType == null || clientType == TYPE_JAVASCRIPT_WEB_SERVICE || clientType == TYPE_JAVASCRIPT_SOCKET ) { 818 819 var tempCliente = document.getElementById("miniApplet"); 820 var appletLoaded; 821 try { 822 appletLoaded = tempCliente != null && tempCliente.echo() != "Cliente JavaScript"; 823 } 824 catch (e) { 825 appletLoaded = false; 826 } 827 if (appletLoaded) { 828 clienteFirma = tempCliente; 829 clientType = TYPE_APPLET; 830 } 831 else if (clientType == null) { 832 cargarAppAfirma(codeBase, defaultKeyStore); 833 } 834 setServlets(storageServletAddress, retrieverServletAddress); 835 } 836 } 837 838 /** 839 * Establece los datos que debera procesar el applet MiniApplet. Los datos se le 840 * pasan al applet en fragmentos porque el JavaPlugin no permite recibir o enviar 841 * mas de 4Mb de datos de una sola vez desde JavaScript. 842 */ 843 function setData(dataB64) { 844 845 if (dataB64 == null) { 846 return; 847 } 848 else if (dataB64.length <= BUFFER_SIZE) { 849 clienteFirma.addData(dataB64); 850 } 851 else { 852 clienteFirma.addData(dataB64.substring(0, BUFFER_SIZE)); 853 setData(dataB64.substring(BUFFER_SIZE)); 854 } 855 } 856 857 /** 858 * Construye el resultado de una funcion a partir de los trozos en la que esta los divide. 859 */ 860 function buildData(dataB64) { 861 var buffer = dataB64; 862 var chunk = clienteFirma.getRemainingData(); 863 while(chunk != EOF) { 864 buffer += chunk; 865 chunk = clienteFirma.getRemainingData(); 866 } 867 return buffer; 868 } 869 870 /************************************************************** 871 ************************************************************** 872 ************************************************************** 873 ************************************************************** 874 * FUNCIONES DEL CLIENTE JAVASCRIPT * 875 ************************************************************** 876 ************************************************************** 877 ************************************************************** 878 **************************************************************/ 879 880 /** 881 * Establece el objeto que simula ser el Applet de firma en sistemas en los que no se 882 * soportan los applets. Se usara comunicacion mediante socket cuando: 883 * - El sistema operativo no sea Android. 884 * - El sistema operativo no sea iOS. 885 * - El navegador web no sea Intenet Explorer 10 o inferior (o 11 con modo de compatibilidad) 886 * - No se fuerce el uso del servidor intermedio. 887 * En caso contrario, la comunicacion se realizara mediante un servidor intermedio. 888 */ 889 function cargarAppAfirma(clientAddress, keystore) { 890 891 if (!keystore) { 892 keystore = getDefaultKeystore(); 893 } 894 895 if (!isIOS() && !isAndroid() && !isOldInternetExplorer() && !isEdge() && !forceWSMode){ 896 clienteFirma = new AppAfirmaJSSocket(clientAddress, window, undefined); 897 clienteFirma.setKeyStore(keystore); 898 clientType = TYPE_JAVASCRIPT_SOCKET; 899 } 900 else { 901 clienteFirma = new AppAfirmaJSWebService(clientAddress, window, undefined); 902 clienteFirma.setKeyStore(keystore); 903 clientType = TYPE_JAVASCRIPT_WEB_SERVICE; 904 } 905 906 } 907 908 var AppAfirmaJSSocket = ( function (clientAddress, window, undefined) { 909 910 var UnsupportedOperationException = "java.lang.UnsupportedOperationException"; 911 912 /** 913 * Atributos para la configuracion del objeto sustituto del applet Java de firma 914 */ 915 var errorMessage = ''; 916 var errorType = ''; 917 918 /** Puerto actual */ 919 var port = ""; 920 921 var idSession; 922 923 var PROTOCOL_VERSION = 1; 924 925 /* Tiempo de retardo para peticiones */ 926 var WAITING_TIME = 500; 927 928 var URL_REQUEST = "https://127.0.0.1:"; 929 930 /* Respuesta del socket a la peticion realizada */ 931 var totalResponseRequest = ""; 932 933 /* Numero de fragmentos recibidos del socket */ 934 var recibidos = 0; 935 936 /* Variable de control para la operacion seleccion de certificado */ 937 var isSelectCertOperation = false; 938 939 /* Variable de control para la operacion guardar */ 940 var isSaveOperation = false; 941 942 /* Variable de control para la operacion de ejecutar operacion criptografica y guardar */ 943 var isOpAndSaveOperation = false; 944 945 /* Variable de control para la operacion batch */ 946 var isBatchOperation = false; 947 948 /* URL de la peticion HTTPS */ 949 var urlHttpRequest = ""; 950 951 /* Maxima longitud permitida para una URL. Si la url se excede se divide 952 * la peticion en fragmentos. Con Internet Explorer y Firefox a partir de 953 * la version 49 se producen corrupciones de datos con el valor original 954 * de 1Mb, asi que se reduce este a un valor seguro para ese navegador. */ 955 var URL_MAX_SIZE = isInternetExplorer() ? 12000 : isFirefox() ? 458752 : 1048576; 956 957 /* Indica si se ha establecido la conexion o no */ 958 var connection = false; 959 960 /* Dominio desde el que se realiza la llamada al servicio */ 961 var baseUri = clientAddress; 962 963 /* Almacen de claves cargado por defecto */ 964 var defaultKeyStore = null; 965 966 /* Funcion callback que se lanza al obtener un resultado */ 967 var successCallback = null; 968 969 /* Funcion error callback */ 970 var errorCallback = null; 971 972 /* Mayor entero */ 973 var MAX_NUMBER = 2147483648; 974 975 /* Caracteres validos para los ID de sesion */ 976 var VALID_CHARS_TO_ID = "1234567890abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 977 978 /* Genera un identificador de sesion. */ 979 function generateNewIdSession () { 980 var ID_LENGTH = 20; 981 var random = ""; 982 var randomInts; 983 if (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") { 984 randomInts = new Uint32Array(ID_LENGTH); 985 window.crypto.getRandomValues(randomInts); 986 } 987 else { 988 randomInts = new Array(ID_LENGTH); 989 for (var i = 0; i < ID_LENGTH; i++) { 990 randomInts[i] = rnd() * MAX_NUMBER; 991 } 992 } 993 994 for (var i = 0; i < ID_LENGTH; i++) { 995 random += VALID_CHARS_TO_ID.charAt(Math.floor(randomInts[i] % VALID_CHARS_TO_ID.length)); 996 } 997 998 return random; 999 } 1000 1001 /** 1002 * Genera numeros aleatorios con una distribucion homogenea. 1003 */ 1004 var seed; 1005 function rnd () { 1006 if (seed == undefined) { 1007 seed = new Date().getMilliseconds() * 1000 * Math.random(); 1008 } 1009 seed = (seed * 9301 + 49297) % 233280; 1010 return seed / 233280; 1011 } 1012 1013 /** 1014 * Establece el almacen de certificados de que se debe utilizar por defecto. 1015 */ 1016 function setKeyStore (keystore) { 1017 defaultKeyStore = keystore; 1018 } 1019 1020 /** 1021 * Inicia el proceso de seleccion de certificado. 1022 */ 1023 function selectCertificate (extraParams, successCallbackFunction, errorCallbackFunction) { 1024 successCallback = successCallbackFunction; 1025 errorCallback = errorCallbackFunction; 1026 selectCertByService(extraParams); 1027 } 1028 1029 function selectCertByService (extraParams) { 1030 1031 var data = new Object(); 1032 data.op = generateDataKeyValue ("op", "selectcert"); 1033 data.properties = generateDataKeyValue ("properties", extraParams != null ? Base64.encode(extraParams) : null); 1034 data.keystore = generateDataKeyValue ("keystore", defaultKeyStore != null ? defaultKeyStore : null); 1035 data.ksb64 = generateDataKeyValue ("ksb64", defaultKeyStore != null ? Base64.encode(defaultKeyStore) : null); 1036 1037 execAppIntent(buildUrl(data)); 1038 } 1039 1040 /** 1041 * Inicia el proceso de firma electronica. 1042 * Implementada en el applet Java de firma 1043 */ 1044 function sign (dataB64, algorithm, format, extraParams, successCallbackFunction, errorCallbackFunction) { 1045 successCallback = successCallbackFunction; 1046 errorCallback = errorCallbackFunction; 1047 signByService("sign", dataB64, algorithm, format, extraParams); 1048 } 1049 1050 /** 1051 * Inicia el proceso de cofirma de una firma electrónica. 1052 * Implementada en el applet Java de firma. 1053 */ 1054 function coSign (signB64, dataB64, algorithm, format, extraParams, successCallbackFunction, errorCallbackFunction) { 1055 successCallback = successCallbackFunction; 1056 errorCallback = errorCallbackFunction; 1057 signByService("cosign", signB64, algorithm, format, extraParams); 1058 } 1059 1060 /** 1061 * Inicia el proceso de contrafirma de una firma electrónica. 1062 * Implementada en el applet Java de firma. 1063 */ 1064 function counterSign (signB64, algorithm, format, extraParams, successCallbackFunction, errorCallbackFunction) { 1065 successCallback = successCallbackFunction; 1066 errorCallback = errorCallbackFunction; 1067 signByService("countersign", signB64, algorithm, format, extraParams); 1068 } 1069 1070 /** 1071 * Realiza una operacion de firma/multifirma comunicandose con la aplicacion nativa por socket. 1072 * @param signId Identificador de la operacion a realizar (sign, cosign y countersign). 1073 * @param dataB64 Datos o firma en base 64. 1074 * @param algorithm Algoritmo de firma. 1075 * @param format Formato de firma. 1076 * @param extraParams Parámetros para la configuració
1076n de la operación. 1077 */ 1078 function signByService (signId, dataB64, algorithm, format, extraParams) { 1079 1080 if (dataB64 == undefined || dataB64 == "") { 1081 dataB64 = null; 1082 } 1083 1084 if (dataB64 != null && !isValidUrl(dataB64)) { 1085 dataB64 = dataB64.replace(/\+/g, "-").replace(/\//g, "_"); 1086 } 1087 1088 var data = generateDataToSign(signId, algorithm, format, extraParams, dataB64, defaultKeyStore); 1089 1090 execAppIntent(buildUrl(data)); 1091 } 1092 1093 /** 1094 * Inicia el proceso de firma por lotes. 1095 */ 1096 function signAndSaveToFile (opId, dataB64, algorithm, format, extraParams, outputFileName, successCallbackFunction, errorCallbackFunction) { 1097 successCallback = successCallbackFunction; 1098 errorCallback = errorCallbackFunction; 1099 signAndSaveFileByService(opId, dataB64, algorithm, format, extraParams, outputFileName); 1100 } 1101 1102 /** 1103 * Realiza una operacion de firma/multifirma seguida del guardado del resultado comunicandose 1104 * con la aplicacion nativa por socket. 1105 * @param signId Identificador de la operacion a realizar (sign, cosign y countersign). 1106 * @param dataB64 Datos o firma en base 64. 1107 * @param algorithm Algoritmo de firma. 1108 * @param format Formato de firma. 1109 * @param extraParams Parametros para la configuració
1109n de la operación. 1110 */ 1111 function signAndSaveFileByService (signId, dataB64, algorithm, format, extraParams, outputFileName) { 1112 1113 if (dataB64 == undefined || dataB64 == "") { 1114 dataB64 = null; 1115 } 1116 1117 if (dataB64 != null && !isValidUrl(dataB64)) { 1118 dataB64 = dataB64.replace(/\+/g, "-").replace(/\//g, "_"); 1119 } 1120 1121 var data = generateDataToSignAndSave(signId, algorithm, format, extraParams, outputFileName, dataB64, defaultKeyStore); 1122 1123 execAppIntent(buildUrl(data)); 1124 } 1125 1126 /** 1127 * Inicia el proceso de firma por lotes. 1128 */ 1129 function signBatch (batchB64, batchPreSignerUrl, batchPostSignerUrl, extraParams, successCallbackFunction, errorCallbackFunction) { 1130 successCallback = successCallbackFunction; 1131 errorCallback = errorCallbackFunction; 1132 signBatchByService(batchB64, batchPreSignerUrl, batchPostSignerUrl, extraParams); 1133 } 1134 1135 function signBatchByService (batchB64, batchPreSignerUrl, batchPostSignerUrl, extraParams) { 1136 1137 if (batchB64 == undefined || batchB64 == "") { 1138 batchB64 = null; 1139 } 1140 1141 if (batchB64 != null) { 1142 batchB64 = batchB64.replace(/\+/g, "-").replace(/\//g, "_"); 1143 } 1144 1145 var data = generateDataToBatch(defaultKeyStore, batchPreSignerUrl, batchPostSignerUrl, extraParams, batchB64); 1146 execAppIntent(buildUrl(data)); 1147 } 1148 1149 /** 1150 * Genera el objeto con los datos de la transaccion para la firma 1151 */ 1152 function generateDataToBatch(keystore, batchPreSignerUrl, batchPostSignerUrl, extraParams, batchB64) { 1153 var data = new Object(); 1154 data.op = generateDataKeyValue("op","batch"); 1155 data.keystore = generateDataKeyValue("keystore", keystore != null ? keystore : null); 1156 data.ksb64 = generateDataKeyValue ("ksb64", keystore != null ? Base64.encode(keystore) : null); 1157 data.batchpresignerurl = generateDataKeyValue("batchpresignerurl", batchPreSignerUrl); 1158 data.batchpostsignerurl = generateDataKeyValue("batchpostsignerurl", batchPostSignerUrl); 1159 data.properties = generateDataKeyValue ("properties", extraParams != null ? Base64.encode(extraParams) : null); 1160 data.dat = generateDataKeyValue ("dat", batchB64 == "" ? null : batchB64); 1161 1162 return data; 1163 } 1164 1165 /** 1166 * Construye una URL para la invocación del Cliente @firma nativo. 1167 * params: Par\u00E1metros para la configuraci\u00F3n de la operaci\u00F3n. 1168 */ 1169 function buildUrl (arr) { 1170 1171 // Operacion seleccionada 1172 var intentURL; 1173 var params = []; 1174 // Convertimos el objeto con los datos en un array del tipo key value 1175 for(var x in arr){ 1176 params.push(arr[x]); 1177 } 1178 1179 if (params != null && params != undefined && params.length > 0) { 1180 intentURL = 'afirma://' + encodeURIComponent(arr.op.value) + '?'; // Agregamos como dominio el identificador del tipo de operacion 1181 for (var i = 0; i < params.length; i++) { 1182 if (params[i].value != null && params[i].value != "null") { 1183 intentURL += (i != 0 ? '&' : '') + params[i].key + '=' + encodeURIComponent(params[i].value); 1184 } 1185 } 1186 } 1187 return intentURL; 1188 } 1189 1190 function execAppIntent (url) { 1191 1192 // Primera ejecucion, no hay puerto definido 1193 if (port == "") { 1194 // Calculamos los puertos 1195 var ports = getRandomPorts(); 1196 // Invocamos a la aplicacion nativa 1197 openNativeApp(ports); 1198 // Enviamos la peticion a la app despues de esperar un tiempo prudencial 1199 setTimeout(executeEchoByServiceByPort, MiniApplet.AUTOFIRMA_LAUNCHING_TIME, ports, url); 1200 } 1201 // Se ha ejecutado anteriormente y tenemos un puerto calculado. 1202 else { 1203 connection = false; 1204 executeEchoByService (port, url, MiniApplet.AUTOFIRMA_CONNECTION_RETRIES) 1205 } 1206 } 1207 1208 /** 1209 * Obtiene un puerto aleatorio para la comunicaci\u00F3n con la aplicaci\u00F3n nativa. 1210 */ 1211 function getRandomPorts () { 1212 var MIN_PORT = 49152; 1213 var MAX_PORT = 65535; 1214 var ports = new Array(); 1215 ports[0] = Math.floor((Math.random() * (MAX_PORT - MIN_PORT))) + MIN_PORT; 1216 ports[1] = Math.floor((Math.random() * (MAX_PORT - MIN_PORT))) + MIN_PORT; 1217 ports[2] = Math.floor((Math.random() * (MAX_PORT - MIN_PORT))) + MIN_PORT; 1218 return ports; 1219 } 1220 1221 1222 function openNativeApp (ports) { 1223 1224 var portsLine = ""; 1225 for (var i = 0; i < ports.length; i++) { 1226 portsLine += ports[i];
1227 if (i < (ports.length - 1)) { 1228 portsLine += ","; 1229 } 1230 } 1231 idSession = generateNewIdSession(); 1232 openUrl("afirma://service?ports=" + portsLine + "&v=" + PROTOCOL_VERSION + "&idsession=" + idSession); 1233 } 1234 1235 /** 1236 * Llama a la aplicacion de firma a traves de la URL de invocacion sin que afecte 1237 * a la pagina que se esta mostrando. 1238 * @param url URL de invocacion. 1239 */ 1240 function openUrl (url) { 1241 1242 // Usamos document.location porque tiene mejor soporte por los navegadores que 1243 // window.location que es el mecanismo estandar 1244 if (isChrome()) { 1245 document.location = url; 1246 } 1247 else { 1248 1249 // Si ya existe el iframe, lo eliminamos para despues volverlo a crear 1250 if (document.getElementById("iframeAfirma") != null) { 1251 try { 1252 var element = document.getElementById("iframeAfirma"); 1253 element.outerHTML = ""; 1254 delete element; 1255 } 1256 catch (e) { 1257 // No hacemos nada 1258 } 1259 } 1260 1261 var iframeElem = document.createElement("iframe"); 1262 1263 var idAttr = document.createAttribute("id"); 1264 idAttr.value = "iframeAfirma"; 1265 iframeElem.setAttributeNode(idAttr); 1266 1267 var srcAttr = document.createAttribute("src"); 1268 srcAttr.value = url; 1269 iframeElem.setAttributeNode(srcAttr); 1270 1271 var heightAttr = document.createAttribute("height"); 1272 heightAttr.value = 1; 1273 iframeElem.setAttributeNode(heightAttr); 1274 1275 var widthAttr = document.createAttribute("width"); 1276 widthAttr.value = 1; 1277 iframeElem.setAttributeNode(widthAttr); 1278 1279 var styleAttr = document.createAttribute("style"); 1280 styleAttr.value = "display: none;"; 1281 iframeElem.setAttributeNode(styleAttr); 1282 1283 document.body.appendChild(iframeElem); 1284 } 1285 } 1286 1287 /** 1288 * Genera el objeto con los datos de la transaccion para la operacion 1289 * de firma/multifirma 1290 */ 1291 function generateDataToSign(signId, algorithm, format, extraParams, dataB64, keystore) { 1292 var data = new Object(); 1293 data.op = generateDataKeyValue("op", signId); 1294 data.keystore = generateDataKeyValue ("keystore", keystore != null ? keystore : null); 1295 data.ksb64 = generateDataKeyValue ("ksb64", keystore != null ? Base64.encode(keystore) : null); 1296 data.algorithm = generateDataKeyValue ("algorithm", algorithm); 1297 data.format = generateDataKeyValue ("format", format); 1298 data.properties = generateDataKeyValue ("properties", extraParams != null ? Base64.encode(extraParams) : null); 1299 data.dat = generateDataKeyValue ("dat", dataB64 == "" ? null : dataB64); 1300 return data; 1301 } 1302 1303 /** 1304 * Genera el objeto con los datos de la transaccion para la operacion 1305 * de firma/multifirma seguida del guardado del resultado 1306 */ 1307 function generateDataToSignAndSave(signId, algorithm, format, extraParams, outputFileName, dataB64, keystore) { 1308 var data = new Object(); 1309 data.op = generateDataKeyValue("op", "signandsave"); 1310 data.cryptoOp = generateDataKeyValue("cop", signId); 1311 data.keystore = generateDataKeyValue ("keystore", keystore != null ? keystore : null); 1312 data.ksb64 = generateDataKeyValue ("ksb64", keystore != null ? Base64.encode(keystore) : null); 1313 data.algorithm = generateDataKeyValue ("algorithm", algorithm); 1314 data.format = generateDataKeyValue ("format", format); 1315 data.properties = generateDataKeyValue ("properties", extraParams != null ? Base64.encode(extraParams) : null); 1316 data.filename = generateDataKeyValue ("filename", outputFileName); 1317 data.dat = generateDataKeyValue ("dat", dataB64 == "" ? null : dataB64); 1318 return data; 1319 } 1320 1321 function executeEchoByServiceByPort (ports, url) { 1322 connection = false; 1323 var semaphore = new Object(); 1324 semaphore.locked = false; 1325 executeEchoByService (ports[0], url, MiniApplet.AUTOFIRMA_CONNECTION_RETRIES, semaphore); 1326 executeEchoByService (ports[1], url, MiniApplet.AUTOFIRMA_CONNECTION_RETRIES, semaphore); 1327 executeEchoByService (ports[2], url, MiniApplet.AUTOFIRMA_CONNECTION_RETRIES, semaphore); 1328 } 1329 1330 /** 1331 * Intenta conectar con la aplicación nativa mandando una peticion echo al puerto. 1332 * Si la aplicación responde lanzamos la ejecucion del servicio. 1333 * Si la aplicación no responde volvemos a lanzar cada 2 segundos otra peticion echo hasta que una 1334 * peticion sea aceptada. 1335 */ 1336 function executeEchoByService (currentPort, url, timeoutResetCounter, semaphore) { 1337 var httpRequest = getHttpRequest(); 1338 httpRequest.open("POST", URL_REQUEST + currentPort + "/afirma", true); 1339 httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 1340 httpRequest.onreadystatechange = function() { 1341 if (httpRequest.readyState == 4 && httpRequest.status == 200 && Base64.decode(httpRequest.responseText, true) == "OK" && !connection) { 1342 //console.log("puerto asignado puerto:" + currentPort); 1343 port = currentPort; 1344 urlHttpRequest = URL_REQUEST + port + "/afirma"; 1345 connection = true; 1346 if (semaphore) { 1347 semaphore.locked = true; 1348 } 1349 // Comprobamos si es una operacion de seleccion de certificado 1350 isSelectCertOperation = url.indexOf("afirma://selectcert") > -1; 1351 // Comprobamos si es una operacion de guardado. 1352 isSaveOperation = url.indexOf("afirma://save") > -1; 1353 // Comprobamos si es una operacion de firma por lotes 1354 isBatchOperation = url.indexOf("afirma://batch") > -1; 1355 // Comprobamos si es una operacion criptografica mas guardado del resultado 1356 isOpAndSaveOperation = url.indexOf("afirma://signandsave") > -1; 1357 executeOperationByService(url); 1358 } 1359 else if ((!semaphore || !semaphore.locked) && !connection && httpRequest.readyState != 2 && httpRequest.readyState != 3) { 1360 timeoutResetCounter--; 1361 //console.log("Quedan " + timeoutResetCounter + " peticiones a " + currentPort) 1362 1363 // Si ya se conecto antes con la aplicacion pero ahora llevamos la mitad de los intentos 1364 // sin conectar, consideramos que se ha tumbado y hay que relanzarla 1365 if (port != "" && timeoutResetCounter < MiniApplet.AUTOFIRMA_CONNECTION_RETRIES/2) { 1366 port = ""; 1367 if (semaphore) { 1368 semaphore.locked = true; 1369 } 1370 timeoutResetCounter = MiniApplet.AUTOFIRMA_CONNECTION_RETRIES; 1371 execAppIntent(url); 1372 } 1373 // Si hemos agotado todos los reintentos consideramos que la aplicacion no esta instalada 1374 else if (timeoutResetCounter == 0) { 1375 if (!semaphore || !semaphore.locked) { 1376 if (semaphore) { 1377 semaphore.locked = true; 1378 } 1379 errorCallback("es.gob.afirma.standalone.ApplicationNotFoundException", "No se ha podido conectar con AutoFirma."); 1380 } 1381 return; 1382 } 1383 // Aun quedan reintentos 1384 else { 1385 setTimeout(executeEchoByService, MiniApplet.AUTOFIRMA_LAUNCHING_TIME, currentPort, url, timeoutResetCounter, semaphore); 1386 } 1387 } 1388 } 1389 1390 if (!connection) { 1391 // Mandamos un echo con - por lo que las variables de control se resetearan 1392 // Se anade EOF para que cuando el socket SSL lea la peticion del buffer sepa 1393 // que ha llegado al final y no se quede en espera 1394 httpRequest.send("echo=-idsession=" + idSession + "@EOF"); 1395 //console.log("probamos puerto " +currentPort) 1396 } 1397 } 1398 1399 /** 1400 * Comprueba si hay que dividir los datos que se se mandan a la a
1400plicacion nativa. 1401 * Si hay que dividirlos se llama a la funcion executeOperationRecursive. 1402 * Si cabe en un solo envio se manda directamente. 1403 */ 1404 function executeOperationByService (url) { 1405 1406 // Si el envio se debe fragmentar, llamamos a una función que se encarga 1407 // de mandar la peticion recursivamente 1408 if (url.length > URL_MAX_SIZE) { 1409 executeOperationRecursive(url, 1, Math.ceil(url.length/URL_MAX_SIZE)); 1410 return; 1411 } 1412 1413 var httpRequest = getHttpRequest(); 1414 httpRequest.open("POST", urlHttpRequest, true); 1415 httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 1416 1417 // El envio no se fragmenta 1418 httpRequest.onreadystatechange = function() { 1419 if (httpRequest.status == 404) { 1420 errorServiceResponseFunction("java.lang.IOException", httpRequest.responseText); 1421 } 1422 // Las operaciones que no requieren respuesta, llaman directamente a la funcion de exito 1423 if (isSaveOperation) { 1424 if (httpRequest.readyState == 4 && Base64.decode(httpRequest.responseText) != "") { 1425 successServiceResponseFunction(Base64.decode(httpRequest.responseText)); 1426 } 1427 return; 1428 } 1429 // El resto de operaciones deben componer el resultado 1430 else { 1431 if (httpRequest.readyState == 4 && httpRequest.status == 200 && httpRequest.responseText != "") { 1432 if (Base64.decode(httpRequest.responseText) == "MEMORY_ERROR"){ 1433 errorServiceResponseFunction("java.lang.OutOfMemoryError", "Problema de memoria en servidor"); 1434 return; 1435 } 1436 // Juntamos los fragmentos 1437 totalResponseRequest = ""; 1438 addFragmentRequest (1, Base64.decode(httpRequest.responseText, true)); 1439 } 1440 // Volvemos a mandar la peticion si no manda texto en la respuesta y la peticion esta en estado ready 1441 else if (httpRequest.responseText == "" && httpRequest.status == 0 && httpRequest.readyState == 0) { 1442 setTimeout(executeOperationByService, WAITING_TIME, url); 1443 } 1444 } 1445 } 1446 httpRequest.onerror = function(e) { 1447 // status error 0 es que no se ha podido comunicar con la aplicacion 1448 if (e.target.status == 0) { 1449 errorServiceResponseFunction("java.lang.IOException", "Se ha perdido la conexión con la aplicación @firma "+e.target.statusText); 1450 } 1451 // error desconocido 1452 else { 1453 errorServiceResponseFunction("java.lang.IOException", "Ocurrio un error de red en la llamada al servicio de firma "+e.target.statusText); 1454 } 1455 } 1456 // se anade EOF para que cuando el socket SSL lea la peticion del buffer sepa que ha llegado al final y no se quede en espera 1457 httpRequest.send("cmd=" + Base64.encode(url, true) + "idsession=" + idSession + "@EOF"); 1458 } 1459 1460 /** 1461 * Manda los datos a la aplicación nativa en varios fragmentos porque ha habido que dividir los datos. 1462 * Se va mandando cada petición cuando se reciba la anterior. 1463 */ 1464 function executeOperationRecursive (url, i, iFinal) { 1465 1466 var httpRequest = getHttpRequest(); 1467 httpRequest.open("POST", urlHttpRequest, true); 1468 httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 1469 var urlToSend = url.substring(((i-1) * URL_MAX_SIZE), Math.min(URL_MAX_SIZE * i, url.length)); 1470 httpRequest.onreadystatechange = function (evt) { 1471 if (httpRequest.status == 404) { 1472 errorServiceResponseFunction("java.lang.Exception", httpRequest.responseText); 1473 return; 1474 } 1475 // Respuesta afirmativa, hay que mandar mas fragmentos 1476 if (httpRequest.readyState == 4 && httpRequest.status == 200 && httpRequest.responseText != "" ) { 1477 recibidos++; 1478 1479 // Faltan mas peticiones por enviar 1480 if (Base64.decode(httpRequest.responseText, true) == "MORE_DATA_NEED") { 1481 if (recibidos < iFinal ){ 1482 //console.log("recibido el fragmento "+recibidos + "de "+iFinal) 1483 executeOperationRecursive(url, i+1, iFinal); 1484 } 1485 } 1486 // Todas las peticiones se han recibido, hay que mandar operacion firma 1487 // Respuesta es OK 1488 else if (Base64.decode(httpRequest.responseText, true) == "OK") { 1489 if(recibidos == iFinal){ 1490 //console.log("recibido todo, realizamos la operacion"); 1491 recibidos = 0; 1492 doFirm(); 1493 } 1494 } 1495 else if(Base64.decode(httpRequest.responseText) == "MEMORY_ERROR"){
1496 successServiceResponseFunction(Base64.decode(httpRequest.responseText)); 1497 } 1498 } 1499 else if (httpRequest.responseText == "" && httpRequest.status == 0 && httpRequest.readyState == 0) { 1500 setTimeout(executeOperationRecursive, WAITING_TIME, url, i, iFinal); 1501 } 1502 } 1503 httpRequest.onerror = function(e) { 1504 // Status error 0 es que no se ha podido comunicar con la aplicacion 1505 if (e.target.status == 0){ 1506 errorServiceResponseFunction("java.lang.IOException", "Se ha perdido la conexión con la aplicación @firma "+e.target.statusText); 1507 } 1508 // Error desconocido 1509 else{ 1510 errorServiceResponseFunction("java.lang.IOException", "Ocurrio un error de red en la llamada al servicio de firma "+e.target.statusText); 1511 } 1512 } 1513 // Se anade EOF para que cuando el socket SSL lea la peticion del buffer sepa que ha llegado al final y no se quede en espera 1514 httpRequest.send("fragment=@" + i + "@" + iFinal + "@" + Base64.encode(urlToSend, true) + "idsession=" + idSession +"@EOF"); 1515 //console.log("mandado parte "+i+" de"+iFinal); 1516 1517 } 1518 1519 /** 1520 * Realiza una operacion que se ha mandando en varios fragmentos. 1521 */ 1522 function doFirm () { 1523 httpRequest = getHttpRequest(); 1524 httpRequest.open("POST", urlHttpRequest, true); 1525 httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 1526 httpRequest.onreadystatechange = function() { 1527 1528 if (httpRequest.status == 404) { 1529 errorServiceResponseFunction("java.lang.Exception", httpRequest.responseText); 1530 } 1531 1532 // Se ha realizado la operacion save, no controlamos reintentos ni el exito de la peticion 1533 // porque no requiere respuesta 1534 if (isSaveOperation) { 1535 if (httpRequest.readyState == 4 && Base64.decode(httpRequest.responseText) != "") { 1536 successServiceResponseFunction(Base64.decode(httpRequest.responseText)); 1537 } 1538 return; 1539 } 1540 else { 1541 if (httpRequest.readyState == 4 && httpRequest.status == 200 && httpRequest.responseText != "") { 1542 if(Base64.decode(httpRequest.responseText) == "MEMORY_ERROR"){ 1543 successServiceResponseFunction(Base64.decode(httpRequest.responseText)); 1544 } 1545 totalResponseRequest = ""; 1546 addFragmentRequest (1, Base64.decode(httpRequest.responseText, true)); 1547 } 1548 // No recibimos la respuesta, volvemos a llamar. 1549 else { 1550 if (httpRequest.status == 0 && httpRequest.readyState == 0 ) { 1551 setTimeout(doFirm, WAITING_TIME); 1552 } 1553 } 1554 } 1555 } 1556 httpRequest.onerror = function(e) { 1557 // status error 0 es que no se ha podido comunicar con la aplicacion 1558 if (e.target.status == 0){ 1559 errorServiceResponseFunction("java.lang.IOException", "Se ha perdido la conexión con la aplicación @firma "+e.target.statusText); 1560 } 1561 // error desconocido 1562 else{ 1563 errorServiceResponseFunction("java.lang.IOException", "Ocurrio un error de red en la llamada al servicio de firma "+e.target.statusText); 1564 } 1565 } 1566 httpRequest.send("firm=idsession=" + idSession +"@EOF"); 1567 } 1568 1569 /** 1570 * Se encarga de solicitar y montar la respuesta de la operacion realizada. 1571 */ 1572 function addFragmentRequest (part, totalParts){ 1573 httpRequest = getHttpRequest(); 1574 httpRequest.open("POST", urlHttpRequest, true); 1575 httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 1576 httpRequest.onreadystatechange = function() { 1577 if (httpRequest.readyState == 4 && httpRequest.status == 200 && httpRequest.responseText != "") { 1578 if(Base64.decode(httpRequest.responseText) == "MEMORY_ERROR"){ 1579 errorServiceResponseFunction("java.lang.OutOfMemoryError", "Problema de memoria en servidor"); 1580 return; 1581 } 1582 //console.log("recibida la parte " + part); 1583 totalResponseRequest += Base64.decode(httpRequest.responseText, true); 1584 // Si estan todas las partes llamamos al successcallback 1585 if (part == totalParts) { 1586 // Es una operacion de firma por lotes y tiene un callback propio 1587 if (isBatchOperation) {
1588 successBatchResponseFunction(totalResponseRequest); 1589 isBatchOperation = false; 1590 } 1591 // Es una operacion de seleccion de certificados y tiene un callback propio 1592 else if (isSelectCertOperation) { 1593 successSelectCertServiceResponseFunction(totalResponseRequest); 1594 isSelectCertOperation = false; 1595 } 1596 // Es cualquier otra operacion que requiere respuesta 1597 else { 1598 successServiceResponseFunction(totalResponseRequest); 1599 } 1600 totalResponseRequest = ""; 1601 } 1602 else { 1603 addFragmentRequest(part + 1, totalParts); 1604 } 1605 } 1606 else { 1607 if (httpRequest.responseText == "" && httpRequest.status == 0 && httpRequest.readyState == 0){ 1608 setTimeout(addFragmentRequest, WAITING_TIME, part, totalParts); 1609 } 1610 } 1611 } 1612 httpRequest.onerror = function(e) { 1613 // status error 0 es que no se ha podido comunicar con la aplicacion 1614 if (e.target.status == 0){ 1615 errorServiceResponseFunction("java.lang.IOException", "Se ha perdido la conexión con la aplicación @firma "+e.target.statusText); 1616 } 1617 // error desconocido 1618 else{ 1619 errorServiceResponseFunction("java.lang.IOException", "Ocurrio un error de red en la llamada al servicio de firma "+e.target.statusText); 1620 } 1621 } 1622 if (part <= totalParts){ 1623 // se anade EOF para que cuando el socket SSL lea la peticion del buffer sepa que ha llegado al final y no se quede en espera 1624 httpRequest.send("send=@"+part+"@"+totalParts+"idsession=" + idSession +"@EOF"); 1625 //console.log("solicitarmos la parte "+part+" de "+ totalParts) 1626 } 1627 } 1628 1629 /** 1630 * Lee el resultado devuelto por el servicio, 'CANCEL' o empieza por 'SAF-', ejecutara el metodo 1631 * de error, si es 'OK' o cualquier otra cosa (que se intepretara como el resultado en base 64) 1632 * se ejecutara el metodo de exito. En este ultimo caso, se descifrara el resultado. 1633 * @param data Resultado obtenido. 1634 */ 1635 function successSelectCertServiceResponseFunction (data) { 1636 // No se ha obtenido respuesta o se notifica la cancelacion 1637 if (data == undefined || data == null || data == "CANCEL") { 1638 errorCallback("es.gob.afirma.core.AOCancelledOperationException", "Operacion cancelada por el usuario"); 1639 return; 1640 } 1641 1642 // Se ha producido un error 1643 if (data.length > 4 && data.substr(0, 4) == "SAF_") { 1644 errorCallback("java.lang.Exception", data); 1645 return; 1646 } 1647 1648 // Se ha producido un error y no se ha identificado el tipo 1649 if (data == "NULL") { 1650 errorCallback("java.lang.Exception", "Error desconocido"); 1651 return; 1652 } 1653 1654 successCallback(data.replace(/\-/g, "+").replace(/\_/g, "/")); 1655 } 1656 1657 /** 1658 * Lee el resultado devuelto por el servicio, 'CANCEL' o empieza por 'SAF-', ejecutara el metodo 1659 * de error, si es 'OK' o cualquier otra cosa (que se intepretara como el resultado en base 64) 1660 * se ejecutara el metodo de exito. En este ultimo caso, se descifrara el resultado. 1661 * @param data Resultado obtenido. 1662 */ 1663 function successServiceResponseFunction (data) { 1664 // No se ha obtenido respuesta o se notifica la cancelacion 1665 if (data == undefined || data == null || data == "CANCEL") { 1666 errorCallback("es.gob.afirma.core.AOCancelledOperationException", "Operacion cancelada por el usuario"); 1667 return; 1668 } 1669 1670 // Termina bien y no devuelve ningun resultado o es una operacion guardado 1671 if (data == "OK") { 1672 successCallback(data, null); 1673 return; 1674 } 1675 1676 // Error de memoria 1677 if (data == "MEMORY_ERROR") { 1678 errorCallback("es.gob.afirma.core.OutOfMemoryError", "El fichero que se pretende firmar o guardar excede de la memoria disponible para aplicacion"); 1679 return; 1680 } 1681 1682 // Se ha producido un error 1683 if (data.length > 4 && data.substr(0, 4) == "SAF_") { 1684 errorCallback("java.lang.Exception", data); 1685 return; 1686 } 1687 1688 // Se ha producido un error y no se ha identificado el tipo 1689 if (data == "NULL") { 1690 errorCallback("java.lang.Exception", "Error desconocido"); 1691 return; 1692 } 1693 1694 // Operacion guardado con exito 1695 if (data == "SAVE_OK") { 1696 if (successCallback) { 1697 successCallback(data); 1698 } 1699 return; 1700 } 1701 1702 // Interpretamos el resultado como un base 64 y el certificado y los datos cifrados 1703 var signature; 1704 var certificate = null; 1705 var sepPos = data.indexOf("|"); 1706 1707 if (sepPos == -1) { 1708 signature = Base64.decode(data, true).replace(/\-/g, "+").replace(/\_/g, "/"); 1709 } 1710 else { 1711 certificate = data.substring(0, sepPos).replace(/\-/g, "+").replace(/\_/g, "/"); 1712 signature = data.substring(sepPos + 1).replace(/\-/g, "+").replace(/\_/g, "/"); 1713 } 1714 successCallback(signature, certificate); 1715 } 1716
1717 function errorServiceResponseFunction(exception, message){ 1718 errorCallback(exception, message); 1719 } 1720 1721 function successBatchResponseFunction (data) { 1722 // No se ha obtenido respuesta o se notifica la cancelacion 1723 if (data == undefined || data == null || data == "CANCEL") { 1724 errorCallback("es.gob.afirma.core.AOCancelledOperationException", "Operacion cancelada por el usuario"); 1725 return; 1726 } 1727 1728 // Se ha producido un error 1729 if (data.length > 4 && data.substr(0, 4) == "SAF_") { 1730 errorCallback("java.lang.Exception", data); 1731 return; 1732 } 1733 1734 // Se ha producido un error y no se ha identificado el tipo 1735 if (data == "NULL") { 1736 errorCallback("java.lang.Exception", "Error desconocido"); 1737 return; 1738 } 1739 successCallback(data); 1740 } 1741 1742 /** 1743 * Inicia el proceso de guardado de una firma. 1744 */ 1745 function saveDataToFile (dataB64, title, filename, extension, description, successCallbackSave, errorCallbackSave) { 1746 successCallback = successCallbackSave; 1747 errorCallback = errorCallbackSave; 1748 saveByService(dataB64, title, filename, extension, description); 1749 } 1750 1751 function saveByService ( dataB64, title, filename, extension, description) { 1752 1753 if (dataB64 == undefined || dataB64 == "") { 1754 dataB64 = null; 1755 } 1756 1757 if (dataB64 != null && !isValidUrl(dataB64)) { 1758 dataB64 = dataB64.replace(/\+/g, "-").replace(/\//g, "_"); 1759 } 1760 1761 var data = generateDataToSave(dataB64, title, filename, extension, description); 1762 1763 execAppIntent(buildUrl(data)); 1764 } 1765 1766 /** 1767 * Genera el objeto de datos para la operacion de guardar 1768 */ 1769 function generateDataToSave(dataB64, title, filename, extension, description) { 1770 var data = new Object(); 1771 data.op = generateDataKeyValue ("op", "save"); 1772 data.title = generateDataKeyValue ("title", title); 1773 data.filename = generateDataKeyValue ("filename", filename); 1774 data.extension = generateDataKeyValue ("extension", extension); 1775 data.description = generateDataKeyValue ("description", description); 1776 data.dat = generateDataKeyValue ("dat", dataB64 == "" ? null : dataB64); 1777 1778 return data; 1779 } 1780 1781 function generateDataKeyValue(key, value) { 1782 var data = new Object(); 1783 data.key = key; 1784 data.value = value; 1785 return data; 1786 } 1787 1788 /** 1789 * Convierte texto plano en texto base 64. 1790 * Implementada en el applet Java de firma. 1791 */ 1792 function getBase64FromText (plainText, charset) { 1793 return plainText != null ? Base64.encode(plainText) : null; 1794 } 1795 1796 /** 1797 * Convierte texto base 64 en texto plano. 1798 * Implementada en el applet Java de firma. 1799 */ 1800 function getTextFromBase64 (base64Text, charset) { 1801 return base64Text != null ? Base64.decode(base64Text) : null; 1802 } 1803 1804 1805 /** 1806 * Carga de un fichero. Operacion no soportada. 1807 * Implementada en el applet Java de firma. 1808 */ 1809 function getFileNameContentBase64 (title, extensions, description) { 1810 throwException(UnsupportedOperationException, "La operacion de carga de ficheros no esta soportada"); 1811 } 1812 1813 /** 1814 * Carga de multiples ficheros. Operacion no soportada. 1815 * Implementada en el applet Java de firma. 1816 */ 1817 function getMultiFileNameContentBase64 (title, extensions, description) { 1818 throwException(UnsupportedOperationException, "La operacion de carga de multiples ficheros no esta soportada"); 1819 } 1820 1821 /** 1822 * Funcion para la comprobacion de existencia del objeto. No hace nada. 1823 * Implementada en el applet Java de firma. 1824 */ 1825 function echo () { 1826 return "Cliente JavaScript"; 1827 } 1828 1829 /** 1830 * No hace nada. 1831 * Implementada en el applet Java de firma. 1832 */ 1833 function setStickySignatory (sticky) { 1834 // No hace nada 1835 } 1836 1837 /** 1838 * Recupera el mensaje de error asociado al ultimo error capturado. 1839 * Implementada en el applet Java de firma. 1840 */ 1841 function getErrorMessage () { 1842 return errorMessage; 1843 } 1844 1845 /** 1846 * Recupera el tipo del ultimo error capturado. 1847 * Implementada en el applet Java de firma. 1848 */ 1849 function getErrorType () { 1850 return errorType; 1851 } 1852 1853 /** 1854 * Recupera el log de la aplicacion. Actualmente, el log solo esta 1855 * disponible en el applet, no en las aplicacion moviles. 1856 */ 1857 function getCurrentLog () { 1858 return "Applet no cargado"; 1859 } 1860 1861 /** 1862 * Funcion para identificar el tipo de objeto del Cliente (javascript, applet,...). 1863 */ 1864 function getType () { 1865 return "javascript"; 1866 } 1867 1868 /** 1869 * Establece el error indicado como error interno y lanza una excepcion. 1870 */ 1871 function throwException (type, message) { 1872 errorType = type; 1873 errorMessage = message; 1874 throw new Error(); 1875 } 1876 1877 /* Metodos que publicamos del objeto AppAfirmaJS */ 1878 return { 1879 echo : echo, 1880 checkTime : checkTime, 1881 setKeyStore : setKeyStore, 1882 sign : sign, 1883 coSign : coSign, 1884 counterSign : counterSign, 1885 signBatch : signBatch, 1886 selectCertificate : selectCertificate, 1887 saveDataToFile : saveDataToFile, 1888 signAndSaveToFile : signAndSaveToFile, 1889 getFileNameContentBase64: getFileNameContentBase64, 1890 getMultiFileNameContentBase64 : getMultiFileNameContentBase64, 1891 getBase64FromText : getBase64FromText, 1892 getTextFromBase64 : getTextFromBase64, 1893 setStickySignatory : setStickySignatory, 1894 setLocale : setLocale, 1895 getErrorMessage : getErrorMessage, 1896 getErrorType : getErrorType, 1897 getCurrentLog : getCurrentLog 1898 } 1899 }); 1900 1901 1902 /** 1903 * Objeto JavaScript que va a reemplazar al cliente de firma en los entornos en los que 1904 * no pueden ejecutarse applets. 1905 */ 1906 var AppAfirmaJSWebService = ( function (clientAddress, window, undefined) { 1907 1908 var UnsupportedOperationException = "java.lang.UnsupportedOperationException"; 1909 1910 /* Longitud maxima de una URL en Android para la invocacion de una aplicacion nativa. */ 1911 var MAX_LONG_ANDROID_URL = 2000; 1912 1913 /* Longitud maxima de una URL en iOS para la invocacion de una aplicacion nativa. */ 1914 var MAX_LONG_IOS_URL = 80000; 1915 1916 /* Longitud maxima de una URL en Windows 8 para la invocacion de una aplicacion nativa. */ 1917 var MAX_LONG_WINDOWS8_URL = 2000; 1918 1919 /* Longitud maxima que generalmente se permite a una URL. */ 1920 var MAX_LONG_GENERAL_URL = 2000; 1921 1922 /** Version del protocolo utilizada. */ 1923 var PROTOCOL_VERSION = 1; 1924 1925 /* Indica si ya se ha detectado que la aplicacion no esta instalada */ 1926 var wrongInstallation = true; 1927 1928 /** 1929 * Atributos para la configuracion del objeto sustituto del applet Java de firma 1930 */ 1931 var errorMessage = ''; 1932 var errorType = ''; 1933 var defaultKeyStore = null; 1934 var retrieverServletAddress = null; 1935 var storageServletAddress = null; 1936 1937 if (clientAddress != undefined && clientAddress != null) { 1938 if (clientAddress.indexOf("://") != -1 && clientAddress.indexOf("/", clientAddress.indexOf("://") + 3) != -1) { 1939 var servletsBase = clientAddress.substring(0, clientAddress.indexOf("/", clientAddress.indexOf("://") + 3)); 1940 retrieverServletAddress = servletsBase + "/afirma-signature-retriever/RetrieveService"; 1941 storageServletAddress = servletsBase + "/afirma-signature-storage/StorageService"; 1942 } else { 1943 retrieverServletAddress = clientAddress + "/afirma-signature-retriever/RetrieveService"; 1944 storageServletAddress = clientAddress + "/afirma-signature-storage/StorageService"; 1945 } 1946 } 1947 1948 /** 1949 * Establece el almacen de certificados de que se debe utilizar por defecto. 1950 */ 1951 function setKeyStore (keystore) { 1952 defaultKeyStore = keystore; 1953 } 1954 1955 /** 1956 * Inicia el proceso de seleccion de certificado. 1957 * Implementada en el applet Java de firma 1958 */ 1959 function selectCertificate (extraParams, successCallback, errorCallback) { 1960 throwException(UnsupportedOperationException, "La operacion de seleccion de certificados no esta soportada"); 1961 } 1962 1963 /** 1964 * Inicia el proceso de firma electronica. 1965 * Implementada en el applet Java de firma 1966 */ 1967 function sign (dataB64, algorithm, format, extraParams, successCallback, errorCallback) { 1968 signOperation("sign", dataB64, algorithm, format, extraParams, successCallback, errorCallback); 1969 } 1970 1971 /** 1972 * Inicia el proceso de cofirma de una firma electrónica. 1973 * Implementada en el applet Java de firma. 1974 */ 1975 function coSign (signB64, dataB64, algorithm, format, extraParams, successCallback, errorCallback) {
1976 signOperation("cosign", signB64, algorithm, format, extraParams, successCallback, errorCallback); 1977 } 1978 1979 /** 1980 * Inicia el proceso de contrafirma de una firma electrónica. 1981 * Implementada en el applet Java de firma. 1982 */ 1983 function counterSign (signB64, algorithm, format, extraParams, successCallback, errorCallback) { 1984 signOperation("countersign", signB64, algorithm, format, extraParams, successCallback, errorCallback); 1985 } 1986 1987 /** 1988 * Realiza una operacion de firma/multifirma. 1989 * @param signId Identificador de la operacion a realizar (sign, cosign y countersign). 1990 * @param dataB64 Datos o firma en base 64. 1991 * @param algorithm Algoritmo de firma. 1992 * @param format Formato de firma. 1993 * @param extraParams Parámetros para la configuración de la operación. 1994 * @param successCallback Método a ejecutar en caso de éxito. 1995 * @param errorCallback Método a ejecutar en caso de error. 1996 */ 1997 function signOperation (signId, dataB64, algorithm, format, extraParams, successCallback, errorCallback) { 1998 1999 if (dataB64 == undefined || dataB64 == "") { 2000 dataB64 = null; 2001 } 2002 2003 if (dataB64 != null && !isValidUrl(dataB64)) { 2004 dataB64 = dataB64.replace(/\+/g, "-").replace(/\//g, "_"); 2005 dataB64 = dataB64.replace(/\n/g, "").replace(/\r/g, ""); //eliminamos saltos de carro para que no generen espacios 0x20 al parsear los atributos del XML enviado/recibido (storageServletAddress y retrieverServletAddress) que impiden la firma en AutoFirma 2006 } 2007 2008 var idSession = generateNewIdSession(); 2009 var cipherKey = generateCipherKey(); 2010 2011 var i = 0; 2012 var params = new Array(); 2013 params[i++] = {key:"ver", value:PROTOCOL_VERSION}; 2014 if (signId != null && signId != undefined) { params[i++] = {key:"op", value:signId}; } 2015 if (idSession != null && idSession != undefined) { params[i++] = {key:"id", value:idSession}; } 2016 if (cipherKey != null && cipherKey != undefined) { params[i++] = {key:"key", value:cipherKey}; } 2017 if (defaultKeyStore != null && 2018 defaultKeyStore != undefined) { params[i++] = {key:"keystore", value:defaultKeyStore}; 2019 params[i++] = {key:"ksb64", value:Base64.encode(defaultKeyStore)}; } 2020 if (storageServletAddress != null && 2021 storageServletAddress != undefined) { params[i++] = {key:"stservlet", value:storageServletAddress}; } 2022 if (format != null && format != undefined) { params[i++] = {key:"format", value:format}; } 2023 if (algorithm != null && algorithm != undefined) { params[i++] = {key:"algorithm", value:algorithm}; } 2024 if (extraParams != null && extraParams != undefined) { params[i++] = {key:"properties", value:Base64.encode(extraParams)}; } 2025 if (dataB64 != null) { params[i++] = {key:"dat", value:dataB64}; } 2026 2027 var url = buildUrl(signId, params); 2028 2029 // Si la URL es muy larga, realizamos un preproceso para que los datos se suban al 2030 // servidor y la aplicacion nativa los descargue, en lugar de pasarlos directamente 2031 if (isURLTooLong(url)) { 2032 if (storageServletAddress == null || storageServletAddress == undefined) { 2033 throwException("java.lang.IllegalArgumentException", "No se ha indicado la direccion del servlet para el guardado de datos"); 2034 return; 2035 } 2036 2037 sendDataAndExecAppIntent(idSession, cipherKey, storageServletAddress, retrieverServletAddress, signId, params, successCallback, errorCallback) 2038 } 2039 else { 2040 execAppIntent(url, idSession, cipherKey, successCallback, errorCallback); 2041 } 2042 } 2043 2044 /** 2045 * Realiza una operacion de firma/multifirma y permite guardar el fichero a disco. 2046 * @param signId Identificador de la operacion a realizar (sign, cosign y countersign). 2047 * @param dataB64 Datos o firma en base 64. 2048 * @param algorithm Algoritmo de firma. 2049 * @param format Formato de firma. 2050 * @param extraParams Parámetros para la configuració
2050n de la operación. 2051 * @param outputFileName Nombre propuesto para el fichero a guardar. 2052 * @param successCallback Método a ejecutar en caso de éxito. 2053 * @param errorCallback Método a ejecutar en caso de error. 2054 */ 2055 function signAndSaveToFile (signId, dataB64, algorithm, format, extraParams, outputFileName, successCallback, errorCallback) { 2056 2057 if (dataB64 == undefined || dataB64 == "") { 2058 dataB64 = null; 2059 } 2060 2061 if (dataB64 != null && !isValidUrl(dataB64)) { 2062 dataB64 = dataB64.replace(/\+/g, "-").replace(/\//g, "_"); 2063 dataB64 = dataB64.replace(/\n/g, "").replace(/\r/g, ""); //eliminamos saltos de carro para que no generen espacios 0x20 al parsear los atributos del XML enviado/recibido (storageServletAddress y retrieverServletAddress) que impiden la firma en AutoFirma 2064 } 2065 2066 var idSession = generateNewIdSession(); 2067 var cipherKey = generateCipherKey(); 2068 2069 var i = 0; 2070 var opId = "signandsave"; 2071 var params = new Array(); 2072 2073 params[i++] = {key:"ver", value:PROTOCOL_VERSION}; 2074 params[i++] = {key:"op", value:opId}; 2075 if (signId != null && signId != undefined) { params[i++] = {key:"cop", value:signId}; } 2076 if (idSession != null && idSession != undefined) { params[i++] = {key:"id", value:idSession}; } 2077 if (cipherKey != null && cipherKey != undefined) { params[i++] = {key:"key", value:cipherKey}; } 2078 if (defaultKeyStore != null && 2079 defaultKeyStore != undefined) { params[i++] = {key:"keystore", value:defaultKeyStore}; 2080 params[i++] = {key:"ksb64", value:Base64.encode(defaultKeyStore)}; } 2081 if (storageServletAddress != null && 2082 storageServletAddress != undefined) { params[i++] = {key:"stservlet", value:storageServletAddress}; } 2083 if (format != null && format != undefined) { params[i++] = {key:"format", value:format}; } 2084 if (algorithm != null && algorithm != undefined) { params[i++] = {key:"algorithm", value:algorithm}; } 2085 if (extraParams != null && extraParams != undefined) { params[i++] = {key:"properties", value:Base64.encode(extraParams)}; } 2086 if (outputFileName != null && 2087 outputFileName != undefined) { params[i++] = {key:"filename", value:outputFileName}; } 2088 if (dataB64 != null) { params[i++] = {key:"dat", value:dataB64}; } 2089 2090 var url = buildUrl(opId, params); 2091 2092 // Si la URL es muy larga, realizamos un preproceso para que los datos se suban al 2093 // servidor y la aplicacion nativa los descargue, en lugar de pasarlos directamente 2094 if (isURLTooLong(url)) { 2095 if (storageServletAddress == null || storageServletAddress == undefined) { 2096 throwException("java.lang.IllegalArgumentException", "No se ha indicado la direccion del servlet para el guardado de datos"); 2097 return; 2098 } 2099 2100 sendDataAndExecAppIntent(idSession, cipherKey, storageServletAddress, retrieverServletAddress, opId, params, successCallback, errorCallback) 2101 } 2102 else { 2103 execAppIntent(url, idSession, cipherKey, successCallback, errorCallback); 2104 } 2105 } 2106 2107 /** 2108 * Ejecuta una operacion de firma de lote. 2109 */ 2110 function signBatch (batchB64, batchPreSignerUrl, batchPostSignerUrl, extraParams, successCallback, errorCallback) { 2111 2112 if (batchB64 == undefined || batchB64 == "") { 2113 batchB64 = null; 2114 } 2115 2116 if (batchB64 != null && !isValidUrl(batchB64)) { 2117 batchB64 = batchB64.replace(/\+/g, "-").replace(/\//g, "_"); 2118 batchB64 = batchB64.replace(/\n/g, "").replace(/\r/g, ""); //eliminamos saltos de carro para que no generen espacios 0x20 al parsear los atributos del XML enviado/recibido (storageServletAddress y retrieverServletAddress) que impiden la firma en AutoFirma 2119 } 2120 2121 var idSession = generateNewIdSession(); 2122 var cipherKey = generateCipherKey(); 2123 2124 var opId = "batch"; 2125 2126 var i = 0; 2127 var params = new Array(); 2128 params[i++] = {key:"ver", value:PROTOCOL_VERSION}; 2129 params[i++] = {key:"op", value:opId}; 2130 if (idSession != null && idSession != undefined) { params[i++] = {key:"id", value:idSession}; } 2131 if (cipherKey != null && cipherKey != undefined) { params[i++] = {key:"key", value:cipherKey}; } 2132 if (defaultKeyStore != null && 2133 defaultKeyStore != undefined) { params[i++] = {key:"keystore", value:defaultKeyStore}; 2134 params[i++] = {key:"ksb64", value:Base64.encode(defaultKeyStore)}; } 2135 if (storageServletAddress != null && 2136 storageServletAddress != undefined) { params[i++] = {key:"stservlet", value:storageServletAddress}; } 2137 if (batchPreSignerUrl != null && 2138 batchPreSignerUrl != undefined) { params[i++] = {key:"batchpresignerurl", value:batchPreSignerUrl}; } 2139 if (batchPostSignerUrl != null && 2140 batchPostSignerUrl != undefined) { params[i++] = {key:"batchpostsignerurl", value:batchPostSignerUrl}; } 2141 if (extraParams != null && extraParams != undefined) { params[i++] = {key:"properties", value:Base64.encode(extraParams)}; } 2142 if (batchB64 != null) { params[i++] = {key:"dat", value:batchB64}; } 2143 2144 var url = buildUrl(opId, params); 2145 2146 // Si la URL es muy larga, realizamos un preproceso para que los datos se suban al 2147 // servidor y la aplicacion nativa los descargue, en lugar de pasarlos directamente 2148 if (isURLTooLong(url)) { 2149 if (storageServletAddress == null || storageServletAddress == undefined) { 2150 throwException("java.lang.IllegalArgumentException", "No se ha indicado la direccion del servlet para el guardado de datos"); 2151 return; 2152 } 2153 2154 sendDataAndExecAppIntent(idSession, cipherKey, storageServletAddress, retrieverServletAddress, opId, params, successCallback, errorCallback) 2155 } 2156 else { 2157 execAppIntent(url, idSession, cipherKey, successCallback, errorCallback); 2158 } 2159 } 2160 2161 /** 2162 * Convierte texto plano en texto base 64. 2163 * Implementada en el applet Java de firma. 2164 */ 2165 function getBase64FromText (plainText, charset) { 2166 return plainText != null ? Base64.encode(plainText) : null ; 2167 } 2168 2169 /** 2170 * Convierte texto base 64 en texto plano. 2171 * Implementada en el applet Java de firma. 2172 */ 2173 function getTextFromBase64 (base64Text, charset) { 2174 return Base64.decode(base64Text); 2175 } 2176 2177 /** 2178 * Guardado de datos en disco. Se realiza mediante la invocacion de una app nativa. 2179 */ 2180 function saveDataToFile (dataB64, title, filename, extension, description, successCallback, errorCallback) { 2181 2182 if (dataB64 != undefined && dataB64 != null && dataB64 != "") { 2183 dataB64 = dataB64.replace(/\+/g, "-").replace(/\//g, "_"); 2184 } 2185 2186 var idSession = generateNewIdSession(); 2187 var cipherKey = generateCipherKey(); 2188 2189 var i = 0; 2190 var opId = "save"; 2191 var params = new Array(); 2192 params[i++] = {key:"ver", value:PROTOCOL_VERSION};
2193 params[i++] = {key:"op", value:opId}; 2194 if (idSession != null && idSession != undefined) { params[i++] = {key:"id", value:idSession}; } 2195 if (cipherKey != null && cipherKey != undefined) { params[i++] = {key:"key", value:cipherKey}; } 2196 if (storageServletAddress != null && 2197 storageServletAddress != undefined) { params[i++] = {key:"stservlet", value:storageServletAddress}; } 2198 if (title != null && title != undefined) { params[i++] = {key:"title", value:title}; } 2199 if (filename != null && filename != undefined) { params[i++] = {key:"filename", value:filename}; } 2200 if (extension != null && extension != undefined) { params[i++] = {key:"extension", value:extension}; } 2201 if (description != null && description != undefined) { params[i++] = {key:"description", value:description}; } 2202 if (dataB64 != null && dataB64 != undefined && dataB64 != "") { params[i++] = {key:"dat", value:dataB64}; } 2203 2204 var url = buildUrl(opId, params); 2205 2206 // Si la URL es muy larga, realizamos un preproceso para que los datos se suban al 2207 // servidor y la aplicacion nativa los descargue, en lugar de pasarlos directamente 2208 if (isURLTooLong(url)) { 2209 if (storageServletAddress == null || storageServletAddress == undefined) { 2210 throwException("java.lang.IllegalArgumentException", "No se ha indicado la direccion del servlet para el guardado de datos"); 2211 return; 2212 } 2213 2214 sendDataAndExecAppIntent(idSession, cipherKey, storageServletAddress, retrieverServletAddress, opId, params, successCallback, errorCallback) 2215 } 2216 else { 2217 execAppIntent(url, idSession, cipherKey, successCallback, errorCallback); 2218 } 2219 } 2220 2221 /** 2222 * Carga de un fichero. Operacion no soportada. 2223 * Implementada en el applet Java de firma. 2224 */ 2225 function getFileNameContentBase64 (title, extensions, description) { 2226 throwException(UnsupportedOperationException, "La operacion de carga de ficheros no esta soportada"); 2227 } 2228 2229 /** 2230 * Carga de multiples ficheros. Operacion no soportada. 2231 * Implementada en el applet Java de firma. 2232 */ 2233 function getMultiFileNameContentBase64 (title, extensions, description) { 2234 throwException(UnsupportedOperationException, "La operacion de carga de multiples ficheros no esta soportada"); 2235 } 2236 2237 /** 2238 * Funcion para la comprobacion de existencia del objeto. No hace nada. 2239 * Implementada en el applet Java de firma. 2240 */ 2241 function echo () { 2242 return "Cliente JavaScript"; 2243 } 2244 2245 /** 2246 * No hace nada. 2247 * Implementada en el applet Java de firma. 2248 */ 2249 function setStickySignatory (sticky) { 2250 // No hace nada 2251 } 2252 2253 /** 2254 * Recupera el mensaje de error asociado al ultimo error capturado. 2255 * Implementada en el applet Java de firma. 2256 */ 2257 function getErrorMessage () { 2258 return errorMessage; 2259 } 2260 2261 /** 2262 * Recupera el tipo del ultimo error capturado. 2263 * Implementada en el applet Java de firma. 2264 */ 2265 function getErrorType () { 2266 return errorType; 2267 } 2268 2269 /** 2270 * Recupera el log de la aplicacion. Actualmente, el log solo esta 2271 * disponible en el applet, no en las aplicacion moviles. 2272 */ 2273 function getCurrentLog () { 2274 return "Applet no cargado"; 2275 } 2276 2277 /** 2278 * Funcion para identificar el tipo de objeto del Cliente (javascript, applet,...). 2279 */ 2280 function getType () { 2281 return "javascript"; 2282 } 2283 2284 /** 2285 * Establece las rutas de los servlets encargados de almacenar y recuperar las firmas de los dispositivos moviles. 2286 */ 2287 function setServlets (storageServlet, retrieverServlet) { 2288 storageServletAddress = storageServlet; 2289 retrieverServletAddress = retrieverServlet; 2290 } 2291 2292 /** 2293 * Establece el error indicado como error interno y lanza una excepcion. 2294 */ 2295 function throwException (type, message) { 2296 errorType = type; 2297 errorMessage = message; 2298 throw new Error(); 2299 } 2300 2301 2302 /** 2303 * Comprueba si una URL es demasiado larga para ser usada en una llamada 2304 * GET en un Sistema/Navegador concreto. 2305 */ 2306 function isURLTooLong(url) { 2307 if (isAndroid()) { 2308 return url.length > MAX_LONG_ANDROID_URL; 2309 } 2310 else if (isIOS()) { 2311 return url.length > MAX_LONG_IOS_URL; 2312 } 2313 else if (isWindows8()) { 2314 return url.length > MAX_LONG_WINDOWS8_URL; 2315 } 2316 return url.length > MAX_LONG_GENERAL_URL; 2317 } 2318 2319 /* Mayor entero. */ 2320 var MAX_NUMBER = 2147483648; 2321 2322 /* Caracteres validos para los ID de sesion */ 2323 var VALID_CHARS_TO_ID = "1234567890abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 2324 2325 /* Genera un identificador de sesion. */ 2326 function generateNewIdSession () { 2327 var ID_LENGTH = 20; 2328 var random = ""; 2329 var randomInts; 2330 if (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") { 2331 randomInts = new Uint32Array(ID_LENGTH); 2332 window.crypto.getRandomValues(randomInts); 2333 } 2334 else { 2335 randomInts = new Array(ID_LENGTH); 2336 for (var i = 0; i < ID_LENGTH; i++) { 2337 randomInts[i] = rnd() * MAX_NUMBER; 2338 } 2339 } 2340 2341 for (var i = 0; i < ID_LENGTH; i++) { 2342 random += VALID_CHARS_TO_ID.charAt(Math.floor(randomInts[i] % VALID_CHARS_TO_ID.length)); 2343 } 2344 2345 return random; 2346 } 2347 2348 /* Genera un numero aleatorio para utilizar como clave de cifrado. */ 2349 function generateCipherKey() { 2350 var random; 2351 if (typeof window.crypto != "undefined" && typeof window.crypto.getRandomValues != "undefined") { 2352 var randomInts = new Uint32Array(1); 2353 window.crypto.getRandomValues(randomInts); 2354 random = zeroFill(randomInts[0] % 100000000, 8); 2355 } 2356 else {
2357 random = zeroFill(Math.floor(((rnd() * MAX_NUMBER) + 1) % 100000000), 8); 2358 } 2359 2360 return random; 2361 } 2362 2363 /* Completa un numero con ceros a la izquierda. */ 2364 function zeroFill(number, width) { 2365 width -= number.toString().length; 2366 if (width > 0) { 2367 return new Array(width + (/\./.test(number) ? 2 : 1)).join('0') 2368 + number; 2369 } 2370 return number + ""; 2371 } 2372 2373 /** 2374 * Genera numeros aleatorios con una distribucion homogenea 2375 */ 2376 var seed; 2377 function rnd () { 2378 if (seed == undefined) { 2379 seed = new Date().getMilliseconds() * 1000 * Math.random(); 2380 } 2381 seed = (seed * 9301 + 49297) % 233280; 2382 return seed / 233280; 2383 } 2384 2385 /** 2386 * Envia los datos al servidor intermedio y luego invoca a la 2387 * aplicacion nativa para que los descargue y opere con ellos. 2388 * @param idSession Identificador de la operacion con el que se espera recuperar el resultado. 2389 * @param cipherKey Clave de cifrado. Si no se indica, no se cifra. 2390 * @param storageServletAddress URL del servlet que almacena. 2391 * @param retrieverServletAddress URL del servlet que recupera. 2392 * @param op Identificador del tipo de operacion (firma, cofirma, guardado,...). 2393 * @param params Parametros de configuracion de la operacion. 2394 * @param successCallback Funcion callback que debe ejecutarse en caso de exito. 2395 * @param errorCallback Funcion callback que debe ejecutarse en caso de error. 2396 */ 2397 function sendDataAndExecAppIntent(idSession, cipherKey, storageServletAddress, retrieverServletAddress, op, params, successCallback, errorCallback) { 2398 2399 // Identificador del fichero (equivalente a un id de sesion) del que deben recuperarse los datos 2400 var fileId = generateNewIdSession(); 2401 2402 // Subimos los datos al servidor intermedio 2403 var httpRequest = getHttpRequest(); 2404 if (!httpRequest) { 2405 throwException("java.lang.Exception", "Su navegador no permite preprocesar los datos que desea tratar"); 2406 } 2407 httpRequest.open("POST", storageServletAddress, true); 2408 httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 2409 2410 httpRequest.onreadystatechange = function () { 2411 if (httpRequest.readyState == 4) { 2412 if (httpRequest.status == 200) { 2413 2414 url = buildUrlWithoutData(op, fileId, retrieverServletAddress, cipherKey); 2415 if (isURLTooLong(url)) { 2416 errorCallback("java.lang.IllegalArgumentException", "La URL de invocacion al servicio de firma es demasiado larga."); 2417 return; 2418 } 2419 execAppIntent(url, idSession, cipherKey, successCallback, errorCallback); 2420 } 2421 else { 2422 errorCallback("java.lang.IOException", "Ocurrio un error al enviar los datos a la aplicacion nativa"); 2423 } 2424 } 2425 } 2426 2427 httpRequest.onerror = function(e) { 2428 errorCallback("java.lang.IOException", "Ocurrio un error al enviar los datos al servicio intermedio para la comunicacion con la aplicacion nativa"); 2429 } 2430 2431 var requestData = 2432 "op=put&v=1_0&id=" + fileId + "&dat=" + 2433 cipher(buildXML(op, params), cipherKey); 2434 2435 try { 2436 httpRequest.send(requestData); 2437 } 2438 catch(e) { 2439 errorCallback("java.lang.IOException", "No se pudo conectar con el servidor remoto"); 2440 } 2441 } 2442 2443 /** 2444 * Invoca un Intent con la operacion seleccionada, la configuraci\u00F3n establecida y las campos del 2445 * formulario pasado como parametro. Si se define un callback para tratar el caso de exito o error de 2446 * la operacion, se intentara descargar el resultado devuelto por la app del servidor intermedio de 2447 * comunicacion. 2448 * 2449 * @param intentURL URL para la invocacion del Cliente JavaScript 2450 * @param idSession Identificador de la sesi\u00F3n para la recuperaci\u00F3n del resultado. 2451 * @param cipherKey Clave de cifrado para la respuesta del servidor. 2452 * @param successCallback Actuaci\u00F3n a realizar cuando se recupera el resultado de la operación. 2453 * @param errorCallback Actuaci\u00F3n a realizar cuando ocurre un error al recuperar el resultado. 2454 */ 2455 function execAppIntent (intentURL, idSession, cipherKey, successCallback, errorCallback) { 2456 2457 wrongInstallation = false; 2458 2459 // Invocamos al cliente de firma movil. 2460 try { 2461 openUrl(intentURL, errorCallback); 2462 } 2463 catch (e) { 2464 //console.log("Error al abrir la aplicacion nativa: " + e); 2465 return; 2466 } 2467 2468 if (successCallback != null || errorCallback != null) { 2469 if (idSession != null && idSession != undefined && 2470 ((successCallback != undefined && successCallback != null) || 2471 (errorCallback != undefined && errorCallback != null))) { 2472 getStoredFileFromServlet(idSession, retrieverServletAddress, cipherKey, successCallback, errorCallback); 2473 } 2474 } 2475 } 2476 2477 /** 2478 * Construye una URL para la invocacion del Cliente @firma nativo. 2479 * @param op Funcion a invocar en el cliente nativo.
2480 * @param params Parametros para la configuracion de la operacion. 2481 */ 2482 function buildUrl (op, params) { 2483 2484 var urlParams = ""; 2485 if (params != null && params != undefined) { 2486 for (var i = 0; i < params.length; i++) { 2487 if (params[i].value != null && params[i].value != "null") { 2488 urlParams += (i != 0 ? '&' : '') + params[i].key + '=' + encodeURIComponent(params[i].value); 2489 } 2490 } 2491 } 2492 2493 // En el caso de Chrome en Android, construimos la URL en forma de Intent 2494 var url; 2495 if (isChrome() && isAndroid()) { 2496 url = 'intent://' + op + '?' + urlParams + "#Intent;scheme=afirma;package=es.gob.afirma;end"; 2497 } 2498 else { 2499 url = 'afirma://' + op + '?' + urlParams; 2500 } 2501 return url; 2502 } 2503 2504 /** 2505 * Construye un XML con lo valores del array de parametros proporcionado. 2506 * @param op Operacion que configuramos 2507 * @param params Array con los parametros del array. 2508 * @returns XML. 2509 */ 2510 function buildXML (op, params) { 2511 op = (op == null ? "op" : op); 2512 var xml = '<' + op +'>'; 2513 if (params != null) { 2514 for (var i = 0; i < params.length; i++) { 2515 xml += '<e k="' + params[i].key + '" v="' + params[i].value + '"/>'; 2516 } 2517 } 2518 return Base64.encode(xml + '</' + op + '>'); 2519 } 2520 2521 /** 2522 * Crea una URL a partir de los parametros introducidos para la invocación de 2523 * una app nativa para que descargue la configuracion de la operación a realizar. 2524 * @param op Codigo de la operacion a la que tiene que invocar la URL. 2525 * @param id Identificador para la descarga. 2526 * @param rtServlet Servlet para la descarga de la configuración. 2527 * @param cipherKey Clave para el descifrado. 2528 * @returns URL para la llamada a la app con los datos necesarios para que descargue 2529 * la configuració
2529n de la operación a realizar. 2530 */ 2531 function buildUrlWithoutData (op, id, rtServlet, cipherKey) { 2532 var j = 0; 2533 var newParams = new Array(); 2534 newParams[j++] = {key:"fileid", value:id}; 2535 if (rtServlet != null || rtServlet != undefined) { 2536 newParams[j++] = {key:"rtservlet", value:rtServlet}; 2537 } 2538 if (cipherKey != null || cipherKey != undefined) { 2539 newParams[j++] = {key:"key", value:cipherKey}; 2540 } 2541 return buildUrl(op, newParams); 2542 }; 2543 2544 /** 2545 * Llama a la aplicacion de firma a traves de la URL de invocacion sin que afecte 2546 * a la pagina que se esta mostrando. 2547 * @param url URL de invocacion. 2548 * @param errorCallback Funcion de error que deberia lanzarse (ademas de una excepcion), 2549 * si no fuese posible abrir la URL. Puede ser nulo. 2550 */ 2551 function openUrl (url, errorCallback) { 2552 2553 // Usamos el modo de invocacion mas apropiado segun el entorno 2554 if (isChrome() || isIOS()) { 2555 // Usamos document.location porque tiene mejor soporte por los navegadores que 2556 // window.location que es el mecanismo estandar 2557 document.location = url; 2558 } 2559 else { 2560 2561 // Si ya existe el iframe, lo eliminamos para despues volverlo a crear 2562 if (document.getElementById("iframeAfirma") != null) { 2563 try { 2564 var element = document.getElementById("iframeAfirma"); 2565 element.outerHTML = ""; 2566 delete element; 2567 } 2568 catch (e) { 2569 // No hacemos nada 2570 } 2571 } 2572 2573 // En el caso de ser una version de internet Explorer que soportase la deteccion de aplicacion 2574 // capaces de manejar el protocolo, aprovechamos esta caracteristica (Internet Explorer para Windows 8 Modern UI) 2575 2576 if (navigator.msLaunchUri) { 2577 navigator.msLaunchUri( 2578 url, 2579 null, 2580 function() { 2581 // Bloqueamos la conexion para evitar que se sigan haciendo comprobaciones 2582 wrongInstallation = true; 2583 } 2584 ); 2585 } 2586 else { 2587 // Abrimos la URL por medio de un iframe 2588 openUrlWithIframe(url); 2589 } 2590 } 2591 } 2592 2593 /** 2594 * Llama a la aplicacion de firma por medio de un iframe. 2595 * @param url URL de invocacion. 2596 */ 2597 function openUrlWithIframe (url) { 2598 var iframeElem = document.createElement("iframe"); 2599 2600 var idAttr = document.createAttribute("id"); 2601 idAttr.value = "iframeAfirma"; 2602 iframeElem.setAttributeNode(idAttr); 2603 2604 var srcAttr = document.createAttribute("src"); 2605 srcAttr.value = url; 2606 iframeElem.setAttributeNode(srcAttr); 2607 2608 var heightAttr = document.createAttribute("height"); 2609 heightAttr.value = 1; 2610 iframeElem.setAttributeNode(heightAttr); 2611 2612 var widthAttr = document.createAttribute("width"); 2613 widthAttr.value = 1; 2614 iframeElem.setAttributeNode(widthAttr); 2615 2616 document.body.appendChild(iframeElem); 2617 } 2618 2619 /** 2620 * Ejecuta el metodo de error si el html recuperado es tal o el metodo de exito si no lo es, 2621 * en cuyo caso previamente descifrara el resultado. 2622 * @param html Resultado obtenido. 2623 * @param cipherKey Clave para el descifrado del resultado si no es un error. 2624 * @param successCallback Metodo a ejecutar en caso de exito. 2625 * @param errorCallback Metodo a ejecutar en caso de error. 2626 * @returns Devuelve true si se ha fallado pero se puede volver a reintentar, false en caso de 2627 * error determinante o exito. 2628 */ 2629 function successResponseFunction (html, cipherKey, successCallback, errorCallback) { 2630 2631 // Si se obtiene el mensaje de error de que el identificador no existe, seguimos intentandolo 2632 if (html.substr(0, 6).toLowerCase() == "err-06") { 2633 return true; 2634 } 2635 2636 // Si se obtiene otro mensaje de error, se deja de intentar y se ejecuta la funcion callback de error 2637 if (html.substr(0, 4).toLowerCase() == "err-" && html.indexOf(":=") != -1) { 2638 errorMessage = html.substring(html.indexOf(":=") + 2); 2639 errorType = "java.lang.Exception"; 2640 errorCallback(errorType, errorMessage); 2641 return false; 2642 } 2643 2644 // Se ha cancelado la operacion 2645 if (html == "CANCEL" || html == "CANCEL\r\n") { 2646 errorCallback("es.gob.afirma.core.AOCancelledOperationException", "Operacion cancelada por el usuario"); 2647 return false; 2648 } 2649 2650 // La operacion ha finalizado correctamene (Funcion de guardado) 2651 if (html == "OK" || html == "OK\r\n") { 2652 successCallback(); 2653 return false; 2654 } 2655 2656 // Se ha producido un error 2657 if (html.length > 4 && html.substr(0, 4) == "SAF_") { 2658 errorCallback("java.lang.Exception", html); 2659 return false; 2660 } 2661
2662 // Si no se obtuvo un error, habremos recibido la firma y posiblemente el certificado (que antecederia a la 2663 // firma y se separaria de ella con '|'). Si se definio una clave de cifrado, consideramos que la firma 2664 // y el certificado (en caso de estar) llegan cifrados. El cifrado de ambos elementos es independiente 2665 var certificate; 2666 var signature; 2667 var sepPos = html.indexOf('|'); 2668 if (sepPos == -1) { 2669 if (cipherKey != undefined && cipherKey != null) { 2670 signature = decipher(html, cipherKey); 2671 } 2672 else { 2673 signature = fromBase64UrlSaveToBase64(html); 2674 } 2675 } 2676 else { 2677 if (cipherKey != undefined && cipherKey != null) { 2678 certificate = decipher(html.substring(0, sepPos), cipherKey); 2679 signature = decipher(html.substring(sepPos + 1), cipherKey); 2680 } 2681 else { 2682 certificate = fromBase64UrlSaveToBase64(html.substring(0, sepPos)); 2683 signature = fromBase64UrlSaveToBase64(html.substring(sepPos + 1)); 2684 } 2685 } 2686 2687 successCallback(signature, certificate); 2688 2689 return false; 2690 } 2691
2692 function errorResponseFunction (type, message, errorCallback) { 2693 2694 errorType = (type != null && type.length > 0) ? 2695 type : "java.lang.Exception"; 2696 errorMessage = (message != null && message.length > 0) ? 2697 message : "No se ha podido extablecer la comunicaci\u00F3n entre la aplicaci\u00F3n de firma y la p\u00E1gina web"; 2698 if (errorCallback != undefined && errorCallback != null) { 2699 errorCallback(errorType, errorMessage); 2700 } 2701 } 2702 2703 var NUM_MAX_ITERATIONS = 15; 2704 var iterations = 0; 2705 2706 function getStoredFileFromServlet (idDocument, servletAddress, cipherKey, successCallback, errorCallback) { 2707 2708 var httpRequest = getHttpRequest(); 2709 if (!httpRequest) { 2710 throwException("java.lang.Exception", "Su navegador no permite obtener el resulado de la operaci\u00F3n"); 2711 } 2712 2713 iterations = 0; 2714 setTimeout(retrieveRequest, 4000, httpRequest, servletAddress, "op=get&v=1_0&id=" + idDocument + "&it=0", cipherKey, successCallback, errorCallback); 2715 } 2716 2717 function retrieveRequest(httpRequest, url, params, cipherKey, successCallback, errorCallback) { 2718 2719 if (wrongInstallation) {
2720 errorResponseFunction("es.gob.afirma.standalone.ApplicationNotFoundException", "AutoFirma no se encuentra instalado en el sistema.", errorCallback); 2721 return; 2722 } 2723 2724 // Contamos la nueva llamada al servidor 2725 if (iterations > NUM_MAX_ITERATIONS) { 2726 errorResponseFunction("java.util.concurrent.TimeoutException", "El tiempo para la recepcion de la firma por la pagina web ha expirado", errorCallback); 2727 return; 2728 } 2729 iterations++; 2730 2731 httpRequest.onreadystatechange = function() { 2732 if (httpRequest.readyState == 4) { 2733 if (httpRequest.status == 200) { 2734 var needContinue = successResponseFunction(httpRequest.responseText, cipherKey, successCallback, errorCallback); 2735 if (needContinue) { 2736 setTimeout(retrieveRequest, 4000, httpRequest, url, params.replace("&it=" + (iterations-1), "&it=" + iterations), cipherKey, successCallback, errorCallback); 2737 } 2738 } 2739 else { 2740 errorResponseFunction(null, httpRequest.responseText, errorCallback); 2741 } 2742 } 2743 } 2744 2745 httpRequest.onerror = function() {
2746 errorResponseFunction("java.lang.Exception", "No se pudo conectar con el servidor intermedio para la recuperacion del resultado de la operacion", errorCallback); 2747 } 2748 2749 httpRequest.open("POST", url, true); 2750 httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 2751 2752 try { 2753 httpRequest.send(params); 2754 } 2755 catch(e) { 2756 // Error en la llamada para al recuperacion del resultado. No lo encuentra o problema 2757 // de tipo cross-domain 2758 errorResponseFunction("java.lang.IOException", "Ocurrio un error de red en la llamada al servicio de firma", errorCallback); 2759 return; 2760 } 2761 } 2762 2763 /** 2764 * Realiza un descifrado DES compatible con Java (Algoritmo DES, modo CBC, sin Padding). 2765 * Recibe en base 64 la cadena de texto cifrado antecedido por el padding anadido manualmente 2766 * a los datos para permitir el cifrado DES (separado por un punto ('.')), ademas de la clave 2767 * para descifrar. 2768 * Como resultado devuelve la cadena de texto descifrada en base 64. 2769 */ 2770 function decipher(cipheredData, key) { 2771 2772 var dotPos = cipheredData.indexOf('.'); 2773 var padding = cipheredData.substr(0, dotPos); 2774 2775 var deciphered = Cipher.des(key, Cipher.base64ToString(fromBase64UrlSaveToBase64(cipheredData.substr(dotPos + 1))), 0, 0, null); 2776 return Cipher.stringToBase64(deciphered.substr(0, deciphered.length - parseInt(padding) - 8)); 2777 } 2778 2779 /** 2780 * Realiza un cifrado DES compatible con Java (Algoritmo DES, modo CBC, sin Padding). 2781 * @param dataB64 Cadena de texto base 64. 2782 * @param key Clave de cifrado. 2783 * @return Base 64 cifrado. 2784 */ 2785 function cipher(dataB64, key) { 2786 2787 var data = Cipher.base64ToString(fromBase64UrlSaveToBase64(dataB64)); 2788 var padding = (8 - (data.length % 8)) % 8; 2789 2790 // Los datos cifrados los pasamos a base 64 y, antes de devolverlos le anteponemos el padding que 2791 // le habra agregado el metodo de cifrado separados por un punto ('.'). 2792 return padding + "." + Cipher.stringToBase64(Cipher.des(key, data, 1, 0, null)).replace(/\+/g, "-").replace(/\//g, "_"); 2793 } 2794 2795 /** 2796 * Convierte de Base64 URL Save a Base64 normal. 2797 */ 2798 function fromBase64UrlSaveToBase64(base64UrlSave) { 2799 if (!base64UrlSave) { 2800 return base64UrlSave; 2801 } 2802 return base64UrlSave.replace(/\-/g, "+").replace(/\_/g, "/") 2803 } 2804 2805 /* Metodos que publicamos del objeto AppAfirmaJS */ 2806 return { 2807 echo : echo, 2808 checkTime : checkTime, 2809 setKeyStore : setKeyStore, 2810 sign : sign, 2811 coSign : coSign, 2812 counterSign : counterSign, 2813 signBatch : signBatch, 2814 selectCertificate : selectCertificate, 2815 saveDataToFile : saveDataToFile, 2816 signAndSaveToFile : signAndSaveToFile, 2817 getFileNameContentBase64: getFileNameContentBase64, 2818 getMultiFileNameContentBase64 : getMultiFileNameContentBase64, 2819 getBase64FromText : getBase64FromText, 2820 getTextFromBase64 : getTextFromBase64, 2821 setServlets : setServlets, 2822 setStickySignatory : setStickySignatory, 2823 setLocale : setLocale, 2824 getErrorMessage : getErrorMessage, 2825 getErrorType : getErrorType, 2826 getCurrentLog : getCurrentLog 2827 } 2828 }); 2829 2830 /* Metodos que publicamos del objeto MiniApplet */ 2831 return { 2832 2833 VERSION : VERSION, 2834 2835 /* Publicamos las variables para la comprobacion de hora. */ 2836 CHECKTIME_NO : CHECKTIME_NO, 2837 CHECKTIME_RECOMMENDED : CHECKTIME_RECOMMENDED, 2838 CHECKTIME_OBLIGATORY : CHECKTIME_OBLIGATORY,
2839 2840 /* Publicamos las variables para establecer parametros personalizados en la JVM */ 2841 JAVA_ARGUMENTS : JAVA_ARGUMENTS, 2842 SYSTEM_PROPERTIES : SYSTEM_PROPERTIES, 2843 2844 /* Publicamos las variables para configurar un almacen de certificados concreto. */ 2845 KEYSTORE_WINDOWS : KEYSTORE_WINDOWS, 2846 KEYSTORE_APPLE : KEYSTORE_APPLE, 2847 KEYSTORE_PKCS12 : KEYSTORE_PKCS12, 2848 KEYSTORE_PKCS11 : KEYSTORE_PKCS11, 2849 KEYSTORE_MOZILLA : KEYSTORE_MOZILLA, 2850 KEYSTORE_SHARED_NSS : KEYSTORE_SHARED_NSS, 2851 KEYSTORE_JAVA : KEYSTORE_JAVA, 2852 KEYSTORE_JCEKS : KEYSTORE_JCEKS, 2853 KEYSTORE_JAVACE : KEYSTORE_JAVACE, 2854 KEYSTORE_DNIE : KEYSTORE_DNIE, 2855 2856 /* Constantes para la deteccion de la aplicacion de autofirma */ 2857 AUTOFIRMA_LAUNCHING_TIME : AUTOFIRMA_LAUNCHING_TIME, 2858 AUTOFIRMA_CONNECTION_RETRIES : AUTOFIRMA_CONNECTION_RETRIES, 2859 2860 /* Variable para forzar el uso del mecanismo de comunicacion por servidor intermedio */ 2861 setForceWSMode : setForceWSMode, 2862 2863 /* Metodos visibles. */ 2864 cargarMiniApplet : cargarMiniApplet, 2865 cargarAppAfirma : cargarAppAfirma, 2866 echo : echo, 2867 checkTime : checkTime, 2868 sign : sign, 2869 coSign : coSign, 2870 counterSign : counterSign, 2871 signBatch : signBatch, 2872 selectCertificate : selectCertificate, 2873 saveDataToFile : saveDataToFile, 2874 signAndSaveToFile : signAndSaveToFile, 2875 getFileNameContentBase64: getFileNameContentBase64, 2876 getMultiFileNameContentBase64 : getMultiFileNameContentBase64, 2877 getBase64FromText : getBase64FromText, 2878 getTextFromBase64 : getTextFromBase64, 2879 downloadRemoteData : downloadRemoteData, 2880 setKeyStore : setKeyStore, 2881 setServlets : setServlets, 2882 setStickySignatory : setStickySignatory, 2883 setLocale : setLocale, 2884 getErrorMessage : getErrorMessage, 2885 getErrorType : getErrorType, 2886 getCurrentLog : getCurrentLog, 2887 isAndroid : isAndroid, 2888 isIOS : isIOS 2889 }; 2890})(window, undefined); 2891 2892 2893 2894 2895 2896/** 2897 * Base64 encode / decode 2898 * http://www.webtoolkit.info/ 2899 */ 2900var Base64 = { 2901 2902 // private property 2903 _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 2904 _keyStr_URL_SAFE : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=", 2905 2906 // public method for encoding 2907 encode : function (input, URL_SAFE) { 2908 2909 var keyStr = (URL_SAFE == true) ? this._keyStr_URL_SAFE : this._keyStr; 2910 2911 var output = ""; 2912 var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 2913 var i = 0; 2914 2915 input = Base64._utf8_encode(input); 2916 2917 while (i < input.length) { 2918 2919 chr1 = input.charCodeAt(i++); 2920 chr2 = input.charCodeAt(i++); 2921 chr3 = input.charCodeAt(i++); 2922 2923 enc1 = chr1 >> 2; 2924 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 2925 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 2926 enc4 = chr3 & 63; 2927 2928 if (isNaN(chr2)) { 2929 enc3 = enc4 = 64; 2930 } else if (isNaN(chr3)) { 2931 enc4 = 64; 2932 } 2933 2934 output = output + 2935 keyStr.charAt(enc1) + keyStr.charAt(enc2) + 2936 keyStr.charAt(enc3) + keyStr.charAt(enc4); 2937 2938 } 2939 2940 return output; 2941 }, 2942 2943 /** 2944 * Codifica los datos binarios obtenidos en un ArrayBuffer. 2945 */ 2946 encodeArrayBuffer : function(input, URL_SAFE) { 2947 2948 var uInt8Array = new Uint8Array(input); 2949 var i = uInt8Array.length; 2950 var binaryString = new Array(i); 2951 while (i--) 2952 { 2953 binaryString[i] = String.fromCharCode(uInt8Array[i]); 2954 } 2955 2956 return URL_SAFE ? 2957 window.btoa(binaryString.join('')).replace('+', '-').replace('/', '_') : 2958 window.btoa(binaryString.join('')); 2959 }, 2960 2961 /** 2962 * Codifica un array de bytes a Base64. 2963 * 2964 * @param {Array.<number>|Uint8Array} input An array of bytes (numbers with 2965 * value in [0, 255]) to encode. 2966 * @param {boolean=} opt_webSafe Boolean indicating we should use the 2967 * alternative alphabet. 2968 * @return {string} The base64 encoded string. 2969 */ 2970 encodeByteArray : function(input, URL_SAFE) { 2971 2972 var keyStr = (URL_SAFE == true) ? this._keyStr_URL_SAFE : this._keyStr; 2973 2974 var output = []; 2975 2976 for (var i = 0; i < input.length; i += 3) {
2977 var byte1 = input[i]; 2978 var haveByte2 = i + 1 < input.length; 2979 var byte2 = haveByte2 ? input[i + 1] : 0; 2980 var haveByte3 = i + 2 < input.length; 2981 var byte3 = haveByte3 ? input[i + 2] : 0; 2982 2983 var outByte1 = byte1 >> 2; 2984 var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4); 2985 var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6); 2986 var outByte4 = byte3 & 0x3F; 2987 2988 if (!haveByte3) { 2989 outByte4 = 64; 2990 2991 if (!haveByte2) { 2992 outByte3 = 64; 2993 } 2994 } 2995 2996 output.push(keyStr.charAt(outByte1), 2997 keyStr.charAt(outByte2), 2998 keyStr.charAt(outByte3), 2999 keyStr.charAt(outByte4)); 3000 } 3001 3002 return output.join(''); 3003 }, 3004 3005 // public method for decoding 3006 decode : function (input, URL_SAFE) { 3007 3008 var keyStr = (URL_SAFE == true) ? this._keyStr_URL_SAFE : this._keyStr; 3009 3010 var output = ""; 3011 var chr1, chr2, chr3; 3012 var enc1, enc2, enc3, enc4; 3013 var i = 0; 3014 3015 input = (URL_SAFE == true) ? input.replace(/[^A-Za-z0-9\-\_\=]/g, "") : input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 3016 3017 while (i < input.length) { 3018 3019 enc1 = keyStr.indexOf(input.charAt(i++)); 3020 enc2 = keyStr.indexOf(input.charAt(i++)); 3021 enc3 = keyStr.indexOf(input.charAt(i++)); 3022 enc4 = keyStr.indexOf(input.charAt(i++)); 3023 3024 chr1 = (enc1 << 2) | (enc2 >> 4); 3025 chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 3026 chr3 = ((enc3 & 3) << 6) | enc4; 3027 3028 output = output + String.fromCharCode(chr1); 3029 3030 if (enc3 != 64) { 3031 output = output + String.fromCharCode(chr2); 3032 } 3033 if (enc4 != 64) { 3034 output = output + String.fromCharCode(chr3); 3035 } 3036 3037 } 3038 3039 output = Base64._utf8_decode(output); 3040 3041 return output; 3042 3043 }, 3044 3045 // private method for UTF-8 encoding 3046 _utf8_encode : function (string) { 3047 string = string.replace(/\r\n/g,"\n"); 3048 var utftext = ""; 3049 3050 for (var n = 0; n < string.length; n++) { 3051 3052 var c = string.charCodeAt(n); 3053 3054 if (c < 128) { 3055 utftext += String.fromCharCode(c); 3056 } 3057 else if((c > 127) && (c < 2048)) { 3058 utftext += String.fromCharCode((c >> 6) | 192); 3059 utftext += String.fromCharCode((c & 63) | 128); 3060 } 3061 else { 3062 utftext += String.fromCharCode((c >> 12) | 224); 3063 utftext += String.fromCharCode(((c >> 6) & 63) | 128); 3064 utftext += String.fromCharCode((c & 63) | 128); 3065 } 3066 3067 } 3068 3069 return utftext; 3070 }, 3071 3072 // private method for UTF-8 decoding 3073 _utf8_decode : function (utftext) { 3074 var string = ""; 3075 var i = 0; 3076 var c = 0; 3077 var c1 = 0; 3078 var c2 = 0; 3079 3080 while ( i < utftext.length ) { 3081 3082 c = utftext.charCodeAt(i); 3083 3084 if (c < 128) { 3085 string += String.fromCharCode(c); 3086 i++; 3087 } 3088 else if((c > 191) && (c < 224)) { 3089 c2 = utftext.charCodeAt(i+1); 3090 string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 3091 i += 2; 3092 } 3093 else { 3094 c2 = utftext.charCodeAt(i+1); 3095 var c3 = utftext.charCodeAt(i+2); 3096 string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 3097 i += 3; 3098 } 3099 } 3100 3101 return string; 3102 } 3103}; 3104 3105//Paul Tero, July 2001 3106//http://www.tero.co.uk/des/ 3107 3108//Optimised for performance with large blocks by Michael Hayworth, November 2001 3109//http://www.netdealing.com 3110 3111//THIS SOFTWARE IS PROVIDED "AS IS" AND 3112//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 3113//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 3114//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 3115//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3116//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3117//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3118//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3119//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3120//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3121//SUCH DAMAGE. 3122 3123//des 3124//this takes the key, the message, and whether to encrypt or decrypt 3125var Cipher = { 3126 tableStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
3126+/", 3127 tableStr_URL_SAFE : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", 3128 3129 des : function (key, message, encrypt, mode, iv, padding) { 3130 //declaring this locally speeds things up a bit 3131 var spfunction1 = new Array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004); 3132 var spfunction2 = new Array (-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000); 3133 var spfunction3 = new Array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200); 3134 var spfunction4 = new Array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080); 3135 var spfunction5 = new Array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,
31350x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100); 3136 var spfunction6 = new Array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010); 3137 var spfunction7 = new Array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002); 3138 var spfunction8 = new Array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000); 3139 3140 //create the 16 or 48 subkeys we will need 3141 var keys = Cipher.des_createKeys (key); 3142 var m=0, i, j, temp, right1, right2, left, right, looping; 3143 var cbcleft, cbcleft2, cbcright, cbcright2; 3144 var endloop, loopinc; 3145 var len = message.length; 3146 var chunk = 0; 3147 //set up the loops for single and triple des 3148 var iterations = keys.length == 32 ? 3 : 9; //single or triple des 3149 if (iterations == 3) {looping = encrypt ? new Array (0, 32, 2) : new Array (30, -2, -2);} 3150 else {looping = encrypt ? new Array (0, 32, 2, 62, 30, -2, 64, 96, 2) : new Array (94, 62, -2, 32, 64, 2, 30, -2, -2);} 3151 3152 //pad the message depending on the padding parameter 3153 if (padding == 2) message += " "; //pad the message with spaces 3154 else if (padding == 1) {temp = 8-(len%8); message += String.fromCharCode (temp,temp,temp,temp,temp,temp,temp,temp); if (temp==8) len+=8;} //PKCS7 padding 3155 else if (!padding) message += "\0\0\0\0\0\0\0\0"; //pad the message out with null bytes 3156 3157 //store the result here 3158 var result = ""; 3159 var tempresult = ""; 3160 3161 if (mode == 1) { //CBC mode 3162 cbcleft = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++); 3163 cbcright = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++); 3164 m=0; 3165 } 3166 3167 //loop through each 64 bit chunk of the message 3168 while (m < len) { 3169 left = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++); 3170 right = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++); 3171 3172 //for Cipher Block Chaining mode, xor the message with the previous result 3173 if (mode == 1) {if (encrypt) {left ^= cbcleft; right ^= cbcright;} else {cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;}} 3174 3175 //first each 64 but chunk of the message must be permuted according to IP 3176 temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 3177 temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); 3178 temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); 3179 temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 3180 temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 3181 3182 left = ((left << 1) | (left >>> 31)); 3183 right = ((right << 1) | (right >>> 31)); 3184 3185 //do this either 1 or 3 times for each chunk of the message 3186 for (j=0; j<iterations; j+=3) { 3187 endloop = looping[j+1]; 3188 loopinc = looping[j+2]; 3189 //now go through and perform the encryption or decryption 3190 for (i=looping[j]; i!=endloop; i+=loopinc) { //for efficiency 3191 right1 = right ^ keys[i]; 3192 right2 = ((right >>> 4) | (right << 28)) ^ keys[i+1]; 3193 //the result is attained by passing these bytes through the S selection functions 3194 temp = left; 3195 left = right; 3196 right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f] 3197 | spfunction6[(right1 >>> 8) & 0x3f] | spfunction8[right1 & 0x3f] 3198 | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f] 3199 | spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]); 3200 } 3201 temp = left; left = right; right = temp; //unreverse left and right 3202 } //for either 1 or 3 iterations 3203 3204 //move then each one bit to the right 3205 left = ((left >>> 1) | (left << 31)); 3206 right = ((right >>> 1) | (right << 31)); 3207 3208 //now perform IP-1, which is IP in the opposite direction 3209 temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 3210 temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 3211 temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); 3212 temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); 3213 temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 3214 3215 //for Cipher Block Chaining mode, xor the message with the previous result 3216 if (mode == 1) {if (encrypt) {cbcleft = left; cbcright = right;} else {left ^= cbcleft2; right ^= cbcright2;}} 3217 tempresult += String.fromCharCode ((left>>>24), ((left>>>16) & 0xff), ((left>>>8) & 0xff), (left & 0xff), (right>>>24), ((right>>>16) & 0xff), ((right>>>8) & 0xff), (right & 0xff)); 3218 3219 chunk += 8; 3220 if (chunk == 512) {result += tempresult; tempresult = ""; chunk = 0;} 3221 } //for every 8 characters, or 64 bits in the message 3222 3223 //return the result as an array 3224 return result + tempresult; 3225 }, 3226 3227 //this takes as input a 64 bit key (even though only 56 bits are used) 3228 //as an array of 2 integers, and returns 16 48 bit keys 3229 des_createKeys : function (key) { 3230 //declaring this locally speeds things up a bit 3231 var pc2bytes0 = new Array (0,0x4,0x20000000,0x20000004,0x10000,0x10004,0
3231x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204); 3232 var pc2bytes1 = new Array (0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101); 3233 var pc2bytes2 = new Array (0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808); 3234 var pc2bytes3 = new Array (0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000); 3235 var pc2bytes4 = new Array (0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010); 3236 var pc2bytes5 = new Array (0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420); 3237 var pc2bytes6 = new Array (0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002); 3238 var pc2bytes7 = new Array (0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800); 3239 var pc2bytes8 = new Array (0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002); 3240 var pc2bytes9 = new Array (0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408); 3241 var pc2bytes10 = new Array (0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020); 3242 var pc2bytes11 = new Array (0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200); 3243 var pc2bytes12 = new Array (0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010); 3244 var pc2bytes13 = new Array (0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105); 3245 3246 //how many iterations (1 for des, 3 for triple des) 3247 var iterations = key.length > 8 ? 3 : 1; //changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys 3248 //stores the return keys 3249 var keys = new Array (32 * iterations); 3250 //now define the left shifts which need to be done 3251 var shifts = new Array (0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0); 3252 //other variables 3253 var lefttemp, righttemp, m=0, n=0, temp; 3254 3255 for (var j=0; j<iterations; j++) { //either 1 or 3 iterations 3256 var left = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCo
3256deAt(m++); 3257 var right = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++); 3258 3259 temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); 3260 temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16); 3261 temp = ((left >>> 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2); 3262 temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16); 3263 temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 3264 temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); 3265 temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); 3266 3267 //the right side needs to be shifted and to get the last four bits of the left side 3268 temp = (left << 8) | ((right >>> 20) & 0x000000f0); 3269 //left needs to be put upside down 3270 left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0); 3271 right = temp; 3272 3273 //now go through and perform these shifts on the left and right keys 3274 for (var i=0; i < shifts.length; i++) {
3275 //shift the keys either one or two bits to the left 3276 if (shifts[i]) {left = (left << 2) | (left >>> 26); right = (right << 2) | (right >>> 26);} 3277 else {left = (left << 1) | (left >>> 27); right = (right << 1) | (right >>> 27);} 3278 left &= -0xf; right &= -0xf; 3279 3280 //now apply PC-2, in such a way that E is easier when encrypting or decrypting 3281 //this conversion will look like PC-2 except only the last 6 bits of each byte are used 3282 //rather than 48 consecutive bits and the order of lines will be according to 3283 //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7 3284 lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf] 3285 | pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf] 3286 | pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf] 3287 | pc2bytes6[(left >>> 4) & 0xf]; 3288 righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf] 3289 | pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf] 3290 | pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf] 3291 | pc2bytes13[(right >>> 4) & 0xf]; 3292 temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff; 3293 keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16); 3294 } 3295 } //for each iterations 3296 //return the keys we've created 3297 return keys; 3298 }, 3299 3300 //Convierte una cadena a Base 64. Debido a un error en el algoritmo original, pasaremos 3301 // de cadena a hexadecimal y de hexadecimal a Base64 3302 stringToBase64 : function (s) { 3303 3304 // Para realizar la transformacion, primero debemos convertir a hexadecimal y luego a cadena 3305 3306 // A hexadecimal 3307 var str = ""; 3308 var hexes = new Array ("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"); 3309 for (var i=0; i<s.length; i++) {str += hexes [s.charCodeAt(i) >> 4] + hexes [s.charCodeAt(i) & 0xf];} 3310 3311 // A cadena 3312 var byteString; 3313 var byteArray = str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" "); 3314 3315 // Vaciamos la cadena por liberar recursos 3316 str = ""; 3317 3318 try { 3319 byteString = String.fromCharCode.apply(null, byteArray); 3320 } catch (e) { 3321 var byteString = ""; 3322 for (var i = 0, len = byteArray.length; i < len; i++) { 3323 byteString += String.fromCharCode(byteArray[i]); 3324 } 3325 } 3326 3327 return Cipher.btoa(byteString, null); 3328 }, 3329 3330 //Convert a base64 string into a normal string 3331 base64ToString : function(s) { 3332 //the base 64 characters 3333 var BASE64 = new Array ('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','
3333i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'); 3334 3335 var decode = new Object(); 3336 for (var i=0; i<BASE64.length; i++) {decode[BASE64[i]] = i;} //inverse of the array 3337 decode['='] = 0; //add the equals sign as well 3338 var r = "", c1, c2, c3, c4, len=s.length; //define variables 3339 s += "===="; //just to make sure it is padded correctly 3340 for (var i=0; i<len; i+=4) { //4 input characters at a time 3341 c1 = s.charAt(i); //the 1st base64 input characther 3342 c2 = s.charAt(i+1); 3343 c3 = s.charAt(i+2); 3344 c4 = s.charAt(i+3); 3345 r += String.fromCharCode (((decode[c1] << 2) & 0xff) | (decode[c2] >> 4)); //reform the string 3346 if (c3 != '=') r += String.fromCharCode (((decode[c2] << 4) & 0xff) | (decode[c3] >> 2)); 3347 if (c4 != '=') r += String.fromCharCode (((decode[c3] << 6) & 0xff) | decode[c4]); 3348 } 3349 return r; 3350 }, 3351 3352 // --- Funciones para pasar de Hexadecimal a base64 3353 3354 btoa : function (bin, urlSafe) { 3355 var table = (urlSafe == true ? Cipher.tableStr_URL_SAFE : Cipher.tableStr).split(""); 3356 3357 for (var i = 0, j = 0, len = bin.length / 3, base64 = []; i < len; ++i) { 3358 var a = bin.charCodeAt(j++), b = bin.charCodeAt(j++), c = bin.charCodeAt(j++); 3359 if ((a | b | c) > 255) throw new Error("String contains an invalid character"); 3360 base64[base64.length] = table[a >> 2] + table[((a << 4) & 63) | (b >> 4)] + 3361 (isNaN(b) ? "=" : table[((b << 2) & 63) | (c >> 6)]) + 3362 (isNaN(b + c) ? "=" : table[c & 63]); 3363 } 3364 return base64.join(""); 3365 } 3366};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.