зеркало из https://github.com/mozilla/pjs.git
Bug 315871, In Certificate Viewer, allow to export cert or full chain Also covers Bug 161275, Support export of SSL, S/MIME, and CA certs Patch contributed by Kaspar Brand r=kengert, sr=rrelyea, a=dsicore
This commit is contained in:
Родитель
2ec86237a3
Коммит
da75c056ed
|
@ -102,21 +102,23 @@
|
|||
|
||||
<!ENTITY certmgr.close.label "Close">
|
||||
<!ENTITY certmgr.close.accesskey "C">
|
||||
<!ENTITY certmgr.view.label "View">
|
||||
<!ENTITY certmgr.view2.label "View…">
|
||||
<!ENTITY certmgr.view.accesskey "V">
|
||||
<!ENTITY certmgr.edit.label "Edit">
|
||||
<!ENTITY certmgr.edit2.label "Edit…">
|
||||
<!ENTITY certmgr.edit.accesskey "E">
|
||||
<!ENTITY certmgr.editca.label "Edit CA Trust">
|
||||
<!ENTITY certmgr.editca.accesskey "d">
|
||||
<!ENTITY certmgr.add.label "Add">
|
||||
<!ENTITY certmgr.add.accesskey "A">
|
||||
<!ENTITY certmgr.delete.label "Delete">
|
||||
<!ENTITY certmgr.export.label "Export…">
|
||||
<!ENTITY certmgr.export.accesskey "x">
|
||||
<!ENTITY certmgr.delete2.label "Delete…">
|
||||
<!ENTITY certmgr.delete.accesskey "D">
|
||||
<!ENTITY certmgr.backup.label "Backup">
|
||||
<!ENTITY certmgr.backup2.label "Backup…">
|
||||
<!ENTITY certmgr.backup.accesskey "B">
|
||||
<!ENTITY certmgr.backupall.label "Backup All">
|
||||
<!ENTITY certmgr.backupall2.label "Backup All…">
|
||||
<!ENTITY certmgr.backupall.accesskey "k">
|
||||
<!ENTITY certmgr.restore.label "Import">
|
||||
<!ENTITY certmgr.restore2.label "Import…">
|
||||
<!ENTITY certmgr.restore.accesskey "m">
|
||||
<!ENTITY certmgr.details.label "Certificate Fields">
|
||||
<!ENTITY certmgr.details.accesskey "F">
|
||||
|
|
|
@ -195,3 +195,17 @@ file_browse_Certificate_spec=Certificate Files
|
|||
|
||||
# Form Signing confirmation prompt
|
||||
formSigningIntro=The site '%S' has requested that you sign the following text message:
|
||||
|
||||
# Cert export
|
||||
SaveCertAs=Save Certificate To File
|
||||
CertFormatBase64=X.509 Certificate (PEM)
|
||||
CertFormatBase64Chain=X.509 Certificate with chain (PEM)
|
||||
CertFormatDER=X.509 Certificate (DER)
|
||||
CertFormatPKCS7=X.509 Certificate (PKCS#7)
|
||||
CertFormatPKCS7Chain=X.509 Certificate with chain (PKCS#7)
|
||||
writeFileFailure=File Error
|
||||
writeFileFailed=Can't write to file %S:\n%S.
|
||||
writeFileAccessDenied=Access denied
|
||||
writeFileIsLocked=File is locked
|
||||
writeFileNoDeviceSpace=No space left on device
|
||||
writeFileUnknownError=Unknown error
|
||||
|
|
|
@ -66,19 +66,23 @@
|
|||
|
||||
<hbox>
|
||||
<button id="ca_viewButton"
|
||||
label="&certmgr.view.label;"
|
||||
label="&certmgr.view2.label;"
|
||||
accesskey="&certmgr.view.accesskey;"
|
||||
disabled="true" oncommand="viewCerts();"/>
|
||||
<button id="ca_editButton"
|
||||
label="&certmgr.edit.label;"
|
||||
label="&certmgr.edit2.label;"
|
||||
accesskey="&certmgr.edit.accesskey;"
|
||||
disabled="true" oncommand="editCerts();"/>
|
||||
<button id="ca_addButton"
|
||||
label="&certmgr.restore.label;"
|
||||
label="&certmgr.restore2.label;"
|
||||
accesskey="&certmgr.restore.accesskey;"
|
||||
oncommand="addCACerts();"/>
|
||||
<button id="ca_exportButton"
|
||||
label="&certmgr.export.label;"
|
||||
accesskey="&certmgr.export.accesskey;"
|
||||
disabled="true" oncommand="exportCerts();"/>
|
||||
<button id="ca_deleteButton"
|
||||
label="&certmgr.delete.label;"
|
||||
label="&certmgr.delete2.label;"
|
||||
accesskey="&certmgr.delete.accesskey;"
|
||||
disabled="true" oncommand="deleteCerts();"/>
|
||||
</hbox>
|
||||
|
|
|
@ -82,23 +82,23 @@
|
|||
|
||||
<hbox>
|
||||
<button id="mine_viewButton" class="normal"
|
||||
label="&certmgr.view.label;"
|
||||
label="&certmgr.view2.label;"
|
||||
accesskey="&certmgr.view.accesskey;"
|
||||
disabled="true" oncommand="viewCerts();"/>
|
||||
<button id="mine_backupButton" class="normal"
|
||||
label="&certmgr.backup.label;"
|
||||
label="&certmgr.backup2.label;"
|
||||
accesskey="&certmgr.backup.accesskey;"
|
||||
disabled="true" oncommand="backupCerts();"/>
|
||||
<button id="mine_backupAllButton" class="normal"
|
||||
label="&certmgr.backupall.label;"
|
||||
label="&certmgr.backupall2.label;"
|
||||
accesskey="&certmgr.backupall.accesskey;"
|
||||
oncommand="backupAllCerts();"/>
|
||||
<button id="mine_restoreButton" class="normal"
|
||||
label="&certmgr.restore.label;"
|
||||
label="&certmgr.restore2.label;"
|
||||
accesskey="&certmgr.restore.accesskey;"
|
||||
oncommand="restoreCerts();"/>
|
||||
<button id="mine_deleteButton" class="normal"
|
||||
label="&certmgr.delete.label;"
|
||||
label="&certmgr.delete2.label;"
|
||||
accesskey="&certmgr.delete.accesskey;"
|
||||
disabled="true" oncommand="deleteCerts();"/>
|
||||
</hbox>
|
||||
|
|
|
@ -67,11 +67,15 @@
|
|||
|
||||
<hbox>
|
||||
<button id="orphan_viewButton" class="normal"
|
||||
label="&certmgr.view.label;"
|
||||
label="&certmgr.view2.label;"
|
||||
accesskey="&certmgr.view.accesskey;"
|
||||
disabled="true" oncommand="viewCerts();"/>
|
||||
<button id="orphan_exportButton" class="normal"
|
||||
label="&certmgr.export.label;"
|
||||
accesskey="&certmgr.export.accesskey;"
|
||||
disabled="true" oncommand="exportCerts();"/>
|
||||
<button id="orphan_deleteButton" class="normal"
|
||||
label="&certmgr.delete.label;"
|
||||
label="&certmgr.delete2.label;"
|
||||
accesskey="&certmgr.delete.accesskey;"
|
||||
disabled="true" oncommand="deleteCerts();"/>
|
||||
</hbox>
|
||||
|
|
|
@ -73,19 +73,23 @@
|
|||
|
||||
<hbox>
|
||||
<button id="email_viewButton"
|
||||
label="&certmgr.view.label;"
|
||||
label="&certmgr.view2.label;"
|
||||
accesskey="&certmgr.view.accesskey;"
|
||||
disabled="true" oncommand="viewCerts();"/>
|
||||
<button id="email_editButton"
|
||||
label="&certmgr.edit.label;"
|
||||
label="&certmgr.edit2.label;"
|
||||
accesskey="&certmgr.edit.accesskey;"
|
||||
disabled="true" oncommand="editCerts();"/>
|
||||
<button id="email_addButton"
|
||||
label="&certmgr.restore.label;"
|
||||
label="&certmgr.restore2.label;"
|
||||
accesskey="&certmgr.restore.accesskey;"
|
||||
oncommand="addEmailCert();"/>
|
||||
<button id="email_exportButton"
|
||||
label="&certmgr.export.label;"
|
||||
accesskey="&certmgr.export.accesskey;"
|
||||
disabled="true" oncommand="exportCerts();"/>
|
||||
<button id="email_deleteButton"
|
||||
label="&certmgr.delete.label;"
|
||||
label="&certmgr.delete2.label;"
|
||||
accesskey="&certmgr.delete.accesskey;"
|
||||
disabled="true" oncommand="deleteCerts();"/>
|
||||
</hbox>
|
||||
|
|
|
@ -72,19 +72,23 @@
|
|||
|
||||
<hbox>
|
||||
<button id="websites_viewButton"
|
||||
label="&certmgr.view.label;"
|
||||
label="&certmgr.view2.label;"
|
||||
accesskey="&certmgr.view.accesskey;"
|
||||
disabled="true" oncommand="viewCerts();"/>
|
||||
<button id="websites_editButton"
|
||||
label="&certmgr.edit.label;"
|
||||
label="&certmgr.edit2.label;"
|
||||
accesskey="&certmgr.edit.accesskey;"
|
||||
disabled="true" oncommand="editCerts();"/>
|
||||
<button id="websites_addButton"
|
||||
label="&certmgr.restore.label;"
|
||||
label="&certmgr.restore2.label;"
|
||||
accesskey="&certmgr.restore.accesskey;"
|
||||
oncommand="addWebSiteCert();"/>
|
||||
<button id="websites_exportButton"
|
||||
label="&certmgr.export.label;"
|
||||
accesskey="&certmgr.export.accesskey;"
|
||||
disabled="true" oncommand="exportCerts();"/>
|
||||
<button id="websites_deleteButton"
|
||||
label="&certmgr.delete.label;"
|
||||
label="&certmgr.delete2.label;"
|
||||
accesskey="&certmgr.delete.accesskey;"
|
||||
disabled="true" oncommand="deleteCerts();"/>
|
||||
</hbox>
|
||||
|
|
|
@ -65,5 +65,12 @@
|
|||
<label class="header" value="&certmgr.fields.label;"/>
|
||||
<textbox id="certDumpVal" multiline="true" rows="8"
|
||||
readonly="true" style="font-family: -moz-fixed;"/>
|
||||
|
||||
<separator class="thin"/>
|
||||
<hbox>
|
||||
<button id="export_cert" class="normal" label="&certmgr.export.label;"
|
||||
accesskey="&certmgr.export.accesskey;"
|
||||
oncommand="exportToFile(window, getCurrentCert());"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</overlay>
|
||||
|
|
|
@ -198,6 +198,8 @@ function ca_enableButtons()
|
|||
enableViewButton.setAttribute("disabled",toggle);
|
||||
var enableEditButton=document.getElementById('ca_editButton');
|
||||
enableEditButton.setAttribute("disabled",edit_toggle);
|
||||
var enableExportButton=document.getElementById('ca_exportButton');
|
||||
enableExportButton.setAttribute("disabled",toggle);
|
||||
var enableDeleteButton=document.getElementById('ca_deleteButton');
|
||||
enableDeleteButton.setAttribute("disabled",toggle);
|
||||
}
|
||||
|
@ -228,6 +230,8 @@ function websites_enableButtons()
|
|||
enableViewButton.setAttribute("disabled",toggle);
|
||||
var enableEditButton=document.getElementById('websites_editButton');
|
||||
enableEditButton.setAttribute("disabled",toggle);
|
||||
var enableExportButton=document.getElementById('websites_exportButton');
|
||||
enableExportButton.setAttribute("disabled",toggle);
|
||||
var enableDeleteButton=document.getElementById('websites_deleteButton');
|
||||
enableDeleteButton.setAttribute("disabled",toggle);
|
||||
}
|
||||
|
@ -243,6 +247,8 @@ function email_enableButtons()
|
|||
enableViewButton.setAttribute("disabled",toggle);
|
||||
var enableEditButton=document.getElementById('email_editButton');
|
||||
enableEditButton.setAttribute("disabled",toggle);
|
||||
var enableExportButton=document.getElementById('email_exportButton');
|
||||
enableExportButton.setAttribute("disabled",toggle);
|
||||
var enableDeleteButton=document.getElementById('email_deleteButton');
|
||||
enableDeleteButton.setAttribute("disabled",toggle);
|
||||
}
|
||||
|
@ -256,6 +262,8 @@ function orphan_enableButtons()
|
|||
}
|
||||
var enableViewButton=document.getElementById('orphan_viewButton');
|
||||
enableViewButton.setAttribute("disabled",toggle);
|
||||
var enableExportButton=document.getElementById('orphan_exportButton');
|
||||
enableExportButton.setAttribute("disabled",toggle);
|
||||
var enableDeleteButton=document.getElementById('orphan_deleteButton');
|
||||
enableDeleteButton.setAttribute("disabled",toggle);
|
||||
}
|
||||
|
@ -334,6 +342,18 @@ function restoreCerts()
|
|||
}
|
||||
}
|
||||
|
||||
function exportCerts()
|
||||
{
|
||||
getSelectedCerts();
|
||||
var numcerts = selected_certs.length;
|
||||
if (!numcerts)
|
||||
return;
|
||||
|
||||
for (var t=0; t<numcerts; t++) {
|
||||
exportToFile(window, selected_certs[t]);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCerts()
|
||||
{
|
||||
getSelectedCerts();
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
accesskey="&validation.updatecrl.accesskey;"
|
||||
oncommand="UpdateCRL();"/>
|
||||
<button id="importCRL" class="push"
|
||||
label="&certmgr.restore.label;"
|
||||
label="&certmgr.restore2.label;"
|
||||
accesskey="&certmgr.restore.accesskey;"
|
||||
oncommand="ImportCRL();"/>
|
||||
<spacer flex="2"/>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Javier Delgadillo <javi@netscape.com>
|
||||
* Kaspar Brand <mozcontrib@velox.ch>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -61,3 +62,133 @@ function viewCertHelper(parent, cert) {
|
|||
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 = {};
|
||||
cert.QueryInterface(Components.interfaces.nsIX509Cert3);
|
||||
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 = null;
|
||||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
|
||||
getService(Components.interfaces.nsIPromptService);
|
||||
ps.alert(window, title, message);
|
||||
}
|
||||
|
||||
function exportToFile(parent, cert)
|
||||
{
|
||||
var bundle = srGetStrBundle("chrome://pippki/locale/pippki.properties");
|
||||
if (!cert)
|
||||
return;
|
||||
|
||||
var nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
var fp = Components.classes["@mozilla.org/filepicker;1"].
|
||||
createInstance(nsIFilePicker);
|
||||
fp.init(parent, bundle.GetStringFromName("SaveCertAs"),
|
||||
nsIFilePicker.modeSave);
|
||||
var filename = cert.commonName;
|
||||
if (!filename.length)
|
||||
filename = cert.windowTitle;
|
||||
// remove all whitespace from the default filename
|
||||
fp.defaultString = filename.replace(/\s*/g,'');
|
||||
fp.defaultExtension = "crt";
|
||||
fp.appendFilter(bundle.GetStringFromName("CertFormatBase64"), "*.crt; *.pem");
|
||||
fp.appendFilter(bundle.GetStringFromName("CertFormatBase64Chain"), "*.crt; *.pem");
|
||||
fp.appendFilter(bundle.GetStringFromName("CertFormatDER"), "*.der");
|
||||
fp.appendFilter(bundle.GetStringFromName("CertFormatPKCS7"), "*.p7c");
|
||||
fp.appendFilter(bundle.GetStringFromName("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 (var 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.nsIX509Cert3.CMS_CHAIN_MODE_CertOnly);
|
||||
break;
|
||||
case 4:
|
||||
content = getPKCS7String(cert, Components.interfaces.nsIX509Cert3.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, 00644, 0);
|
||||
written = fos.write(content, content.length);
|
||||
fos.close();
|
||||
}
|
||||
catch(e) {
|
||||
switch (e.result) {
|
||||
case Components.results.NS_ERROR_FILE_ACCESS_DENIED:
|
||||
msg = bundle.GetStringFromName("writeFileAccessDenied");
|
||||
break;
|
||||
case Components.results.NS_ERROR_FILE_IS_LOCKED:
|
||||
msg = bundle.GetStringFromName("writeFileIsLocked");
|
||||
break;
|
||||
case Components.results.NS_ERROR_FILE_NO_DEVICE_SPACE:
|
||||
case Components.results.NS_ERROR_FILE_DISK_FULL:
|
||||
msg = bundle.GetStringFromName("writeFileNoDeviceSpace");
|
||||
break;
|
||||
default:
|
||||
msg = e.message;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (written != content.length) {
|
||||
if (!msg.length)
|
||||
msg = bundle.GetStringFromName("writeFileUnknownError");
|
||||
alertPromptService(bundle.GetStringFromName("writeFileFailure"),
|
||||
bundle.formatStringFromName("writeFileFailed",
|
||||
[ fp.file.path, msg ], 2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
* Ian McGreer <mcgreer@netscape.com>
|
||||
* Javier Delgadillo <javi@netscape.com>
|
||||
* Kai Engert <kengert@redhat.com>
|
||||
* Kaspar Brand <mozcontrib@velox.ch>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -321,3 +322,29 @@ function getProxyOnUIThread(aObject, aInterface) {
|
|||
aInterface, aObject, 5);
|
||||
// 5 == NS_PROXY_ALWAYS | NS_PROXY_SYNC
|
||||
}
|
||||
|
||||
function getCurrentCert()
|
||||
{
|
||||
var realIndex;
|
||||
var tree = document.getElementById('treesetDump');
|
||||
if (tree.view.selection.isSelected(tree.currentIndex)
|
||||
&& document.getElementById('prettyprint_tab').selected) {
|
||||
/* if the user manually selected a cert on the Details tab,
|
||||
then take that one */
|
||||
realIndex = tree.currentIndex;
|
||||
} else {
|
||||
/* otherwise, take the one at the bottom of the chain
|
||||
(i.e. the one of the end-entity, unless we're displaying
|
||||
CA certs) */
|
||||
realIndex = tree.view.rowCount - 1;
|
||||
}
|
||||
if (realIndex >= 0) {
|
||||
var item = tree.contentView.getItemAtIndex(realIndex);
|
||||
var dbKey = item.firstChild.firstChild.getAttribute('display');
|
||||
var certdb = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
|
||||
var cert = certdb.findCertByDBKey(dbKey,null);
|
||||
return cert;
|
||||
}
|
||||
/* shouldn't really happen */
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -46,9 +46,16 @@ interface nsICertVerificationListener;
|
|||
* TODO: nsIX509Cert3 should be derived from nsIX509Cert2
|
||||
* (and nsIX509Cert2 derived from nsIX509Cert)
|
||||
*/
|
||||
[scriptable, uuid(402aee39-653c-403f-8be1-6d1824223bf9)]
|
||||
[scriptable, uuid(89d9f248-1160-4935-9f99-2bdbf67b5849)]
|
||||
interface nsIX509Cert3 : nsISupports {
|
||||
|
||||
/**
|
||||
* Constants for specifying the chain mode when exporting a certificate
|
||||
*/
|
||||
const unsigned long CMS_CHAIN_MODE_CertOnly = 1;
|
||||
const unsigned long CMS_CHAIN_MODE_CertChain = 2;
|
||||
const unsigned long CMS_CHAIN_MODE_CertChainWithRoot = 3;
|
||||
|
||||
/**
|
||||
* Async version of nsIX509Cert::getUsagesArray()
|
||||
*
|
||||
|
@ -56,6 +63,19 @@ interface nsIX509Cert3 : nsISupports {
|
|||
* availability of results will be notified.
|
||||
*/
|
||||
void requestUsagesArrayAsync(in nsICertVerificationListener cvl);
|
||||
|
||||
/**
|
||||
* Obtain the certificate wrapped in a PKCS#7 SignedData structure,
|
||||
* with or without the certificate chain
|
||||
*
|
||||
* @param chainMode Whether to include the chain (with or without the root),
|
||||
see CMS_CHAIN_MODE constants.
|
||||
* @param length The number of bytes of the PKCS#7 data.
|
||||
* @param data The bytes representing the PKCS#7 wrapped certificate.
|
||||
*/
|
||||
void exportAsCMS(in unsigned long chainMode,
|
||||
out unsigned long length,
|
||||
[retval, array, size_is(length)] out octet data);
|
||||
};
|
||||
|
||||
[scriptable, uuid(2fd0a785-9f2d-4327-8871-8c3e0783891d)]
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "prprf.h"
|
||||
|
||||
#include "nsNSSComponent.h" // for PIPNSS string bundle calls.
|
||||
#include "nsNSSCleaner.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
|
@ -86,6 +87,8 @@ extern "C" {
|
|||
#include "ssl.h"
|
||||
#include "ocsp.h"
|
||||
#include "plbase64.h"
|
||||
#include "cms.h"
|
||||
#include "cert.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gPIPNSSLog;
|
||||
|
@ -93,6 +96,12 @@ extern PRLogModuleInfo* gPIPNSSLog;
|
|||
|
||||
static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
|
||||
|
||||
NSSCleanupAutoPtrClass(CERTCertificateList, CERT_DestroyCertificateList)
|
||||
NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
|
||||
NSSCleanupAutoPtrClass(NSSCMSMessage, NSS_CMSMessage_Destroy)
|
||||
NSSCleanupAutoPtrClass_WithParam(PLArenaPool, PORT_FreeArena, FalseParam, PR_FALSE)
|
||||
NSSCleanupAutoPtrClass(NSSCMSSignedData, NSS_CMSSignedData_Destroy)
|
||||
|
||||
// This is being stored in an PRUint32 that can otherwise
|
||||
// only take values from nsIX509Cert's list of cert types.
|
||||
// As nsIX509Cert is frozen, we choose a value not contained
|
||||
|
@ -967,6 +976,137 @@ nsNSSCertificate::GetRawDER(PRUint32 *aLength, PRUint8 **aArray)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSCertificate::ExportAsCMS(PRUint32 chainMode,
|
||||
PRUint32 *aLength, PRUint8 **aArray)
|
||||
{
|
||||
NS_ENSURE_ARG(aLength);
|
||||
NS_ENSURE_ARG(aArray);
|
||||
|
||||
nsNSSShutDownPreventionLock locker;
|
||||
if (isAlreadyShutDown())
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
if (!mCert)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
switch (chainMode) {
|
||||
case nsIX509Cert3::CMS_CHAIN_MODE_CertOnly:
|
||||
case nsIX509Cert3::CMS_CHAIN_MODE_CertChain:
|
||||
case nsIX509Cert3::CMS_CHAIN_MODE_CertChainWithRoot:
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
};
|
||||
|
||||
PLArenaPool *arena = PORT_NewArena(1024);
|
||||
PLArenaPoolCleanerFalseParam arenaCleaner(arena);
|
||||
if (!arena) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("nsNSSCertificate::ExportAsCMS - out of memory\n"));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
NSSCMSMessage *cmsg = NSS_CMSMessage_Create(nsnull);
|
||||
NSSCMSMessageCleaner cmsgCleaner(cmsg);
|
||||
if (!cmsg) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("nsNSSCertificate::ExportAsCMS - can't create CMS message\n"));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/*
|
||||
* first, create SignedData with the certificate only (no chain)
|
||||
*/
|
||||
NSSCMSSignedData *sigd = NSS_CMSSignedData_CreateCertsOnly(cmsg, mCert, PR_FALSE);
|
||||
NSSCMSSignedDataCleaner sigdCleaner(sigd);
|
||||
if (!sigd) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("nsNSSCertificate::ExportAsCMS - can't create SignedData\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calling NSS_CMSSignedData_CreateCertsOnly() will not allow us
|
||||
* to specify the inclusion of the root, but CERT_CertChainFromCert() does.
|
||||
* Since CERT_CertChainFromCert() also includes the certificate itself,
|
||||
* we have to start at the issuing cert (to avoid duplicate certs
|
||||
* in the SignedData).
|
||||
*/
|
||||
if (chainMode == nsIX509Cert3::CMS_CHAIN_MODE_CertChain ||
|
||||
chainMode == nsIX509Cert3::CMS_CHAIN_MODE_CertChainWithRoot) {
|
||||
CERTCertificate *issuerCert = CERT_FindCertIssuer(mCert, PR_Now(), certUsageAnyCA);
|
||||
CERTCertificateCleaner issuerCertCleaner(issuerCert);
|
||||
/*
|
||||
* the issuerCert of a self signed root is the cert itself,
|
||||
* so make sure we're not adding duplicates, again
|
||||
*/
|
||||
if (issuerCert && issuerCert != mCert) {
|
||||
PRBool includeRoot =
|
||||
(chainMode == nsIX509Cert3::CMS_CHAIN_MODE_CertChainWithRoot);
|
||||
CERTCertificateList *certChain = CERT_CertChainFromCert(issuerCert, certUsageAnyCA, includeRoot);
|
||||
CERTCertificateListCleaner certChainCleaner(certChain);
|
||||
if (certChain) {
|
||||
if (NSS_CMSSignedData_AddCertList(sigd, certChain) == SECSuccess) {
|
||||
certChainCleaner.detach();
|
||||
}
|
||||
else {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("nsNSSCertificate::ExportAsCMS - can't add chain\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* try to add the issuerCert, at least */
|
||||
if (NSS_CMSSignedData_AddCertificate(sigd, issuerCert)
|
||||
== SECSuccess) {
|
||||
issuerCertCleaner.detach();
|
||||
}
|
||||
else {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("nsNSSCertificate::ExportAsCMS - can't add issuer cert\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
|
||||
if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd)
|
||||
== SECSuccess) {
|
||||
sigdCleaner.detach();
|
||||
}
|
||||
else {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("nsNSSCertificate::ExportAsCMS - can't attach SignedData\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
SECItem certP7 = { siBuffer, nsnull, 0 };
|
||||
NSSCMSEncoderContext *ecx = NSS_CMSEncoder_Start(cmsg, nsnull, nsnull, &certP7, arena,
|
||||
nsnull, nsnull, nsnull, nsnull, nsnull,
|
||||
nsnull);
|
||||
if (!ecx) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("nsNSSCertificate::ExportAsCMS - can't create encoder context\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
|
||||
("nsNSSCertificate::ExportAsCMS - failed to add encoded data\n"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*aArray = (PRUint8*)nsMemory::Alloc(certP7.len);
|
||||
if (!*aArray)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
memcpy(*aArray, certP7.data, certP7.len);
|
||||
*aLength = certP7.len;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
CERTCertificate *
|
||||
nsNSSCertificate::GetCert()
|
||||
{
|
||||
|
|
|
@ -101,6 +101,7 @@ public: \
|
|||
object = nsnull; \
|
||||
} \
|
||||
} \
|
||||
void detach() {object=nsnull;} \
|
||||
};
|
||||
|
||||
#define NSSCleanupAutoPtrClass_WithParam(nsstype, cleanfunc, namesuffix, paramvalue) \
|
||||
|
@ -120,6 +121,7 @@ public: \
|
|||
object = nsnull; \
|
||||
} \
|
||||
} \
|
||||
void detach() {object=nsnull;} \
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче