diff options
Diffstat (limited to 'security/manager/pki/resources/content/pippki.js')
-rw-r--r-- | security/manager/pki/resources/content/pippki.js | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/security/manager/pki/resources/content/pippki.js b/security/manager/pki/resources/content/pippki.js new file mode 100644 index 000000000..b660d2d93 --- /dev/null +++ b/security/manager/pki/resources/content/pippki.js @@ -0,0 +1,191 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +/* + * These are helper functions to be included + * pippki UI js files. + */ + +function setText(id, value) { + let element = document.getElementById(id); + if (!element) { + return; + } + if (element.hasChildNodes()) { + element.removeChild(element.firstChild); + } + element.appendChild(document.createTextNode(value)); +} + +const nsICertificateDialogs = Components.interfaces.nsICertificateDialogs; +const nsCertificateDialogs = "@mozilla.org/nsCertificateDialogs;1"; + +function viewCertHelper(parent, cert) { + if (!cert) { + return; + } + + var cd = Components.classes[nsCertificateDialogs].getService(nsICertificateDialogs); + cd.viewCert(parent, cert); +} + +function getDERString(cert) +{ + var length = {}; + var derArray = cert.getRawDER(length); + var derString = ''; + for (var i = 0; i < derArray.length; i++) { + derString += String.fromCharCode(derArray[i]); + } + return derString; +} + +function getPKCS7String(cert, chainMode) +{ + var length = {}; + var pkcs7Array = cert.exportAsCMS(chainMode, length); + var pkcs7String = ''; + for (var i = 0; i < pkcs7Array.length; i++) { + pkcs7String += String.fromCharCode(pkcs7Array[i]); + } + return pkcs7String; +} + +function getPEMString(cert) +{ + var derb64 = btoa(getDERString(cert)); + // Wrap the Base64 string into lines of 64 characters with CRLF line breaks + // (as specified in RFC 1421). + var wrapped = derb64.replace(/(\S{64}(?!$))/g, "$1\r\n"); + return "-----BEGIN CERTIFICATE-----\r\n" + + wrapped + + "\r\n-----END CERTIFICATE-----\r\n"; +} + +function alertPromptService(title, message) +{ + var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]. + getService(Components.interfaces.nsIPromptService); + ps.alert(window, title, message); +} + +const DEFAULT_CERT_EXTENSION = "crt"; + +/** + * Generates a filename for a cert suitable to set as the |defaultString| + * attribute on an nsIFilePicker. + * + * @param {nsIX509Cert} cert + * The cert to generate a filename for. + * @returns {String} + * Generated filename. + */ +function certToFilename(cert) { + let filename = cert.commonName; + if (!filename) { + filename = cert.windowTitle; + } + + // Remove unneeded and/or unsafe characters. + filename = filename.replace(/\s/g, "") + .replace(/\./g, "") + .replace(/\\/g, "") + .replace(/\//g, ""); + + // nsIFilePicker.defaultExtension is more of a suggestion to some + // implementations, so we include the extension in the file name as well. This + // is what the documentation for nsIFilePicker.defaultString says we should do + // anyways. + return `${filename}.${DEFAULT_CERT_EXTENSION}`; +} + +function exportToFile(parent, cert) +{ + var bundle = document.getElementById("pippki_bundle"); + if (!cert) { + return; + } + + var nsIFilePicker = Components.interfaces.nsIFilePicker; + var fp = Components.classes["@mozilla.org/filepicker;1"]. + createInstance(nsIFilePicker); + fp.init(parent, bundle.getString("SaveCertAs"), + nsIFilePicker.modeSave); + fp.defaultString = certToFilename(cert); + fp.defaultExtension = DEFAULT_CERT_EXTENSION; + fp.appendFilter(bundle.getString("CertFormatBase64"), "*.crt; *.pem"); + fp.appendFilter(bundle.getString("CertFormatBase64Chain"), "*.crt; *.pem"); + fp.appendFilter(bundle.getString("CertFormatDER"), "*.der"); + fp.appendFilter(bundle.getString("CertFormatPKCS7"), "*.p7c"); + fp.appendFilter(bundle.getString("CertFormatPKCS7Chain"), "*.p7c"); + fp.appendFilters(nsIFilePicker.filterAll); + var res = fp.show(); + if (res != nsIFilePicker.returnOK && res != nsIFilePicker.returnReplace) { + return; + } + + var content = ''; + switch (fp.filterIndex) { + case 1: + content = getPEMString(cert); + var chain = cert.getChain(); + for (let i = 1; i < chain.length; i++) { + content += getPEMString(chain.queryElementAt(i, Components.interfaces.nsIX509Cert)); + } + break; + case 2: + content = getDERString(cert); + break; + case 3: + content = getPKCS7String(cert, Components.interfaces.nsIX509Cert.CMS_CHAIN_MODE_CertOnly); + break; + case 4: + content = getPKCS7String(cert, Components.interfaces.nsIX509Cert.CMS_CHAIN_MODE_CertChainWithRoot); + break; + case 0: + default: + content = getPEMString(cert); + break; + } + var msg; + var written = 0; + try { + var file = Components.classes["@mozilla.org/file/local;1"]. + createInstance(Components.interfaces.nsILocalFile); + file.initWithPath(fp.file.path); + var fos = Components.classes["@mozilla.org/network/file-output-stream;1"]. + createInstance(Components.interfaces.nsIFileOutputStream); + // flags: PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE + fos.init(file, 0x02 | 0x08 | 0x20, 0o0644, 0); + written = fos.write(content, content.length); + fos.close(); + } catch (e) { + switch (e.result) { + case Components.results.NS_ERROR_FILE_ACCESS_DENIED: + msg = bundle.getString("writeFileAccessDenied"); + break; + case Components.results.NS_ERROR_FILE_IS_LOCKED: + msg = bundle.getString("writeFileIsLocked"); + break; + case Components.results.NS_ERROR_FILE_NO_DEVICE_SPACE: + case Components.results.NS_ERROR_FILE_DISK_FULL: + msg = bundle.getString("writeFileNoDeviceSpace"); + break; + default: + msg = e.message; + break; + } + } + if (written != content.length) { + if (msg.length == 0) { + msg = bundle.getString("writeFileUnknownError"); + } + alertPromptService(bundle.getString("writeFileFailure"), + bundle.getFormattedString("writeFileFailed", + [fp.file.path, msg])); + } +} |