add root certificate to chain if missing. fixes #1137

This commit is contained in:
ThomasV 2015-04-10 16:32:16 +02:00
parent 6f5241302c
commit 4fe32d2ad1
2 changed files with 59 additions and 46 deletions

View File

@ -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.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 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):

View File

@ -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(),