diff --git a/app/extensions/brave/locales/en-US/app.properties b/app/extensions/brave/locales/en-US/app.properties index 46ab66a6624..3599208c618 100644 --- a/app/extensions/brave/locales/en-US/app.properties +++ b/app/extensions/brave/locales/en-US/app.properties @@ -25,6 +25,7 @@ certErrorText=This site cannot be loaded due to a certificate error: certErrorAdvanced=Advanced settings certErrorSafety=Back to safety certErrorButtonText=Ignore certificate error (dangerous!) +certErrorShowCertificate=Show Certificate safebrowsingError=Dangerous Site Blocked safebrowsingErrorText=For your safety, Brave has blocked this site because it is distributing malware or stealing login credentials. safebrowsingErrorAdvanced=Advanced diff --git a/app/index.js b/app/index.js index dea1e3d6664..53bf2f3de42 100644 --- a/app/index.js +++ b/app/index.js @@ -55,6 +55,7 @@ let lastWindowClosed = false // Domains to accept bad certs for. TODO: Save the accepted cert fingerprints. let acceptCertDomains = {} +let errorCerts = {} // URLs to callback for auth. let authCallbacks = {} // Don't show the keytar prompt more than once per 24 hours @@ -208,6 +209,15 @@ app.on('ready', () => { return } + errorCerts[url] = { + subjectName: cert.subjectName, + issuerName: cert.issuerName, + serialNumber: cert.serialNumber, + validStart: cert.validStart, + validExpiry: cert.validExpiry, + fingerprint: cert.fingerprint + } + // Tell the page to show an unlocked icon. Note this is sent to the main // window webcontents, not the webview webcontents let sender = webContents.hostWebContents || webContents @@ -447,6 +457,19 @@ app.on('ready', () => { } }) + ipcMain.on(messages.GET_CERT_ERROR_DETAIL, (event, url) => { + if (errorCerts[url]) { + event.sender.send(messages.SET_CERT_ERROR_DETAIL, { + subjectName: errorCerts[url].subjectName, + issuerName: errorCerts[url].issuerName, + serialNumber: errorCerts[url].serialNumber, + validStart: errorCerts[url].validStart, + validExpiry: errorCerts[url].validExpiry, + fingerprint: errorCerts[url].fingerprint + }) + } + }) + // save app state every 5 minutes regardless of update frequency setInterval(initiateSessionStateSave, 1000 * 60 * 5) AppStore.addChangeListener(() => { diff --git a/js/about/aboutActions.js b/js/about/aboutActions.js index 380719db8fa..f55374b224a 100644 --- a/js/about/aboutActions.js +++ b/js/about/aboutActions.js @@ -65,6 +65,15 @@ const AboutActions = { ipc.send(messages.CERT_ERROR_ACCEPTED, url) }, + /** + * Get certificate detail when error. + * + * @param {string} url - The URL with the cert error + */ + getCertErrorDetail: function (url) { + ipc.send(messages.GET_CERT_ERROR_DETAIL, url) + }, + /** * Opens a context menu */ diff --git a/js/about/certerror.js b/js/about/certerror.js index 5793edc59ea..1c7e1d841f4 100644 --- a/js/about/certerror.js +++ b/js/about/certerror.js @@ -6,17 +6,67 @@ const React = require('react') const Button = require('../components/button') const aboutActions = require('./aboutActions') const WindowConstants = require('../constants/windowConstants') +const messages = require('../constants/messages') +const ipc = window.chrome.ipc require('../../less/button.less') require('../../less/window.less') require('../../less/about/error.less') +function toHexString (byteArray) { + return byteArray.map(function (byte) { + return ('0' + (byte & 0xFF).toString(16)).slice(-2) + }).join(':') +} + +function toByteArray (str) { + var bytes = [] + for (var i = 0; i < str.length; ++i) { + bytes.push(str.charCodeAt(i)) + } + return bytes +} + +function seperateHex (hexStr) { + var result = [] + for (var i = 0; i < hexStr.length; ++i) { + result += hexStr[i] + if (i % 2 && i !== hexStr.length - 1) { + result += ':' + } + } + return result +} + class CertErrorPage extends React.Component { constructor () { super() this.state = { - advanced: false + advanced: false, + certDetail: false, + certIssuerName: null, + certSubjectName: null, + certSerialNumber: null, + certValidStart: null, + certValidExpiry: null, + certFingerprint: null } + + ipc.on(messages.SET_CERT_ERROR_DETAIL, (e, detail) => { + var validStart = new Date() + var validExpiry = new Date() + validStart.setTime(detail.validStart * 1000) + validExpiry.setTime(detail.validExpiry * 1000) + this.setState({ + certDetail: true, + certIssuerName: detail.issuerName, + certSubjectName: detail.subjectName, + certSerialNumber: seperateHex(detail.serialNumber), + certValidStart: validStart.toString(), + certValidExpiry: validExpiry.toString(), + certFingerprint: detail.fingerprint.split('/') + }) + }) } onAccept () { @@ -40,6 +90,10 @@ class CertErrorPage extends React.Component { this.setState({advanced: true}) } + onDetail () { + aboutActions.getCertErrorDetail(this.state.url) + } + render () { return
@@ -49,12 +103,29 @@ class CertErrorPage extends React.Component {   {this.state.url || ''} {this.state.error || ''} + {this.state.certDetail + ? (
+ {'Issued To'} + {'Common Name (CN): ' + this.state.certSubjectName} + {'Serial Number: ' + this.state.certSerialNumber} + {'Issued By'} + {'Common Name (CN): ' + this.state.certIssuerName} + {'Period of Validity'} + {'Begins On: ' + this.state.certValidStart} + {'Expires On: ' + this.state.certValidExpiry} + {'Fingerprint: '} + {this.state.certFingerprint[0] + ': ' + + toHexString(toByteArray(window.atob(this.state.certFingerprint[1])))} +
) : null}
) + :