I\'m trying to develop a Firefox extension/add-on that needs access to the SSL Certificate information of the page that is currently loaded. Once I have this information I
Building on this answer:
The trick is to register a progress listener and check aState
when the onSecurityChange
function is called. If the Ci.nsIWebProgressListener.STATE_IS_SECURE
flag is set then the page is using an SSL connection. That isn't enough however, the aRequest
parameter may not be an instance of Ci.nsIChannel
, that should be verified first with if (aRequest instanceof Ci.nsIChannel)
.
Here is the working code:
function dumpSecurityInfo(channel) {
const Cc = Components.classes
const Ci = Components.interfaces;
// Do we have a valid channel argument?
if (! channel instanceof Ci.nsIChannel) {
dump("No channel available\n");
return;
}
var secInfo = channel.securityInfo;
// Print general connection security state
if (secInfo instanceof Ci.nsITransportSecurityInfo) {
dump("name: " + channel.name + "\n");
secInfo.QueryInterface(Ci.nsITransportSecurityInfo);
dump("\tSecurity state: ");
// Check security state flags
if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_SECURE) == Ci.nsIWebProgressListener.STATE_IS_SECURE)
dump("secure\n");
else if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_INSECURE) == Ci.nsIWebProgressListener.STATE_IS_INSECURE)
dump("insecure\n");
else if ((secInfo.securityState & Ci.nsIWebProgressListener.STATE_IS_BROKEN) == Ci.nsIWebProgressListener.STATE_IS_BROKEN)
dump("unknown\n");
dump("\tSecurity description: " + secInfo.shortSecurityDescription + "\n");
dump("\tSecurity error message: " + secInfo.errorMessage + "\n");
}
else {
dump("\tNo security info available for this channel\n");
}
// Print SSL certificate details
if (secInfo instanceof Ci.nsISSLStatusProvider) {
var cert = secInfo.QueryInterface(Ci.nsISSLStatusProvider).
SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
dump("\nCertificate Status:\n");
var verificationResult = cert.verifyForUsage(Ci.nsIX509Cert.CERT_USAGE_SSLServer);
dump("\tVerification: ");
switch (verificationResult) {
case Ci.nsIX509Cert.VERIFIED_OK:
dump("OK");
break;
case Ci.nsIX509Cert.NOT_VERIFIED_UNKNOWN:
dump("not verfied/unknown");
break;
case Ci.nsIX509Cert.CERT_REVOKED:
dump("revoked");
break;
case Ci.nsIX509Cert.CERT_EXPIRED:
dump("expired");
break;
case Ci.nsIX509Cert.CERT_NOT_TRUSTED:
dump("not trusted");
break;
case Ci.nsIX509Cert.ISSUER_NOT_TRUSTED:
dump("issuer not trusted");
break;
case Ci.nsIX509Cert.ISSUER_UNKNOWN:
dump("issuer unknown");
break;
case Ci.nsIX509Cert.INVALID_CA:
dump("invalid CA");
break;
default:
dump("unexpected failure");
break;
}
dump("\n");
dump("\tCommon name (CN) = " + cert.commonName + "\n");
dump("\tOrganisation = " + cert.organization + "\n");
dump("\tIssuer = " + cert.issuerOrganization + "\n");
dump("\tSHA1 fingerprint = " + cert.sha1Fingerprint + "\n");
var validity = cert.validity.QueryInterface(Ci.nsIX509CertValidity);
dump("\tValid from " + validity.notBeforeGMT + "\n");
dump("\tValid until " + validity.notAfterGMT + "\n");
}
}
var myListener =
{
QueryInterface: function(aIID)
{
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) { },
onLocationChange: function(aProgress, aRequest, aURI) { },
onProgressChange: function(aWebProgress, aRequest, curSelf, maxSelf, curTot, maxTot) { },
onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) { },
onSecurityChange: function(aWebProgress, aRequest, aState)
{
// check if the state is secure or not
if(aState & Ci.nsIWebProgressListener.STATE_IS_SECURE)
{
// this is a secure page, check if aRequest is a channel,
// since only channels have security information
if (aRequest instanceof Ci.nsIChannel)
{
dumpSecurityInfo(aRequest);
}
}
}
}
var test =
{
run: function() {
dump("run\n");
gBrowser.addProgressListener(myListener);
}
};
window.addEventListener("load", function () { test.run(); }, false);
The way you query the channel to get its security info seems sane. I suspect that your problem is actually timing - you query it at the wrong time. Tracing all requests is really the wrong approach if security info is all you are interested in. It makes far more sense to register a progress listener (there are examples) and to look at the channel whenever onSecurityChange
is being called. You are likely to be only interested in requests where aState
contains STATE_IS_SECURE flag. Note that aRequest
parameter is usually an nsIChannel
instance but could also be a plain nsIRequest
- instanceof
check is required.