diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py index 73b5b9cf7..5b4a27c7d 100644 --- a/gui/qt/main_window.py +++ b/gui/qt/main_window.py @@ -2529,6 +2529,7 @@ class ElectrumWindow(QMainWindow): tx_widgets = [] id_widgets = [] + # language lang_help = _('Select which language is used in the GUI (after restart).') lang_label = HelpLabel(_('Language') + ':', lang_help) lang_combo = QComboBox() @@ -2631,20 +2632,28 @@ class ElectrumWindow(QMainWindow): alias_e.editingFinished.connect(on_alias_edit) id_widgets.append((alias_label, alias_e)) - msg = _('Chain of SSL certificates, used to create BIP70 payment requests. ')\ - +_('Put your certificate at the top of the list, and the root CA at the end') - SSL_cert_label = HelpLabel(_('SSL certificate') + ':', msg) - SSL_cert = self.config.get('ssl_chain','') - SSL_cert_e = QLineEdit(SSL_cert) - SSL_cert_e.editingFinished.connect(lambda: self.config.set_key('ssl_chain', str(SSL_cert_e.text()))) - id_widgets.append((SSL_cert_label, SSL_cert_e)) - - msg = _('Path to your SSL private key, used to sign BIP70 payment requests.') - SSL_key_label = HelpLabel(_('SSL private key') + ':', msg) - SSL_key = self.config.get('ssl_privkey','') - SSL_key_e = QLineEdit(SSL_key) - SSL_key_e.editingFinished.connect(lambda: self.config.set_key('ssl_privkey', str(SSL_key_e.text()))) - id_widgets.append((SSL_key_label, SSL_key_e)) + # SSL certificate + msg = ' '.join([ + _('SSL certificate used to sign payment requests.'), + _('Use setconfig to set ssl_chain and ssl_privkey.'), + ]) + if self.config.get('ssl_privkey') or self.onfig.get('ssl_chain'): + try: + SSL_identity = paymentrequest.check_ssl_config(self.config) + SSL_error = None + except BaseException as e: + SSL_identity = "error" + SSL_error = str(e) + else: + SSL_identity = "" + SSL_error = None + SSL_id_label = HelpLabel(_('SSL certificate') + ':', msg) + SSL_id_e = QLineEdit(SSL_identity) + SSL_id_e.setStyleSheet(RED_BG if SSL_error else GREEN_BG if SSL_identity else '') + if SSL_error: + SSL_id_e.setToolTip(SSL_error) + SSL_id_e.setReadOnly(True) + id_widgets.append((SSL_id_label, SSL_id_e)) units = ['BTC', 'mBTC', 'bits'] msg = _('Base unit of your wallet.')\ diff --git a/lib/paymentrequest.py b/lib/paymentrequest.py index ea3ded0ff..1436fa85c 100644 --- a/lib/paymentrequest.py +++ b/lib/paymentrequest.py @@ -119,75 +119,22 @@ class PaymentRequest: return False def verify_x509(self, paymntreq): - """ verify chain of certificates. The last certificate is the CA""" if not ca_list: self.error = "Trusted certificate authorities list not found" return False cert = pb2.X509Certificates() cert.ParseFromString(paymntreq.pki_data) - cert_num = len(cert.certificate) - x509_chain = [] - for i in range(cert_num): - x = x509.X509() - x.parseBinary(bytearray(cert.certificate[i])) - x509_chain.append(x) - if i == 0: - try: - x.check_date() - except Exception as e: - self.error = str(e) - return - self.requestor = x.get_common_name() - if self.requestor.startswith('*.'): - self.requestor = self.requestor[2:] - else: - if not x.check_ca(): - self.error = "ERROR: Supplied CA Certificate Error" - return - if not cert_num > 1: - self.error = "ERROR: CA Certificate Chain Not Provided by Payment Processor" + # verify the chain of certificates + try: + x, ca = verify_cert_chain(cert.certificate) + except BaseException as e: + self.error = str(e) return False - # if the root CA is not supplied, add it to the chain - ca = x509_chain[cert_num-1] - if ca.getFingerprint() not in ca_list: - keyID = ca.get_issuer_keyID() - f = ca_keyID.get(keyID) - if f: - root = ca_list[f] - x509_chain.append(root) - else: - self.error = "Supplied CA Not Found in Trusted CA Store." - return False - # verify the chain of signatures - cert_num = len(x509_chain) - for i in range(1, cert_num): - x = x509_chain[i] - prev_x = x509_chain[i-1] - algo, sig, data = prev_x.get_signature() - sig = bytearray(sig) - - pubkey = rsakey.RSAKey(x.modulus, x.exponent) - - if algo == x509.ALGO_RSA_SHA1: - verify = pubkey.hashAndVerify(sig, data) - elif algo == x509.ALGO_RSA_SHA256: - hashBytes = bytearray(hashlib.sha256(data).digest()) - verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA256 + hashBytes) - elif algo == x509.ALGO_RSA_SHA384: - hashBytes = bytearray(hashlib.sha384(data).digest()) - verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA384 + hashBytes) - elif algo == x509.ALGO_RSA_SHA512: - hashBytes = bytearray(hashlib.sha512(data).digest()) - verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA512 + hashBytes) - else: - self.error = "Algorithm not supported" - util.print_error(self.error, algo.getComponentByName('algorithm')) - return False - if not verify: - self.error = "Certificate not Signed by Provided CA Certificate Chain" - return False + # get requestor name + self.requestor = x.get_common_name() + if self.requestor.startswith('*.'): + self.requestor = self.requestor[2:] # verify the BIP70 signature - x = x509_chain[0] pubkey0 = rsakey.RSAKey(x.modulus, x.exponent) sig = paymntreq.signature paymntreq.signature = '' @@ -330,6 +277,75 @@ def sign_request_with_alias(pr, alias, alias_privkey): +def verify_cert_chain(chain): + """ Verify a chain of certificates. The last certificate is the CA""" + # parse the chain + cert_num = len(chain) + x509_chain = [] + for i in range(cert_num): + x = x509.X509() + x.parseBinary(bytearray(chain[i])) + x509_chain.append(x) + if i == 0: + x.check_date() + else: + if not x.check_ca(): + raise BaseException("ERROR: Supplied CA Certificate Error") + if not cert_num > 1: + raise BaseException("ERROR: CA Certificate Chain Not Provided by Payment Processor") + # if the root CA is not supplied, add it to the chain + ca = x509_chain[cert_num-1] + if ca.getFingerprint() not in ca_list: + keyID = ca.get_issuer_keyID() + f = ca_keyID.get(keyID) + if f: + root = ca_list[f] + x509_chain.append(root) + else: + raise BaseException("Supplied CA Not Found in Trusted CA Store.") + # verify the chain of signatures + cert_num = len(x509_chain) + for i in range(1, cert_num): + x = x509_chain[i] + prev_x = x509_chain[i-1] + algo, sig, data = prev_x.get_signature() + sig = bytearray(sig) + pubkey = rsakey.RSAKey(x.modulus, x.exponent) + if algo == x509.ALGO_RSA_SHA1: + verify = pubkey.hashAndVerify(sig, data) + elif algo == x509.ALGO_RSA_SHA256: + hashBytes = bytearray(hashlib.sha256(data).digest()) + verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA256 + hashBytes) + elif algo == x509.ALGO_RSA_SHA384: + hashBytes = bytearray(hashlib.sha384(data).digest()) + verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA384 + hashBytes) + elif algo == x509.ALGO_RSA_SHA512: + hashBytes = bytearray(hashlib.sha512(data).digest()) + verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA512 + hashBytes) + else: + raise BaseException("Algorithm not supported") + util.print_error(self.error, algo.getComponentByName('algorithm')) + if not verify: + raise BaseException("Certificate not Signed by Provided CA Certificate Chain") + + return x509_chain[0], ca + + +def check_ssl_config(config): + import pem + key_path = config.get('ssl_privkey') + cert_path = config.get('ssl_chain') + with open(key_path, 'r') as f: + params = pem.parse_private_key(f.read()) + privkey = rsakey.RSAKey(*params) + with open(cert_path, 'r') as f: + s = f.read() + bList = pem.dePemList(s, "CERTIFICATE") + # verify chain + x, ca = verify_cert_chain(bList) + # verify pubkey + return x.get_common_name() + def sign_request_with_x509(pr, key_path, cert_path): import pem with open(key_path, 'r') as f: