/assets/javascripts/miniapplet.js

https://github.com/descala/haltr · JavaScript · 1685 lines · 1031 code · 284 blank · 370 comment · 377 complexity · 705d67f8c50fa1d048c0ae1ac1f90302 MD5 · raw file

  1. if (document.all && !window.setTimeout.isPolyfill) {
  2. var __nativeST__ = window.setTimeout;
  3. window.setTimeout = function (vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */) {
  4. var aArgs = Array.prototype.slice.call(arguments, 2);
  5. return __nativeST__(vCallback instanceof Function ? function () {
  6. vCallback.apply(null, aArgs);
  7. } : vCallback, nDelay);
  8. };
  9. window.setTimeout.isPolyfill = true;
  10. }
  11. var MiniApplet = {
  12. VERSION : "1.2",
  13. JAR_NAME : 'miniapplet-full_1_2u1.jar',
  14. JAVA_ARGUMENTS : '-Xms512M -Xmx512M',
  15. CUSTOM_JAVA_ARGUMENTS : null,
  16. clienteFirma : null,
  17. codeBase : null,
  18. storageServletAddress : null,
  19. retrieverServletAddress : null,
  20. clientType : null,
  21. severeTimeDelay : false,
  22. selectedLocale : null,
  23. /* Almacenes de certificados */
  24. KEYSTORE_WINDOWS : "WINDOWS",
  25. KEYSTORE_APPLE : "APPLE",
  26. KEYSTORE_PKCS12 : "PKCS12",
  27. KEYSTORE_PKCS11 : "PKCS11",
  28. KEYSTORE_FIREFOX : "MOZ_UNI",
  29. /* Valores para la configuracion de la comprobacion de tiempo */
  30. CHECKTIME_NO : "CT_NO",
  31. CHECKTIME_RECOMMENDED : "CT_RECOMMENDED",
  32. CHECKTIME_OBLIGATORY : "CT_OBLIGATORY",
  33. /* ------------------------------------------------ */
  34. /* Constantes para la operacion interna del Cliente */
  35. /* ------------------------------------------------ */
  36. /* Longitud maximo de una URL en Android para la invocacion de una aplicacion nativa. */
  37. MAX_LONG_ANDROID_URL : 2000,
  38. /* Longitud maximo de una URL en iOS para la invocacion de una aplicacion nativa. */
  39. MAX_LONG_IOS_URL : 80000,
  40. /* Longitud maximo de una URL en Windows 8 para la invocacion de una aplicacion nativa. */
  41. MAX_LONG_WINDOWS8_URL : 2000,
  42. /* Tamano del buffer con el que se pasa informacion al applet */
  43. BUFFER_SIZE : 1024 * 1024,
  44. /* Cadena que determina el fin de una respuesta */
  45. EOF : "%%EOF%%",
  46. TYPE_APPLET : "APPLET",
  47. TYPE_JAVASCRIPT : "JAVASCRIPT",
  48. /* ------------------------------------ */
  49. /* Funciones de comprobacion de entorno */
  50. /* ------------------------------------ */
  51. /**
  52. * Determina con un boolean si nuestro cliente es Android
  53. */
  54. isAndroid : function () {
  55. return navigator.userAgent.toUpperCase().indexOf("ANDROID") != -1 ||
  56. navigator.appVersion.toUpperCase().indexOf("ANDROID") != -1 ||
  57. // Para la deteccion de los Kindle Fire
  58. navigator.userAgent.toUpperCase().indexOf("SILK/") != -1 ||
  59. navigator.userAgent.toUpperCase().indexOf("KFJWI") != -1 ||
  60. navigator.userAgent.toUpperCase().indexOf("KFJWA") != -1 ||
  61. navigator.userAgent.toUpperCase().indexOf("KFTT") != -1 ||
  62. navigator.userAgent.toUpperCase().indexOf("KFOT") != -1 ||
  63. navigator.userAgent.toUpperCase().indexOf("KINDLE FIRE") != -1
  64. ;
  65. },
  66. /**
  67. * Determina con un boolean si nuestro cliente es iOS.
  68. */
  69. isIOS : function () {
  70. return (navigator.userAgent.toUpperCase().indexOf("IPAD") != -1) ||
  71. (navigator.userAgent.toUpperCase().indexOf("IPOD") != -1) ||
  72. (navigator.userAgent.toUpperCase().indexOf("IPHONE") != -1);
  73. },
  74. /** Comprueba si se permite la ejecucion de ActiveX. */
  75. isActivexEnabled : function () {
  76. var supported = null;
  77. try {
  78. supported = !!new ActiveXObject("htmlfile");
  79. } catch (e) {
  80. supported = false;
  81. }
  82. return supported;
  83. },
  84. /** Determina con un boolean si nos encontramos en Windows 8/8.1 */
  85. isWindows8 : function () {
  86. return navigator.userAgent.indexOf("Windows NT 6.2") != -1 || /* Windows 8 */
  87. navigator.userAgent.indexOf("Windows NT 6.3") != -1; /* Windows 8.1 */
  88. },
  89. /** Determina con un boolean si nos encontramos en Windows RT */
  90. isWindowsRT : function () {
  91. return MiniApplet.isWindows8() && navigator.userAgent.indexOf("ARM;") != -1;
  92. },
  93. /** Determina con un boolean si estamos en Internet Explorer */
  94. isInternetExplorer : function () {
  95. return !!(navigator.userAgent.match(/MSIE/)) /* Internet Explorer 10 o inferior */
  96. || !!(navigator.userAgent.match(/Trident/) && navigator.userAgent.match(/rv:11/)); /* Internet Explorer 11 o superior */
  97. },
  98. isFirefoxUAM : function () {
  99. return navigator.userAgent.indexOf("UAM") > 0;
  100. },
  101. /** Determina con un boolean si nos encontramos en un entorno Windows 8 en modo "Modern UI".
  102. * Este metodo no es infalible dado que el navegador no ofrece forma de saberlo.
  103. * La comprobacion . */
  104. isWindows8ModernUI : function () {
  105. return MiniApplet.isWindows8() && !MiniApplet.isActivexEnabled() && MiniApplet.isInternetExplorer();
  106. },
  107. /**
  108. * Determina con un boolean si se accede a la web con Chrome
  109. */
  110. isChrome : function () {
  111. return navigator.userAgent.toUpperCase().indexOf("CHROME") != -1 ||
  112. navigator.userAgent.toUpperCase().indexOf("CHROMIUM") != -1;
  113. },
  114. /**
  115. * Determina con un boolean si el navegador es Internet Explorer 10
  116. */
  117. isIE10 : function () {
  118. return navigator.userAgent.toUpperCase().indexOf("MSIE 10.0") != -1;
  119. },
  120. /**
  121. * Determina con un boolean si el navegador es Internet Explorer 11
  122. */
  123. isIE11 : function () {
  124. return !!navigator.userAgent.match(/Trident.*rv 11\./);
  125. },
  126. isURLTooLong : function (url) {
  127. if (MiniApplet.isAndroid()) {
  128. return url.length > MiniApplet.MAX_LONG_ANDROID_URL;
  129. }
  130. else if (MiniApplet.isIOS()) {
  131. return url.length > MiniApplet.MAX_LONG_IOS_URL;
  132. }
  133. else if (MiniApplet.isWindows8()) {
  134. return url.length > MiniApplet.MAX_LONG_WINDOWS8_URL;
  135. }
  136. return false;
  137. },
  138. /** Indica si el navegador detecta Java. Este valor no es completamente fiable, ya que
  139. * Internet Explorer siempre indica que si esta activado. */
  140. isJavaEnabled : function () {
  141. return navigator.javaEnabled();
  142. },
  143. /** Permite habilitar la comprobacion de la hora local contra la hora del servidor y
  144. * establecer un tiempo maximo permitido y el comportamiento si se supera.
  145. * Parametros:
  146. * - checkType: Tipo de comprobacion. Admite los valores CT_NO, CT_RECOMMENDED y CT_OBLIGATORY.
  147. * - maxMillis: Tiempo maximo de desfase en milisegundos.
  148. * Cuando el HTML es local, no se realiza ningun tipo de comprobacion.
  149. * */
  150. checkTime : function (checkType, maxMillis) {
  151. if (checkType == undefined || checkType == null || checkType == MiniApplet.CT_NO
  152. || maxMillis == undefined || maxMillis == null || maxMillis <= 0) {
  153. return;
  154. }
  155. // Hacemos una llamada al servidor para conocer su hora
  156. var xhr = new XMLHttpRequest();
  157. xhr.open('GET', document.URL + '/' + Math.random(), false);
  158. xhr.send();
  159. // Recogemos la hora local, nada mas obtener la respuesta del servidor
  160. var clientDate = new Date();
  161. // Tomamos la hora a partir de la respuesta del servidor. Si esta es 0, estamos en local
  162. var serverDate = new Date(xhr.getResponseHeader("Date"));
  163. if (serverDate == null || serverDate.getTime() == 0) {
  164. // No hacemos nada si estamos en local
  165. return;
  166. }
  167. var delay = Math.abs(clientDate.getTime() - serverDate.getTime());
  168. if (delay > maxMillis) {
  169. if (checkType == MiniApplet.CHECKTIME_RECOMMENDED) {
  170. alert("Se ha detectado un desfase horario entre su sistema y el servidor. Se recomienda que se corrija antes de pulsar Aceptar para continuar." +
  171. "\nHora de su sistema: " + clientDate.toLocaleString() +
  172. "\nHora del servidor: " + serverDate.toLocaleString());
  173. }
  174. else if (checkType == MiniApplet.CHECKTIME_OBLIGATORY) {
  175. MiniApplet.severeTimeDelay = true;
  176. alert("Se ha detectado un desfase horario entre su sistema y el servidor. Debe corregir la hora de su sistema antes de continuar." +
  177. "\nHora de su sistema: " + clientDate.toLocaleString() +
  178. "\nHora del servidor: " + serverDate.toLocaleString());
  179. }
  180. }
  181. },
  182. /** Establece los parametros de configuracion para la correcta seleccion del almacen
  183. * de claves que se debe cargar. */
  184. configureKeyStore : function () {
  185. if (MiniApplet.isFirefoxUAM()) {
  186. if (MiniApplet.CUSTOM_JAVA_ARGUMENTS == null) {
  187. MiniApplet.CUSTOM_JAVA_ARGUMENTS = "";
  188. }
  189. MiniApplet.CUSTOM_JAVA_ARGUMENTS += " -Des.gob.afirma.keystores.mozilla.UseEnvironmentVariables=true";
  190. }
  191. },
  192. /** Carga el MiniApplet. */
  193. cargarMiniApplet : function (base, keystore) {
  194. // Antes que nada, comprobamos que no haya un desfase horario declarado como
  195. // grave.
  196. if (MiniApplet.severeTimeDelay) {
  197. return;
  198. }
  199. // Si estamos claramente en un sistema movil o que no permite la ejecucion de Java,
  200. // cargamos directamente el Cliente JavaScript
  201. if (MiniApplet.isAndroid() || MiniApplet.isIOS() || MiniApplet.isWindowsRT()) {
  202. MiniApplet.cargarAppAfirma(base);
  203. return;
  204. }
  205. // Si estamos en un entorno que permite Java, comprobamos si esta disponible
  206. // y en caso de no estarlo, tambien cargamos el Cliente JavaScript.
  207. if (!MiniApplet.isJavaEnabled()) {
  208. MiniApplet.cargarAppAfirma(base);
  209. return;
  210. }
  211. // Configuramos los argumentos para la seleccion de almacen
  212. MiniApplet.configureKeyStore();
  213. // Incluso si el navegador informa que hay Java, puede no haberlo (Internet Explorer
  214. // siempre dice que hay), asi que cargamos el applet, pero tenemos en cuenta que en
  215. // caso de error debemos cargar el cliente JavaScript
  216. MiniApplet.codeBase = (base != undefined && base != null) ? base : './';
  217. var keystoreConfig = keystore;
  218. if (keystoreConfig == undefined) {
  219. keystoreConfig = null;
  220. }
  221. var attributes = {
  222. 'id': 'miniApplet',
  223. 'name': 'MiniApplet @firma (Gobierno de Espa\u00F1a)',
  224. 'type': 'application/x-java-applet',
  225. 'width': 1,
  226. 'height': 1
  227. };
  228. // Los argumentos de java no llegan al propio applet en las pruebas con Java 6 y 7,
  229. // asi que (salvo los argumentos de carga) vamos a pasarlos como un parametro mas al
  230. // applet para luego establecerlos internamente.
  231. var parameters = {
  232. 'keystore': keystoreConfig,
  233. 'userAgent': window.navigator.userAgent,
  234. 'archive': MiniApplet.codeBase + '/' + MiniApplet.JAR_NAME,
  235. 'code': 'es.gob.afirma.miniapplet.MiniAfirmaApplet',
  236. 'java-vm-args': MiniApplet.JAVA_ARGUMENTS,
  237. 'java_arguments': MiniApplet.JAVA_ARGUMENTS,
  238. 'custom_java_arguments': MiniApplet.CUSTOM_JAVA_ARGUMENTS,
  239. 'codebase_lookup': false,
  240. 'separate_jvm': true,
  241. 'locale': MiniApplet.selectedLocale
  242. };
  243. MiniApplet.loadMiniApplet(attributes, parameters);
  244. MiniApplet.clienteFirma = document.getElementById("miniApplet");
  245. // Si no esta definido el cliente es porque se ha intentado cargar el applet
  246. // y no se ha podido, asi que se usara la aplicacion nativa
  247. if (MiniApplet.clienteFirma == null) {
  248. MiniApplet.cargarAppAfirma(MiniApplet.codeBase);
  249. }
  250. },
  251. sign : function (dataB64, algorithm, format, params, successCallback, errorCallback) {
  252. this.forceLoad();
  253. if (MiniApplet.clientType == MiniApplet.TYPE_APPLET) {
  254. try {
  255. this.setData(dataB64);
  256. if (successCallback == undefined || successCallback == null) {
  257. return this.buildData(MiniApplet.clienteFirma.sign(algorithm, format, params));
  258. }
  259. successCallback(this.buildData(MiniApplet.clienteFirma.sign(algorithm, format, params)));
  260. } catch(e) {
  261. if (errorCallback == undefined || errorCallback == null) {
  262. throw e;
  263. }
  264. errorCallback(MiniApplet.clienteFirma.getErrorType(), MiniApplet.clienteFirma.getErrorMessage());
  265. }
  266. }
  267. else if (MiniApplet.clientType == MiniApplet.TYPE_JAVASCRIPT) {
  268. errorCallback('Haltr','Please enable Java')
  269. // MiniApplet.clienteFirma.sign(dataB64, algorithm, format, params, successCallback, errorCallback);
  270. }
  271. },
  272. coSign : function (signB64, dataB64, algorithm, format, params, successCallback, errorCallback) {
  273. this.forceLoad();
  274. if (MiniApplet.clientType == MiniApplet.TYPE_APPLET) {
  275. try {
  276. this.setData(signB64);
  277. if (successCallback == undefined || successCallback == null) {
  278. return this.buildData(MiniApplet.clienteFirma.coSign(dataB64, algorithm, format, params));
  279. }
  280. successCallback(this.buildData(MiniApplet.clienteFirma.coSign(dataB64, algorithm, format, params)));
  281. } catch(e) {
  282. if (errorCallback == undefined || errorCallback == null) {
  283. throw e;
  284. }
  285. errorCallback(MiniApplet.clienteFirma.getErrorType(), MiniApplet.clienteFirma.getErrorMessage());
  286. }
  287. }
  288. else if (MiniApplet.clientType == MiniApplet.TYPE_JAVASCRIPT) {
  289. MiniApplet.clienteFirma.coSign(signB64, dataB64, algorithm, format, params, successCallback, errorCallback);
  290. }
  291. },
  292. counterSign : function (signB64, algorithm, format, params, successCallback, errorCallback) {
  293. this.forceLoad();
  294. if (MiniApplet.clientType == MiniApplet.TYPE_APPLET) {
  295. try {
  296. this.setData(signB64);
  297. if (successCallback == undefined || successCallback == null) {
  298. return this.buildData(MiniApplet.clienteFirma.counterSign(algorithm, format, params));
  299. }
  300. successCallback(this.buildData(MiniApplet.clienteFirma.counterSign(algorithm, format, params)));
  301. } catch(e) {
  302. if (errorCallback == undefined || errorCallback == null) {
  303. throw e;
  304. }
  305. errorCallback(MiniApplet.clienteFirma.getErrorType(), MiniApplet.clienteFirma.getErrorMessage());
  306. }
  307. }
  308. else if (MiniApplet.clientType == MiniApplet.TYPE_JAVASCRIPT) {
  309. MiniApplet.clienteFirma.counterSign(signB64, algorithm, format, params, successCallback, errorCallback);
  310. }
  311. },
  312. getBase64FromText : function (plainText, charset) {
  313. this.forceLoad();
  314. return MiniApplet.clienteFirma.getBase64FromText(plainText, charset);
  315. },
  316. getTextFromBase64 : function (dataB64, charset) {
  317. this.forceLoad();
  318. return MiniApplet.clienteFirma.getTextFromBase64(dataB64, charset);
  319. },
  320. saveDataToFile : function (dataB64, title, fileName, extension, description) {
  321. this.forceLoad();
  322. if (MiniApplet.clientType == MiniApplet.TYPE_APPLET) {
  323. this.setData(dataB64);
  324. return MiniApplet.clienteFirma.saveDataToFile(title, fileName, extension, description);
  325. }
  326. else if (MiniApplet.clientType == MiniApplet.TYPE_JAVASCRIPT) {
  327. return MiniApplet.clienteFirma.saveDataToFile(dataB64, title, fileName, extension, description);
  328. }
  329. return null;
  330. },
  331. getFileNameContentBase64 : function (title, extensions, description, filePath) {
  332. this.forceLoad();
  333. return this.buildData(MiniApplet.clienteFirma.getFileNameContentBase64(title, extensions, description, filePath));
  334. },
  335. getMultiFileNameContentBase64 : function (title, extensions, description, filePath) {
  336. this.forceLoad();
  337. return this.buildData(MiniApplet.clienteFirma.getMultiFileNameContentBase64(title, extensions, description, filePath));
  338. },
  339. echo : function () {
  340. this.forceLoad();
  341. return MiniApplet.clienteFirma.echo();
  342. },
  343. setStickySignatory : function (sticky) {
  344. this.forceLoad();
  345. return MiniApplet.clienteFirma.setStickySignatory(sticky);
  346. },
  347. setLocale : function (locale) {
  348. MiniApplet.selectedLocale = locale;
  349. },
  350. getErrorMessage : function () {
  351. this.forceLoad();
  352. return MiniApplet.clienteFirma.getErrorMessage();
  353. },
  354. getErrorType : function () {
  355. this.forceLoad();
  356. return MiniApplet.clienteFirma.getErrorType();
  357. },
  358. getCurrentLog : function () {
  359. this.forceLoad();
  360. return " === JAVASCRIPT INFORMATION === " +
  361. "\nnavigator.appCodeName: " + navigator.appCodeName +
  362. "\nnavigator.appName: " + navigator.appName +
  363. "\nnavigator.appVersion: " + navigator.appVersion +
  364. "\nnavigator.platform: " + navigator.platform +
  365. "\nnavigator.userAgent: " + navigator.userAgent+
  366. "\nnavigator.javaEnabled(): " + navigator.javaEnabled() +
  367. "\nscreen.width: " + (window.screen ? screen.width : 0) +
  368. "\nscreen.height: " + (window.screen ? screen.height : 0) +
  369. "\n\n === CLIENTE LOG === \n" +
  370. MiniApplet.clienteFirma.getCurrentLog();
  371. },
  372. setServlets : function (storageServlet, retrieverServlet) {
  373. MiniApplet.storageServletAddress = storageServlet;
  374. MiniApplet.retrieverServletAddress = retrieverServlet;
  375. if (MiniApplet.clienteFirma && MiniApplet.clienteFirma.setServlets) {
  376. MiniApplet.clienteFirma.setServlets(storageServlet, retrieverServlet);
  377. }
  378. },
  379. /*************************************************************
  380. * FUNCIONES PARA EL DESPLIEGUE DEL APPLET *
  381. **************************************************************/
  382. loadMiniApplet : function (attributes, parameters) {
  383. // Internet Explorer (a excepcion de la version 10) se carga mediante un
  384. // elemento <object>. El resto con un <embed>.
  385. if (MiniApplet.isInternetExplorer()) { // && !MiniApplet.isIE10()) {
  386. var appletTag = "<object classid='clsid:8AD9C840-044E-11D1-B3E9-00805F499D93' width='" + attributes["width"] + "' height='" + attributes["height"] + "' id='" + attributes["id"] + "'>";
  387. if (attributes != undefined && attributes != null) {
  388. for (var attribute in attributes) {
  389. appletTag += "<param name='" + attribute + "' value='" + attributes[attribute] + "' />";
  390. }
  391. }
  392. if (parameters != undefined && parameters != null) {
  393. for (var parameter in parameters) {
  394. appletTag += "<param name='" + parameter + "' value='" + parameters[parameter] + "' />";
  395. }
  396. }
  397. appletTag += "</object>";
  398. // Al agregar con append() estos nodos no se carga automaticamente el applet en IE10 e inferiores, así que
  399. // hay que usar document.write() o innerHTML. Para asegurarnos de no pisar HTML previo, crearemos un <div>
  400. // en la pagina, lo recogeremos e insertaremos dentro suyo el codigo del applet.
  401. var divElem = document.createElement("div");
  402. var idAtt = document.createAttribute("id");
  403. idAtt.value = 'divAfirmaApplet';
  404. divElem.setAttributeNode(idAtt);
  405. document.body.appendChild(divElem);
  406. document.getElementById("divAfirmaApplet").innerHTML = appletTag;
  407. }
  408. else {
  409. var embed = document.createElement("embed");
  410. if (attributes != undefined && attributes != null) {
  411. for (var attribute in attributes) {
  412. var att = document.createAttribute(attribute);
  413. att.value = attributes[attribute];
  414. try {
  415. embed.setAttributeNode(att);
  416. }
  417. catch (e) {
  418. // Probamos este como alternativa en caso de error. Caso detectado en:
  419. // - IE10 sin modo de compabilidad con el Document Mode de IE7.
  420. // - Firefox en Mac OS X
  421. // Este intento no soluciona el error, pero evita que se propague
  422. embed.setAttribute(attribute, attributes[attribute]);
  423. }
  424. }
  425. }
  426. if (parameters != undefined && parameters != null) {
  427. for (var parameter in parameters) {
  428. var att = document.createAttribute(parameter);
  429. att.value = parameters[parameter];
  430. embed.setAttributeNode(att);
  431. }
  432. }
  433. document.body.appendChild(embed);
  434. }
  435. },
  436. /**
  437. * Establece los datos que debera procesar el applet MiniApplet.
  438. */
  439. forceLoad : function () {
  440. // Antes que nada, comprobamos que no haya un desfase horario declarado como
  441. // grave.
  442. if (MiniApplet.severeTimeDelay) {
  443. return;
  444. }
  445. if (MiniApplet.clientType == null) {
  446. MiniApplet.clienteFirma = document.getElementById("miniApplet");
  447. try {
  448. MiniApplet.clienteFirma.echo();
  449. MiniApplet.clientType = MiniApplet.TYPE_APPLET;
  450. } catch (e) {
  451. MiniApplet.cargarAppAfirma(MiniApplet.codeBase);
  452. }
  453. MiniApplet.setServlets(MiniApplet.storageServletAddress, MiniApplet.retrieverServletAddress);
  454. }
  455. },
  456. /**
  457. * Establece los datos que debera procesar el applet MiniApplet.
  458. */
  459. setData : function (dataB64) {
  460. if (dataB64 == null) {
  461. return;
  462. }
  463. else if (dataB64.length <= MiniApplet.BUFFER_SIZE) {
  464. MiniApplet.clienteFirma.addData(dataB64);
  465. }
  466. else {
  467. MiniApplet.clienteFirma.addData(dataB64.substring(0, MiniApplet.BUFFER_SIZE));
  468. this.setData(dataB64.substring(MiniApplet.BUFFER_SIZE));
  469. }
  470. },
  471. /**
  472. * Construye el resultado de una funcion a partir de los trozos en la que esta los divide.
  473. */
  474. buildData : function (dataB64) {
  475. var buffer = dataB64;
  476. var chunk = MiniApplet.clienteFirma.getRemainingData();
  477. while(chunk != MiniApplet.EOF) {
  478. buffer += chunk;
  479. chunk = MiniApplet.clienteFirma.getRemainingData();
  480. }
  481. return buffer;
  482. },
  483. /**************************************************************
  484. **************************************************************
  485. **************************************************************
  486. **************************************************************
  487. * FUNCIONES DEL CLIENTE JAVASCRIPT *
  488. **************************************************************
  489. **************************************************************
  490. **************************************************************
  491. **************************************************************/
  492. /**
  493. * Establece el objeto que simula ser el Applet de firma en sistemas en los que no se
  494. * soportan los applets.
  495. */
  496. cargarAppAfirma : function (clientAddress) {
  497. document.miniapplet = new MiniApplet.AppAfirmaJS(clientAddress);
  498. MiniApplet.clienteFirma = document.miniapplet;
  499. MiniApplet.clientType = MiniApplet.TYPE_JAVASCRIPT;
  500. },
  501. /**
  502. * Objeto JavaScript que va a reemplazar al cliente de firma en los entornos en los que
  503. * no pueden ejecutarse applets.
  504. */
  505. AppAfirmaJS : function (clientAddress) {
  506. var UnsupportedOperationException = "java.lang.UnsupportedOperationException";
  507. /**
  508. * Atributos para la configuracion del objeto sustituto del applet Java de firma
  509. */
  510. this.errorMessage = '';
  511. this.errorType = '';
  512. if (clientAddress.indexOf("://") != -1 && clientAddress.indexOf("/", clientAddress.indexOf("://") + 3) != -1) {
  513. var servletsBase = clientAddress.substring(0, clientAddress.indexOf("/", clientAddress.indexOf("://") + 3));
  514. this.retrieverServletAddress = servletsBase + "/SignatureRetrieverServer/RetrieveService";
  515. this.storageServletAddress = servletsBase + "/SignatureStorageServer/StorageService";
  516. } else {
  517. this.retrieverServletAddress = clientAddress + "/SignatureRetrieverServer/RetrieveService";
  518. this.storageServletAddress = clientAddress + "/SignatureStorageServer/StorageService";
  519. }
  520. /**
  521. * Inicia el proceso de firma electronica.
  522. * Implementada en el applet Java de firma
  523. */
  524. this.sign = function(dataB64, algorithm, format, extraParams, successCallback, errorCallback) {
  525. this.signOperation("sign", dataB64, algorithm, format, extraParams, successCallback, errorCallback);
  526. };
  527. /**
  528. * Inicia el proceso de cofirma de una firma electr&oacute;nica.
  529. * Implementada en el applet Java de firma.
  530. */
  531. this.coSign = function(signB64, dataB64, algorithm, format, extraParams, successCallback, errorCallback) {
  532. this.signOperation("cosign", signB64, algorithm, format, extraParams, successCallback, errorCallback);
  533. };
  534. /**
  535. * Inicia el proceso de contrafirma de una firma electr&oacute;nica.
  536. * Implementada en el applet Java de firma.
  537. */
  538. this.counterSign = function(signB64, algorithm, format, extraParams, successCallback, errorCallback) {
  539. this.signOperation("countersign", signB64, algorithm, format, extraParams, successCallback, errorCallback);
  540. };
  541. /**
  542. * Realiza una operacion de firma/multifirma.
  543. * @param signId Identificador de la operacion a realizar (sign, cosign y countersign).
  544. * @param dataB64 Datos o firma en base 64.
  545. * @param algorithm Algoritmo de firma.
  546. * @param format Formato de firma.
  547. * @param extraParams Par&aacute;metros para la configuraci&oacute;n de la operaci&oacute;n.
  548. * @param successCallback M&eacute;todo a ejecutar en caso de &eacute;xito.
  549. * @param errorCallback M&eacute;todo a ejecutar en caso de error.
  550. */
  551. this.signOperation = function(signId, dataB64, algorithm, format, extraParams, successCallback, errorCallback) {
  552. if (dataB64 == undefined || dataB64 == "") {
  553. dataB64 = null;
  554. }
  555. if (dataB64 != null) {
  556. dataB64 = dataB64.replace(/\+/g, "-").replace(/\//g, "_");
  557. }
  558. if (isPolicyConfigurated(extraParams)) {
  559. extraParams = expandPolicy(format, extraParams);
  560. }
  561. var idSession = generateNewIdSession();
  562. var cipherKey = generateCipherKey();
  563. var i = 0;
  564. var params = new Array();
  565. if (signId != null && signId != undefined) { params[i++] = {key:"op", value:encodeURIComponent(signId)}; }
  566. if (idSession != null && idSession != undefined) { params[i++] = {key:"id", value:encodeURIComponent(idSession)}; }
  567. if (cipherKey != null && cipherKey != undefined) { params[i++] = {key:"key", value:encodeURIComponent(cipherKey)}; }
  568. if (this.storageServletAddress != null && this.storageServletAddress != undefined) { params[i++] = {key:"stservlet", value:this.storageServletAddress}; }
  569. if (format != null && format != undefined) { params[i++] = {key:"format", value:encodeURIComponent(format)}; }
  570. if (algorithm != null && algorithm != undefined) { params[i++] = {key:"algorithm", value:encodeURIComponent(algorithm)}; }
  571. if (extraParams != null && extraParams != undefined) { params[i++] = {key:"properties", value:encodeURIComponent(Base64.encode(extraParams))}; }
  572. if (MiniApplet.isWindows8()) { params[i++] = {key:"metro", value:MiniApplet.isWindows8ModernUI() ? "true" : "false"}; }
  573. if (dataB64 != null) { params[i++] = {key:"dat", value:encodeURIComponent(dataB64)}; }
  574. var url = this.buildUrl(signId, params);
  575. // Si la URL es muy larga, realizamos un preproceso para que los datos se suban al
  576. // servidor y la aplicacion nativa los descargue, en lugar de pasarlos directamente
  577. if (MiniApplet.isURLTooLong(url)) {
  578. if (this.storageServletAddress == null || this.storageServletAddress == undefined) {
  579. throwException("java.lang.IllegalArgumentException", "No se ha indicado la direccion del servlet para el guardado de datos");
  580. return;
  581. }
  582. var fileId = this.preProccessData(cipherKey, this.storageServletAddress, signId, params);
  583. if (!fileId) {
  584. throwException("java.net.UnknownHostException", "No se han podido enviar los datos a la aplicacion de firma");
  585. return;
  586. }
  587. url = this.buildUrlWithoutData(signId, fileId, this.retrieverServletAddress, cipherKey);
  588. if (MiniApplet.isURLTooLong(url)) {
  589. throwException("java.lang.IllegalArgumentException", "La URL de invocacion al servicio de firma es demasiado larga.");
  590. return;
  591. }
  592. }
  593. this.execAppIntent(url, idSession, cipherKey, successCallback, errorCallback);
  594. };
  595. /**
  596. * Convierte texto plano en texto base 64.
  597. * Implementada en el applet Java de firma.
  598. */
  599. this.getBase64FromText = function(plainText, charset) {
  600. return Base64.encode(plainText);
  601. };
  602. /**
  603. * Convierte texto base 64 en texto plano.
  604. * Implementada en el applet Java de firma.
  605. */
  606. this.getTextFromBase64 = function(base64Text, charset) {
  607. return Base64.decode(base64Text);
  608. };
  609. /**
  610. * Guardado de datos en disco. Se realiza mediante la invocacion de una app nativa.
  611. */
  612. this.saveDataToFile = function(dataB64, title, filename, extension, description) {
  613. if (dataB64 != undefined && dataB64 != null && dataB64 != "") {
  614. dataB64 = dataB64.replace(/\+/g, "-").replace(/\//g, "_");
  615. }
  616. var idSession = generateNewIdSession();
  617. var cipherKey = generateCipherKey();
  618. var i = 0;
  619. var params = new Array();
  620. params[i++] = {key:"op", value:"save"};
  621. if (idSession != null && idSession != undefined) { params[i++] = {key:"id", value:encodeURIComponent(idSession)}; }
  622. if (cipherKey != null && cipherKey != undefined) { params[i++] = {key:"key", value:encodeURIComponent(cipherKey)}; }
  623. if (this.storageServletAddress != null && this.storageServletAddress != undefined) { params[i++] = {key:"stservlet", value:this.storageServletAddress}; }
  624. if (title != null && title != undefined) { params[i++] = {key:"title", value:encodeURIComponent(title)}; }
  625. if (filename != null && filename != undefined) { params[i++] = {key:"filename", value:encodeURIComponent(filename)}; }
  626. if (extension != null && extension != undefined) { params[i++] = {key:"extension", value:encodeURIComponent(extension)}; }
  627. if (description != null && description != undefined) { params[i++] = {key:"description", value:encodeURIComponent(description)}; }
  628. if (MiniApplet.isWindows8()) { params[i++] = {key:"metro", value:MiniApplet.isWindows8ModernUI() ? "true" : "false"}; }
  629. if (dataB64 != null && dataB64 != undefined && dataB64 != "") { params[i++] = {key:"dat", value:encodeURIComponent(dataB64)}; }
  630. var url = this.buildUrl("save", params);
  631. // Si la URL es muy larga, realizamos un preproceso para que los datos se suban al
  632. // servidor y la aplicacion nativa los descargue, en lugar de pasarlos directamente
  633. if (MiniApplet.isURLTooLong(url)) {
  634. if (this.storageServletAddress == null || this.storageServletAddress == undefined) {
  635. throwException("java.lang.IllegalArgumentException", "No se ha indicado la direccion del servlet para el guardado de datos");
  636. return;
  637. }
  638. var fileId = this.preProccessData(cipherKey, this.storageServletAddress, "save", params);
  639. if (!fileId) {
  640. throwException("java.net.UnknownHostException", "No se han podido enviar los datos a la aplicacion de firma");
  641. return;
  642. }
  643. url = this.buildUrlWithoutData("save", fileId, this.retrieverServletAddress, cipherKey);
  644. if (MiniApplet.isURLTooLong(url)) {
  645. throwException("java.lang.IllegalArgumentException", "La URL de invocacion al servicio de firma es demasiado larga. No se soportan tantas propiedades de configuracion.");
  646. return;
  647. }
  648. }
  649. this.execAppIntent(url, idSession, cipherKey);
  650. };
  651. /**
  652. * Carga de un fichero. Operacion no soportada.
  653. * Implementada en el applet Java de firma.
  654. */
  655. this.getFileNameContentBase64 = function(title, extensions, description) {
  656. this.throwException(UnsupportedOperationException, "La operacion de carga de ficheros no esta soportada");
  657. };
  658. /**
  659. * Carga de multiples ficheros. Operacion no soportada.
  660. * Implementada en el applet Java de firma.
  661. */
  662. this.getMultiFileNameContentBase64 = function(title, extensions, description) {
  663. this.throwException(UnsupportedOperationException, "La operacion de carga de multiples ficheros no esta soportada");
  664. };
  665. /**
  666. * Funcion para la comprobacion de existencia del objeto. No hace nada.
  667. * Implementada en el applet Java de firma.
  668. */
  669. this.echo = function() {
  670. return "Cliente JavaScript";
  671. };
  672. /**
  673. * No hace nada.
  674. * Implementada en el applet Java de firma.
  675. */
  676. this.setStickySignatory = function(sticky) {
  677. // No hace nada
  678. };
  679. /**
  680. * Recupera el mensaje de error asociado al ultimo error capturado.
  681. * Implementada en el applet Java de firma.
  682. */
  683. this.getErrorMessage = function() {
  684. return this.errorMessage;
  685. };
  686. /**
  687. * Recupera el tipo del ultimo error capturado.
  688. * Implementada en el applet Java de firma.
  689. */
  690. this.getErrorType = function() {
  691. return this.errorType;
  692. };
  693. /**
  694. * Recupera el log de la aplicacion. Actualmente, el log solo esta
  695. * disponible en el applet, no en las aplicacion moviles.
  696. */
  697. this.getCurrentLog = function() {
  698. return "Applet no cargado";
  699. };
  700. /**
  701. * Funcion para identificar el tipo de objeto del Cliente (javascript, applet,...).
  702. */
  703. this.getType = function() {
  704. return "javascript";
  705. };
  706. /**
  707. * Establece las rutas de los servlets encargados de almacenar y recuperar las firmas de los dispositivos moviles.
  708. */
  709. this.setServlets = function(storageServlet, retrieverServlet) {
  710. this.storageServletAddress = storageServlet;
  711. this.retrieverServletAddress = retrieverServlet;
  712. };
  713. /**
  714. * Establece el error indicado como error interno y lanza una excepcion.
  715. */
  716. this.throwException = function(type, message) {
  717. this.errorType = type;
  718. this.errorMessage = message;
  719. throw new Exception();
  720. };
  721. // Constants
  722. var MAX_NUMBER = 2147483648;
  723. // Pure javascript functions
  724. function zeroFill(number, width) {
  725. width -= number.toString().length;
  726. if (width > 0) {
  727. return new Array(width + (/\./.test(number) ? 2 : 1)).join('0')
  728. + number;
  729. }
  730. return number + ""; // Always return a string
  731. }
  732. /**
  733. * Funciones auxiliares del objeto JS del cliente de firma.
  734. **/
  735. function generateNewIdSession() {
  736. return zeroFill(Math.floor((Math.random() * MAX_NUMBER) + 1), 12);
  737. }
  738. var EXPAND_POLICIY_KEY_AND_VALUE = "expPolicy=FirmaAGE";
  739. /**
  740. * Identifica si debe expandirse la propiedad de politica de firma.
  741. * @param config Configuracion de la firma.
  742. * @returns Indica con true si debe expandirse el parametro de politica, false en caso contrario.
  743. */
  744. function isPolicyConfigurated(config) {
  745. return (config != undefined && config != null) ?
  746. config.indexOf(EXPAND_POLICIY_KEY_AND_VALUE) > -1 : false;
  747. }
  748. /**
  749. * Expande la variable de firma politica de firma si la encuentra en los extra params.
  750. **/
  751. function expandPolicy(format, config) {
  752. var expandedPolicy = "";
  753. if (compareFormats(format, "CAdES")) {
  754. expandedPolicy = "policyIdentifier=urn:oid:2.16.724.1.3.1.1.2.1.8\n" +
  755. "policyQualifier=http://administracionelectronica.gob.es/es/ctt/politicafirma/politica_firma_AGE_v1_8.pdf\n" +
  756. "policyIdentifierHashAlgorithm=http://www.w3.org/2000/09/xmldsig#sha1\n" +
  757. "policyIdentifierHash=7SxX3erFuH31TvAw9LZ70N7p1vA=";
  758. }
  759. else if (compareFormats(format, "XAdES")) {
  760. expandedPolicy = "policyIdentifier=urn:oid:2.16.724.1.3.1.1.2.1.8\n" +
  761. "policyQualifier=http://administracionelectronica.gob.es/es/ctt/politicafirma/politica_firma_AGE_v1_8.pdf\n" +
  762. "policyIdentifierHashAlgorithm=http://www.w3.org/2000/09/xmldsig#sha1\n" +
  763. "policyIdentifierHash=V8lVVNGDCPen6VELRD1Ja8HARFk=";
  764. }
  765. // else if (compareFormats(format, "PAdES") || compareFormats(format, "PDF")) {
  766. // // NO DISPONIBLE HASTA LA VERSION 1.9 DE LA POLITICA
  767. // expandedPolicy = "policyIdentifier=urn:oid:2.16.724.1.3.1.1.2.1.8\n" +
  768. // "policyQualifier=http://administracionelectronica.gob.es/es/ctt/politicafirma/politica_firma_AGE_v1_8.pdf\n" +
  769. // "policyIdentifierHashAlgorithm=http://www.w3.org/2000/09/xmldsig#sha1\n" +
  770. // "policyIdentifierHash=7SxX3erFuH31TvAw9LZ70N7p1vA=";
  771. // }
  772. if (expandedPolicy != "") {
  773. config = config.replace(EXPAND_POLICIY_KEY_AND_VALUE, expandedPolicy);
  774. }
  775. return config;
  776. }
  777. /**
  778. * Compara que un nombre de formato sea equivalente a un formato de firma monofasico.
  779. * Por ejemplo, que XAdEStri sea igual a XAdES.
  780. **/
  781. function compareFormats(format, supportedFormat) {
  782. format = format.toUpperCase();
  783. supportedFormat = supportedFormat.toUpperCase();
  784. return format == supportedFormat ||
  785. (format.length > supportedFormat.length &&
  786. format.substr(0, supportedFormat.length) == supportedFormat);
  787. }
  788. /**
  789. * Invoca un Intent con la operacion seleccionada, la configuraci\u00F3n establecida y las campos del
  790. * formulario pasado como parametro. Si se define un callback para tratar el caso de exito o error de
  791. * la operacion, se intentara descargar el resultado devuelto por la app del servidor intermedio de
  792. * comunicacion.
  793. *
  794. * intentURL: URL para la invocacion del Cliente JavaScript
  795. * idSession: Identificador de la sesi\u00F3n para la recuperaci\u00F3n del resultado.
  796. * cipherKey: Clave de cifrado para la respuesta del servidor.
  797. * successCallback: Actuaci\u00F3n a realizar cuando se recupera el resultado de la operaci&oacute;n.
  798. * errorCallback: Actuaci\u00F3n a realizar cuando ocurre un error al recuperar el resultado.
  799. */
  800. this.execAppIntent = function (intentURL, idSession, cipherKey, successCallback, errorCallback) {
  801. // Invocamos al cliente de firma movil.
  802. this.openUrl(intentURL);
  803. if (successCallback != null || errorCallback != null) {
  804. if (idSession != null && idSession != undefined &&
  805. ((successCallback != undefined && successCallback != null) ||
  806. (errorCallback != undefined && errorCallback != null))) {
  807. this.getStoredFileFromServlet(idSession, this.retrieverServletAddress, cipherKey, successCallback, errorCallback);
  808. }
  809. }
  810. };
  811. /**
  812. * Construye una URL para la invocaci&oacute;n del Cliente @firma nativo.
  813. *
  814. * op: Funcion a invocar en el cliente nativo.
  815. * params: Par\u00E1metros para la configuraci\u00F3n de la operaci\u00F3n.
  816. */
  817. this.buildUrl = function(op, params) {
  818. // Operacion seleccionada
  819. var intentURL = this.getProtocol() + '://' + op + '?';
  820. if (params != null && params != undefined) {
  821. for (var i = 0; i < params.length; i++) {
  822. intentURL += (i != 0 ? '&' : '') + params[i].key + '=' + params[i].value;
  823. }
  824. }
  825. return intentURL;
  826. };
  827. this.getProtocol = function () {
  828. // En Windows 8, siempre usaremos el modo "afirmametro", ya que por ahora
  829. // la aplicacion con el protocolo "afirma" no hay otro disponible
  830. if (MiniApplet.isWindows8()) {
  831. return "afirmametro";
  832. }
  833. return "afirma";
  834. };
  835. /**
  836. * Generan un XML con los datos de configuracion de la operacion indicada,
  837. * los cifra y lo envia a un servidor para su descarga.
  838. * @param cipherKey Clave de cifrado. Si no se indica, no se cifra.
  839. * @param storageServletAddress URL del servlet que almacena.
  840. * @param op Operacion que se configura.
  841. * @param params Parametros de configuracion de la operacion
  842. * @returns El identificador con el que se ha guardado el fichero en servidor o false
  843. * si se produjo algun error.
  844. */
  845. this.preProccessData = function (cipherKey, storageServletAddress, op, params) {
  846. // Identificador del fichero (equivalente a un id de sesion) del que deben recuperarse los datos
  847. var fileId = generateNewIdSession();
  848. var httpRequest = getHttpRequest();
  849. if (!httpRequest) {
  850. this.throwException("java.lang.Exception", "Su navegador no permite preprocesar los datos que desea tratar");
  851. }
  852. var cipheredDataB64 = cipher(buildXML(op, params), cipherKey);
  853. httpRequest.open("POST", storageServletAddress, false);
  854. httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  855. try {
  856. httpRequest.send("op=put&v=1_0&id=" + fileId + "&dat=" + cipheredDataB64);
  857. }
  858. catch(e) {
  859. this.errorMessage = "No se pudo conectar con el servidor remoto";
  860. this.errorType = "java.io.IOException";
  861. }
  862. if (httpRequest.readyState==4 && httpRequest.status==200) {
  863. return fileId;
  864. }
  865. return false;
  866. };
  867. /**
  868. * Construye un XML con lo valores del array de parametros proporcionado.
  869. * @param op Operacion que configuramos
  870. * @param params Array con los parametros del array.
  871. * @returns XML.
  872. */
  873. function buildXML (op, params) {
  874. op = (op == null ? "op" : op);
  875. var xml = '<' + op +'>';
  876. for (var i = 0; i < params.length; i++) {
  877. xml += '<e k="' + params[i].key + '" v="' + params[i].value + '"/>';
  878. }
  879. return Base64.encode(xml + '</' + op + '>');
  880. };
  881. /**
  882. * Crea una URL a partir de los parametros introducidos para la invocaci&oacute;n de
  883. * una app nativa para que descargue la configuracion de la operaci&oacute;n a realizar.
  884. * @param op Codigo de la operacion a la que tiene que invocar la URL.
  885. * @param id Identificador para la descarga.
  886. * @param rtServlet Servlet para la descarga de la configuraci&oacute;n.
  887. * @param cipherKey Clave para el descifrado.
  888. * @returns URL para la llamada a la app con los datos necesarios para que descargue
  889. * la configuraci&oacute;n de la operaci&oacute;n a realizar.
  890. */
  891. this.buildUrlWithoutData = function (op, id, rtServlet, cipherKey) {
  892. var j = 0;
  893. var newParams = new Array();
  894. newParams[j++] = {key:"fileid", value:id};
  895. if (rtServlet != null || rtServlet != undefined) {
  896. newParams[j++] = {key:"rtservlet", value:rtServlet};
  897. }
  898. if (cipherKey != null || cipherKey != undefined) {
  899. newParams[j++] = {key:"key", value:cipherKey};
  900. }
  901. return this.buildUrl(op, newParams);
  902. };
  903. /**
  904. * Llama a la aplicacion de firma a traves de la URL de invocacion sin que afecte
  905. * a la pagina que se esta mostrando.
  906. * @param url URL de invocacion.
  907. */
  908. this.openUrl = function (url) {
  909. // Usamos document.location porque tiene mejor soporte por los navegadores que
  910. // window.location que es el mecanismo estandar
  911. if (MiniApplet.isChrome()) {
  912. document.location = url;
  913. }
  914. else {
  915. var iframeElem = document.createElement("iframe");
  916. var srcAttr = document.createAttribute("src");
  917. srcAttr.value = url;
  918. iframeElem.setAttributeNode(srcAttr);
  919. var heightAttr = document.createAttribute("height");
  920. heightAttr.value = 1;
  921. iframeElem.setAttributeNode(heightAttr);
  922. var widthAttr = document.createAttribute("width");
  923. widthAttr.value = 1;
  924. iframeElem.setAttributeNode(widthAttr);
  925. var seamlessAttr = document.createAttribute("seamless");
  926. seamlessAttr.value = "seamless";
  927. iframeElem.setAttributeNode(seamlessAttr);
  928. document.body.appendChild(iframeElem);
  929. }
  930. };
  931. var iterations = 0;
  932. /**
  933. * Ejecuta el metodo de error si el html recuperado es tal o el metodo de exito si no lo es,
  934. * en cuyo caso previamente descifrara el resultado.
  935. * @param html Resultado obtenido.
  936. * @param cipherKey Clave para el descifrado del resultado si no es un error.
  937. * @param successCallback Metodo a ejecutar en caso de exito.
  938. * @param errorCallback Metodo a ejecutar en caso de error.
  939. * @returns Devuelve true si se ha fallado pero se puede volver a reintentar, false en caso de
  940. * error determinante o exito.
  941. */
  942. this.successResponseFunction = function(html, cipherKey, successCallback, errorCallback) {
  943. // Si se obtiene el mensaje de error de que el identificador no existe, seguimos intentandolo
  944. if (html.substr(0, 6).toLowerCase() == "err-06") {
  945. return true;
  946. }
  947. // Si se obtiene otro mensaje de error, se deja de intentar y se ejecuta la funcion callback de error
  948. if (html.substr(0, 4).toLowerCase() == "err-" && html.indexOf(":=") != -1) {
  949. this.errorMessage = html.substring(html.indexOf(":=") + 2);
  950. this.errorType = "java.lang.Exception";
  951. errorCallback(this.errorType, this.errorMessage);
  952. return false;
  953. }
  954. // Si no se obtuvo un error y se definio una clave de cifrado privada, desciframos.
  955. // Los datos cifrados van precedidos por la cantidad de caracteres agregados manualmente al final para
  956. // cumplir con los requisitos de padding del algoritmo de cifrado. Este numero se separa de la cadena
  957. // cifrada con el caracter '.'. Devuelve el resultado del descifrado en Base64.
  958. if (cipherKey != undefined && cipherKey != null) {
  959. html = decipher(html, cipherKey);
  960. }
  961. // Ejecutamos la funcion callback de exito y notificamos que se dejen de realizar peticiones
  962. successCallback(html);
  963. return false;
  964. };
  965. this.errorResponseFunction = function(type, message, errorCallback) {
  966. this.errorType = (type != null && type.length > 0) ?
  967. type : "java.lang.Exception";
  968. this.errorMessage = (message != null && message.length > 0) ?
  969. message : "No se ha podido extablecer la comunicaci\u00F3n entre la aplicaci\u00F3n de firma y la p\u00E1gina web";
  970. errorCallback(this.errorType, this.errorMessage);
  971. };
  972. this.getStoredFileFromServlet = function (idDocument, servletAddress, cipherKey, successCallback, errorCallback) {
  973. var httpRequest = getHttpRequest();
  974. if (!httpRequest) {
  975. this.throwException("java.lang.Exception", "Su navegador no permite obtener el resulado de la operaci\u00F3n");
  976. }
  977. iterations = 0;
  978. setTimeout(retrieveRequest, 4000, httpRequest, servletAddress, "op=get&v=1_0&id=" + idDocument + "&it=0", cipherKey, successCallback, errorCallback);
  979. };
  980. var NUM_MAX_ITERATIONS = 15;
  981. function retrieveRequest(httpRequest, url, params, cipherKey, successCallback, errorCallback) {
  982. // Contamos la nueva llamada al servidor
  983. if (iterations > NUM_MAX_ITERATIONS) {
  984. MiniApplet.clienteFirma.errorResponseFunction("java.util.concurrent.TimeoutException", "El tiempo para la recepcion de la firma por la pagina web ha expirado", errorCallback);
  985. return;
  986. }
  987. iterations++;
  988. //TODO: Separar parametros
  989. httpRequest.open("POST", url, false);
  990. httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
  991. try {
  992. httpRequest.send(params);
  993. }
  994. catch(e) {
  995. // Error en la llamada para al recuperacion del resultado. No lo encuentra o problema
  996. // de tipo cross-domain
  997. MiniApplet.clienteFirma.errorResponseFunction("java.lang.IOException", "Ocurrio un error de red en la llamada al servicio de firma", errorCallback);
  998. return;
  999. }
  1000. if (httpRequest.readyState==4) {
  1001. if (httpRequest.status==200) {
  1002. var needContinue = MiniApplet.clienteFirma.successResponseFunction(httpRequest.responseText, cipherKey, successCallback, errorCallback);
  1003. if (!needContinue) {
  1004. return;
  1005. }
  1006. }
  1007. else {
  1008. MiniApplet.clienteFirma.errorResponseFunction(null, httpRequest.responseText, errorCallback);
  1009. return;
  1010. }
  1011. }
  1012. setTimeout(retrieveRequest, 4000, httpRequest, url, params.replace("&it=" + (iterations-1), "&it=" + iterations), cipherKey, successCallback, errorCallback);
  1013. }
  1014. // getHttpRequest
  1015. function getHttpRequest() {
  1016. var activexmodes=["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"]; //activeX versions to check for in IE
  1017. if (window.ActiveXObject){ //Test for support for ActiveXObject in IE first (as XMLHttpRequest in IE7 is broken)
  1018. for (var i=0; i<activexmodes.length; i++) {
  1019. try {
  1020. return new ActiveXObject(activexmodes[i]);
  1021. }
  1022. catch(e) {
  1023. //suppress error
  1024. }
  1025. }
  1026. }
  1027. else if (window.XMLHttpRequest) { // if Mozilla, Safari etc
  1028. return new XMLHttpRequest();
  1029. }
  1030. else {
  1031. return false;
  1032. }
  1033. }
  1034. /**
  1035. * Genera un numero aleatorio para utilizar como clave de cifrado.
  1036. */
  1037. function generateCipherKey() {
  1038. return zeroFill(Math.floor(((Math.random() * MAX_NUMBER) + 1) % 100000000), 8);
  1039. }
  1040. /**
  1041. * Realiza un descifrado DES compatible con Java (Algoritmo DES, modo CBC, sin Padding).
  1042. * Recibe en base 64 la cadena de texto cifrado antecedido por el padding anadido manualmente
  1043. * a los datos para permitir el cifrado DES (separado por un punto ('.')), ademas de la clave
  1044. * para descifrar.
  1045. * Como resultado devuelve la cadena de texto descifrada en base 64.
  1046. */
  1047. function decipher(cipheredData, key) {
  1048. var dotPos = cipheredData.indexOf('.');
  1049. var padding = cipheredData.substr(0, dotPos);
  1050. var deciphered = des(key, base64ToString(cipheredData.substr(dotPos + 1).replace(/\-/g, "+").replace(/\_/g, "/")), 0, 0, null);
  1051. return stringToBase64(deciphered.substr(0, deciphered.length - padding - 8));
  1052. }
  1053. /**
  1054. * Realiza un cifrado DES compatible con Java (Algoritmo DES, modo CBC, sin Padding).
  1055. * @param dataB64 Cadena de texto base 64.
  1056. * @param key Clave de cifrado.
  1057. * @return Base 64 cifrado.
  1058. */
  1059. function cipher(dataB64, key) {
  1060. var data = base64ToString(dataB64.replace(/\-/g, "+").replace(/\_/g, "/"));
  1061. var padding = (8 - (data.length % 8)) % 8;
  1062. // Los datos cifrados los pasamos a base 64 y, antes de devolverlos le anteponemos el padding que
  1063. // le habra agregado el metodo de cifrado separados por un punto ('.').
  1064. return padding + "." + stringToBase64(des(key, data, 1, 0, null)).replace(/\+/g, "-").replace(/\//g, "_");
  1065. }
  1066. }
  1067. };
  1068. /**
  1069. * Base64 encode / decode
  1070. * http://www.webtoolkit.info/
  1071. */
  1072. var Base64 = {
  1073. // private property
  1074. _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
  1075. _keyStr_URL_SAFE : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",
  1076. // public method for encoding
  1077. encode : function (input, URL_SAFE) {
  1078. var keyStr = (URL_SAFE == true) ? this._keyStr_URL_SAFE : this._keyStr;
  1079. var output = "";
  1080. var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
  1081. var i = 0;
  1082. input = Base64._utf8_encode(input);
  1083. while (i < input.length) {
  1084. chr1 = input.charCodeAt(i++);
  1085. chr2 = input.charCodeAt(i++);
  1086. chr3 = input.charCodeAt(i++);
  1087. enc1 = chr1 >> 2;
  1088. enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  1089. enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  1090. enc4 = chr3 & 63;
  1091. if (isNaN(chr2)) {
  1092. enc3 = enc4 = 64;
  1093. } else if (isNaN(chr3)) {
  1094. enc4 = 64;
  1095. }
  1096. output = output +
  1097. keyStr.charAt(enc1) + keyStr.charAt(enc2) +
  1098. keyStr.charAt(enc3) + keyStr.charAt(enc4);
  1099. }
  1100. return output;
  1101. },
  1102. // public method for decoding
  1103. decode : function (input, URL_SAFE) {
  1104. var keyStr = (URL_SAFE == true) ? this._keyStr_URL_SAFE : this._keyStr;
  1105. var output = "";
  1106. var chr1, chr2, chr3;
  1107. var enc1, enc2, enc3, enc4;
  1108. var i = 0;
  1109. input = (URL_SAFE == true) ? input.replace(/[^A-Za-z0-9\-\_\=]/g, "") : input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  1110. while (i < input.length) {
  1111. enc1 = keyStr.indexOf(input.charAt(i++));
  1112. enc2 = keyStr.indexOf(input.charAt(i++));
  1113. enc3 = keyStr.indexOf(input.charAt(i++));
  1114. enc4 = keyStr.indexOf(input.charAt(i++));
  1115. chr1 = (enc1 << 2) | (enc2 >> 4);
  1116. chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
  1117. chr3 = ((enc3 & 3) << 6) | enc4;
  1118. output = output + String.fromCharCode(chr1);
  1119. if (enc3 != 64) {
  1120. output = output + String.fromCharCode(chr2);
  1121. }
  1122. if (enc4 != 64) {
  1123. output = output + String.fromCharCode(chr3);
  1124. }
  1125. }
  1126. output = Base64._utf8_decode(output);
  1127. return output;
  1128. },
  1129. // private method for UTF-8 encoding
  1130. _utf8_encode : function (string) {
  1131. string = string.replace(/\r\n/g,"\n");
  1132. var utftext = "";
  1133. for (var n = 0; n < string.length; n++) {
  1134. var c = string.charCodeAt(n);
  1135. if (c < 128) {
  1136. utftext += String.fromCharCode(c);
  1137. }
  1138. else if((c > 127) && (c < 2048)) {
  1139. utftext += String.fromCharCode((c >> 6) | 192);
  1140. utftext += String.fromCharCode((c & 63) | 128);
  1141. }
  1142. else {
  1143. utftext += String.fromCharCode((c >> 12) | 224);
  1144. utftext += String.fromCharCode(((c >> 6) & 63) | 128);
  1145. utftext += String.fromCharCode((c & 63) | 128);
  1146. }
  1147. }
  1148. return utftext;
  1149. },
  1150. // private method for UTF-8 decoding
  1151. _utf8_decode : function (utftext) {
  1152. var string = "";
  1153. var i = 0;
  1154. var c = c1 = c2 = 0;
  1155. while ( i < utftext.length ) {
  1156. c = utftext.charCodeAt(i);
  1157. if (c < 128) {
  1158. string += String.fromCharCode(c);
  1159. i++;
  1160. }
  1161. else if((c > 191) && (c < 224)) {
  1162. c2 = utftext.charCodeAt(i+1);
  1163. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  1164. i += 2;
  1165. }
  1166. else {
  1167. c2 = utftext.charCodeAt(i+1);
  1168. c3 = utftext.charCodeAt(i+2);
  1169. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  1170. i += 3;
  1171. }
  1172. }
  1173. return string;
  1174. }
  1175. };
  1176. //Paul Tero, July 2001
  1177. //http://www.tero.co.uk/des/
  1178. //Optimised for performance with large blocks by Michael Hayworth, November 2001
  1179. //http://www.netdealing.com
  1180. //THIS SOFTWARE IS PROVIDED "AS IS" AND
  1181. //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  1182. //IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  1183. //ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  1184. //FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  1185. //DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  1186. //OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  1187. //HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  1188. //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  1189. //OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  1190. //SUCH DAMAGE.
  1191. //des
  1192. //this takes the key, the message, and whether to encrypt or decrypt
  1193. function des (key, message, encrypt, mode, iv, padding) {
  1194. //declaring this locally speeds things up a bit
  1195. 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);
  1196. 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);
  1197. 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);
  1198. 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);
  1199. 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,0x42080100,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);
  1200. 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);
  1201. 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);
  1202. 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);
  1203. //create the 16 or 48 subkeys we will need
  1204. var keys = des_createKeys (key);
  1205. var m=0, i, j, temp, right1, right2, left, right, looping;
  1206. var cbcleft, cbcleft2, cbcright, cbcright2;
  1207. var endloop, loopinc;
  1208. var len = message.length;
  1209. var chunk = 0;
  1210. //set up the loops for single and triple des
  1211. var iterations = keys.length == 32 ? 3 : 9; //single or triple des
  1212. if (iterations == 3) {looping = encrypt ? new Array (0, 32, 2) : new Array (30, -2, -2);}
  1213. 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);}
  1214. //pad the message depending on the padding parameter
  1215. if (padding == 2) message += " "; //pad the message with spaces
  1216. 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
  1217. else if (!padding) message += "\0\0\0\0\0\0\0\0"; //pad the message out with null bytes
  1218. //store the result here
  1219. result = "";
  1220. tempresult = "";
  1221. if (mode == 1) { //CBC mode
  1222. cbcleft = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
  1223. cbcright = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
  1224. m=0;
  1225. }
  1226. //loop through each 64 bit chunk of the message
  1227. while (m < len) {
  1228. left = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
  1229. right = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
  1230. //for Cipher Block Chaining mode, xor the message with the previous result
  1231. if (mode == 1) {if (encrypt) {left ^= cbcleft; right ^= cbcright;} else {cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;}}
  1232. //first each 64 but chunk of the message must be permuted according to IP
  1233. temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
  1234. temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
  1235. temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
  1236. temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
  1237. temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
  1238. left = ((left << 1) | (left >>> 31));
  1239. right = ((right << 1) | (right >>> 31));
  1240. //do this either 1 or 3 times for each chunk of the message
  1241. for (j=0; j<iterations; j+=3) {
  1242. endloop = looping[j+1];
  1243. loopinc = looping[j+2];
  1244. //now go through and perform the encryption or decryption
  1245. for (i=looping[j]; i!=endloop; i+=loopinc) { //for efficiency
  1246. right1 = right ^ keys[i];
  1247. right2 = ((right >>> 4) | (right << 28)) ^ keys[i+1];
  1248. //the result is attained by passing these bytes through the S selection functions
  1249. temp = left;
  1250. left = right;
  1251. right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f]
  1252. | spfunction6[(right1 >>> 8) & 0x3f] | spfunction8[right1 & 0x3f]
  1253. | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f]
  1254. | spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]);
  1255. }
  1256. temp = left; left = right; right = temp; //unreverse left and right
  1257. } //for either 1 or 3 iterations
  1258. //move then each one bit to the right
  1259. left = ((left >>> 1) | (left << 31));
  1260. right = ((right >>> 1) | (right << 31));
  1261. //now perform IP-1, which is IP in the opposite direction
  1262. temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
  1263. temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
  1264. temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
  1265. temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
  1266. temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
  1267. //for Cipher Block Chaining mode, xor the message with the previous result
  1268. if (mode == 1) {if (encrypt) {cbcleft = left; cbcright = right;} else {left ^= cbcleft2; right ^= cbcright2;}}
  1269. tempresult += String.fromCharCode ((left>>>24), ((left>>>16) & 0xff), ((left>>>8) & 0xff), (left & 0xff), (right>>>24), ((right>>>16) & 0xff), ((right>>>8) & 0xff), (right & 0xff));
  1270. chunk += 8;
  1271. if (chunk == 512) {result += tempresult; tempresult = ""; chunk = 0;}
  1272. } //for every 8 characters, or 64 bits in the message
  1273. //return the result as an array
  1274. return result + tempresult;
  1275. } //end of des
  1276. //des_createKeys
  1277. //this takes as input a 64 bit key (even though only 56 bits are used)
  1278. //as an array of 2 integers, and returns 16 48 bit keys
  1279. function des_createKeys (key) {
  1280. //declaring this locally speeds things up a bit
  1281. pc2bytes0 = new Array (0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204);
  1282. pc2bytes1 = new Array (0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101);
  1283. pc2bytes2 = new Array (0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808);
  1284. pc2bytes3 = new Array (0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000);
  1285. pc2bytes4 = new Array (0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010);
  1286. pc2bytes5 = new Array (0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420);
  1287. pc2bytes6 = new Array (0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002);
  1288. pc2bytes7 = new Array (0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800);
  1289. pc2bytes8 = new Array (0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002);
  1290. pc2bytes9 = new Array (0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408);
  1291. pc2bytes10 = new Array (0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020);
  1292. pc2bytes11 = new Array (0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200);
  1293. pc2bytes12 = new Array (0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010);
  1294. pc2bytes13 = new Array (0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105);
  1295. //how many iterations (1 for des, 3 for triple des)
  1296. var iterations = key.length > 8 ? 3 : 1; //changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys
  1297. //stores the return keys
  1298. var keys = new Array (32 * iterations);
  1299. //now define the left shifts which need to be done
  1300. var shifts = new Array (0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0);
  1301. //other variables
  1302. var lefttemp, righttemp, m=0, n=0, temp;
  1303. for (var j=0; j<iterations; j++) { //either 1 or 3 iterations
  1304. left = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++);
  1305. right = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++);
  1306. temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
  1307. temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
  1308. temp = ((left >>> 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2);
  1309. temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
  1310. temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
  1311. temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
  1312. temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
  1313. //the right side needs to be shifted and to get the last four bits of the left side
  1314. temp = (left << 8) | ((right >>> 20) & 0x000000f0);
  1315. //left needs to be put upside down
  1316. left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0);
  1317. right = temp;
  1318. //now go through and perform these shifts on the left and right keys
  1319. for (var i=0; i < shifts.length; i++) {
  1320. //shift the keys either one or two bits to the left
  1321. if (shifts[i]) {left = (left << 2) | (left >>> 26); right = (right << 2) | (right >>> 26);}
  1322. else {left = (left << 1) | (left >>> 27); right = (right << 1) | (right >>> 27);}
  1323. left &= -0xf; right &= -0xf;
  1324. //now apply PC-2, in such a way that E is easier when encrypting or decrypting
  1325. //this conversion will look like PC-2 except only the last 6 bits of each byte are used
  1326. //rather than 48 consecutive bits and the order of lines will be according to
  1327. //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7
  1328. lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf]
  1329. | pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf]
  1330. | pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf]
  1331. | pc2bytes6[(left >>> 4) & 0xf];
  1332. righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf]
  1333. | pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf]
  1334. | pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf]
  1335. | pc2bytes13[(right >>> 4) & 0xf];
  1336. temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff;
  1337. keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16);
  1338. }
  1339. } //for each iterations
  1340. //return the keys we've created
  1341. return keys;
  1342. } //end of des_createKeys
  1343. //Convierte una cadena a Base 64. Debido a un error en el algoritmo original, pasaremos
  1344. // de cadena a hexadecimal y de hexadecimal a Base64
  1345. function stringToBase64 (s) {
  1346. return hexToBase64(stringToHex(s));
  1347. }
  1348. //Convert a base64 string into a normal string
  1349. function base64ToString (s) {
  1350. //the base 64 characters
  1351. 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','i','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','+','/');
  1352. var decode = new Object();
  1353. for (var i=0; i<BASE64.length; i++) {decode[BASE64[i]] = i;} //inverse of the array
  1354. decode['='] = 0; //add the equals sign as well
  1355. var r = "", c1, c2, c3, c4, len=s.length; //define variables
  1356. s += "===="; //just to make sure it is padded correctly
  1357. for (var i=0; i<len; i+=4) { //4 input characters at a time
  1358. c1 = s.charAt(i); //the 1st base64 input characther
  1359. c2 = s.charAt(i+1);
  1360. c3 = s.charAt(i+2);
  1361. c4 = s.charAt(i+3);
  1362. r += String.fromCharCode (((decode[c1] << 2) & 0xff) | (decode[c2] >> 4)); //reform the string
  1363. if (c3 != '=') r += String.fromCharCode (((decode[c2] << 4) & 0xff) | (decode[c3] >> 2));
  1364. if (c4 != '=') r += String.fromCharCode (((decode[c3] << 6) & 0xff) | decode[c4]);
  1365. }
  1366. return r;
  1367. }
  1368. function stringToHex (s) {
  1369. var r = "";
  1370. var hexes = new Array ("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f");
  1371. for (var i=0; i<s.length; i++) {r += hexes [s.charCodeAt(i) >> 4] + hexes [s.charCodeAt(i) & 0xf];}
  1372. return r;
  1373. }
  1374. // --- Funciones para pasar de Hexadecimal a base64
  1375. var tableStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  1376. var tableStr_URL_SAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
  1377. function btoa (bin, urlSafe) {
  1378. var table = (urlSafe == true ? tableStr_URL_SAFE : tableStr).split("");
  1379. for (var i = 0, j = 0, len = bin.length / 3, base64 = []; i < len; ++i) {
  1380. var a = bin.charCodeAt(j++), b = bin.charCodeAt(j++), c = bin.charCodeAt(j++);
  1381. if ((a | b | c) > 255) throw new Error("String contains an invalid character");
  1382. base64[base64.length] = table[a >> 2] + table[((a << 4) & 63) | (b >> 4)] +
  1383. (isNaN(b) ? "=" : table[((b << 2) & 63) | (c >> 6)]) +
  1384. (isNaN(b + c) ? "=" : table[c & 63]);
  1385. }
  1386. return base64.join("");
  1387. }
  1388. function hexToBase64(str, urlSafe) {
  1389. var byteString;
  1390. var byteArray = str.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ");
  1391. try {
  1392. byteString = String.fromCharCode.apply(null, byteArray);
  1393. } catch (e) {
  1394. var strTemp = "";
  1395. for (var i = 0, len = byteArray.length; i < len; i++) {
  1396. strTemp += String.fromCharCode(byteArray[i]);
  1397. }
  1398. byteString = strTemp;
  1399. }
  1400. return btoa(byteString, urlSafe);
  1401. }