add root certificate to chain if missing. fixes #1137
This commit is contained in:
parent
6f5241302c
commit
4fe32d2ad1
|
@ -106,23 +106,26 @@ class PaymentRequest:
|
||||||
except:
|
except:
|
||||||
self.error = "cannot parse payment request"
|
self.error = "cannot parse payment request"
|
||||||
return
|
return
|
||||||
|
self.details = pb2.PaymentDetails()
|
||||||
|
self.details.ParseFromString(self.data.serialized_payment_details)
|
||||||
|
for o in self.details.outputs:
|
||||||
|
addr = transaction.get_address_from_output_script(o.script)[1]
|
||||||
|
self.outputs.append(('address', addr, o.amount))
|
||||||
|
self.memo = self.details.memo
|
||||||
|
self.payment_url = self.details.payment_url
|
||||||
|
|
||||||
|
|
||||||
def verify(self):
|
def verify(self):
|
||||||
|
|
||||||
if not ca_list:
|
if not ca_list:
|
||||||
self.error = "Trusted certificate authorities list not found"
|
self.error = "Trusted certificate authorities list not found"
|
||||||
return False
|
return False
|
||||||
|
|
||||||
paymntreq = self.data
|
paymntreq = self.data
|
||||||
if not paymntreq.signature:
|
if not paymntreq.signature:
|
||||||
self.error = "No signature"
|
self.error = "No signature"
|
||||||
return
|
return
|
||||||
|
|
||||||
cert = pb2.X509Certificates()
|
cert = pb2.X509Certificates()
|
||||||
cert.ParseFromString(paymntreq.pki_data)
|
cert.ParseFromString(paymntreq.pki_data)
|
||||||
cert_num = len(cert.certificate)
|
cert_num = len(cert.certificate)
|
||||||
|
|
||||||
x509_chain = []
|
x509_chain = []
|
||||||
for i in range(cert_num):
|
for i in range(cert_num):
|
||||||
x = x509.X509()
|
x = x509.X509()
|
||||||
|
@ -140,15 +143,39 @@ class PaymentRequest:
|
||||||
if not x.check_ca():
|
if not x.check_ca():
|
||||||
self.error = "ERROR: Supplied CA Certificate Error"
|
self.error = "ERROR: Supplied CA Certificate Error"
|
||||||
return
|
return
|
||||||
|
|
||||||
if not cert_num > 1:
|
if not cert_num > 1:
|
||||||
self.error = "ERROR: CA Certificate Chain Not Provided by Payment Processor"
|
self.error = "ERROR: CA Certificate Chain Not Provided by Payment Processor"
|
||||||
return False
|
return False
|
||||||
|
# if the root CA is not supplied, add it to the chain
|
||||||
|
ca = x509_chain[cert_num-1]
|
||||||
|
supplied_CA_fingerprint = ca.getFingerprint()
|
||||||
|
supplied_CA_names = ca.extract_names()
|
||||||
|
CA_OU = supplied_CA_names['OU']
|
||||||
|
x = ca_list.get(supplied_CA_fingerprint)
|
||||||
|
if x:
|
||||||
|
x.slow_parse()
|
||||||
|
names = x.extract_names()
|
||||||
|
assert names['CN'] == supplied_CA_names['CN']
|
||||||
|
else:
|
||||||
|
issuer = ca.get_issuer()
|
||||||
|
for x in ca_list.values():
|
||||||
|
try:
|
||||||
|
x.slow_parse()
|
||||||
|
names = x.extract_names()
|
||||||
|
except Exception as e:
|
||||||
|
util.print_error("cannot parse cert:", e)
|
||||||
|
continue
|
||||||
|
if names.get('CN') == issuer.get('CN'):
|
||||||
|
x509_chain.append(x)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self.error = "Supplied CA Not Found in Trusted CA Store."
|
||||||
|
return False
|
||||||
|
# verify the chain of signatures
|
||||||
|
cert_num = len(cert.certificate)
|
||||||
for i in range(1, cert_num):
|
for i in range(1, cert_num):
|
||||||
x = x509_chain[i]
|
x = x509_chain[i]
|
||||||
prev_x = x509_chain[i-1]
|
prev_x = x509_chain[i-1]
|
||||||
|
|
||||||
algo, sig, data = prev_x.extract_sig()
|
algo, sig, data = prev_x.extract_sig()
|
||||||
sig = bytearray(sig[5:])
|
sig = bytearray(sig[5:])
|
||||||
pubkey = x.publicKey
|
pubkey = x.publicKey
|
||||||
|
@ -166,35 +193,17 @@ class PaymentRequest:
|
||||||
else:
|
else:
|
||||||
self.error = "Algorithm not supported"
|
self.error = "Algorithm not supported"
|
||||||
util.print_error(self.error, algo.getComponentByName('algorithm'))
|
util.print_error(self.error, algo.getComponentByName('algorithm'))
|
||||||
return
|
return False
|
||||||
|
|
||||||
if not verify:
|
if not verify:
|
||||||
self.error = "Certificate not Signed by Provided CA Certificate Chain"
|
self.error = "Certificate not Signed by Provided CA Certificate Chain"
|
||||||
return
|
return False
|
||||||
|
# verify the BIP70 signature
|
||||||
ca = x509_chain[cert_num-1]
|
|
||||||
supplied_CA_fingerprint = ca.getFingerprint()
|
|
||||||
supplied_CA_names = ca.extract_names()
|
|
||||||
CA_OU = supplied_CA_names['OU']
|
|
||||||
|
|
||||||
x = ca_list.get(supplied_CA_fingerprint)
|
|
||||||
if x:
|
|
||||||
x.slow_parse()
|
|
||||||
names = x.extract_names()
|
|
||||||
CA_match = True
|
|
||||||
if names['CN'] != supplied_CA_names['CN']:
|
|
||||||
print "ERROR: Trusted CA CN Mismatch; however CA has trusted fingerprint"
|
|
||||||
print "Payment will continue with manual verification."
|
|
||||||
else:
|
|
||||||
CA_match = False
|
|
||||||
|
|
||||||
pubkey0 = x509_chain[0].publicKey
|
pubkey0 = x509_chain[0].publicKey
|
||||||
sig = paymntreq.signature
|
sig = paymntreq.signature
|
||||||
paymntreq.signature = ''
|
paymntreq.signature = ''
|
||||||
s = paymntreq.SerializeToString()
|
s = paymntreq.SerializeToString()
|
||||||
sigBytes = bytearray(sig)
|
sigBytes = bytearray(sig)
|
||||||
msgBytes = bytearray(s)
|
msgBytes = bytearray(s)
|
||||||
|
|
||||||
if paymntreq.pki_type == "x509+sha256":
|
if paymntreq.pki_type == "x509+sha256":
|
||||||
hashBytes = bytearray(hashlib.sha256(msgBytes).digest())
|
hashBytes = bytearray(hashlib.sha256(msgBytes).digest())
|
||||||
verify = pubkey0.verify(sigBytes, x509.PREFIX_RSA_SHA256 + hashBytes)
|
verify = pubkey0.verify(sigBytes, x509.PREFIX_RSA_SHA256 + hashBytes)
|
||||||
|
@ -203,28 +212,11 @@ class PaymentRequest:
|
||||||
else:
|
else:
|
||||||
self.error = "ERROR: Unsupported PKI Type for Message Signature"
|
self.error = "ERROR: Unsupported PKI Type for Message Signature"
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if not verify:
|
if not verify:
|
||||||
self.error = "ERROR: Invalid Signature for Payment Request Data"
|
self.error = "ERROR: Invalid Signature for Payment Request Data"
|
||||||
return False
|
return False
|
||||||
|
|
||||||
### SIG Verified
|
### SIG Verified
|
||||||
self.details = pay_det = pb2.PaymentDetails()
|
self.status = 'Signed by Trusted CA:\n' + CA_OU
|
||||||
self.details.ParseFromString(paymntreq.serialized_payment_details)
|
|
||||||
|
|
||||||
for o in pay_det.outputs:
|
|
||||||
addr = transaction.get_address_from_output_script(o.script)[1]
|
|
||||||
self.outputs.append( ('address', addr, o.amount) )
|
|
||||||
|
|
||||||
self.memo = self.details.memo
|
|
||||||
|
|
||||||
if CA_match:
|
|
||||||
self.status = 'Signed by Trusted CA:\n' + CA_OU
|
|
||||||
else:
|
|
||||||
self.status = "Supplied CA Not Found in Trusted CA Store."
|
|
||||||
|
|
||||||
self.payment_url = self.details.payment_url
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def has_expired(self):
|
def has_expired(self):
|
||||||
|
|
21
lib/x509.py
21
lib/x509.py
|
@ -75,6 +75,27 @@ class X509(tlslite.X509):
|
||||||
self.subject = self.tbs.getComponentByName('subject')
|
self.subject = self.tbs.getComponentByName('subject')
|
||||||
self.extensions = self.tbs.getComponentByName('extensions') or []
|
self.extensions = self.tbs.getComponentByName('extensions') or []
|
||||||
|
|
||||||
|
def get_issuer(self):
|
||||||
|
results = {'CN': None, 'OU': None,}
|
||||||
|
issuer = self.tbs.getComponentByName('issuer')
|
||||||
|
# Extract the CommonName(s) from the cert.
|
||||||
|
for rdnss in issuer:
|
||||||
|
for rdns in rdnss:
|
||||||
|
for name in rdns:
|
||||||
|
oid = name.getComponentByName('type')
|
||||||
|
value = name.getComponentByName('value')
|
||||||
|
|
||||||
|
if oid == COMMON_NAME:
|
||||||
|
value = decoder.decode(value, asn1Spec=DirectoryString())[0]
|
||||||
|
value = decode_str(value.getComponent())
|
||||||
|
results['CN'] = value
|
||||||
|
|
||||||
|
elif oid == OU_NAME:
|
||||||
|
value = decoder.decode(value, asn1Spec=DirectoryString())[0]
|
||||||
|
value = decode_str(value.getComponent())
|
||||||
|
results['OU'] = value
|
||||||
|
return results
|
||||||
|
|
||||||
def extract_names(self):
|
def extract_names(self):
|
||||||
results = {'CN': None,
|
results = {'CN': None,
|
||||||
'DNS': set(),
|
'DNS': set(),
|
||||||
|
|
Loading…
Reference in New Issue