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:
self.error = "cannot parse payment request"
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):
if not ca_list:
self.error = "Trusted certificate authorities list not found"
return False
paymntreq = self.data
if not paymntreq.signature:
self.error = "No signature"
return
cert = pb2.X509Certificates()
cert.ParseFromString(paymntreq.pki_data)
cert_num = len(cert.certificate)
x509_chain = []
for i in range(cert_num):
x = x509.X509()
@ -140,15 +143,39 @@ class PaymentRequest:
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"
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):
x = x509_chain[i]
prev_x = x509_chain[i-1]
algo, sig, data = prev_x.extract_sig()
sig = bytearray(sig[5:])
pubkey = x.publicKey
@ -166,35 +193,17 @@ class PaymentRequest:
else:
self.error = "Algorithm not supported"
util.print_error(self.error, algo.getComponentByName('algorithm'))
return
return False
if not verify:
self.error = "Certificate not Signed by Provided CA Certificate Chain"
return
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
return False
# verify the BIP70 signature
pubkey0 = x509_chain[0].publicKey
sig = paymntreq.signature
paymntreq.signature = ''
s = paymntreq.SerializeToString()
sigBytes = bytearray(sig)
msgBytes = bytearray(s)
if paymntreq.pki_type == "x509+sha256":
hashBytes = bytearray(hashlib.sha256(msgBytes).digest())
verify = pubkey0.verify(sigBytes, x509.PREFIX_RSA_SHA256 + hashBytes)
@ -203,28 +212,11 @@ class PaymentRequest:
else:
self.error = "ERROR: Unsupported PKI Type for Message Signature"
return False
if not verify:
self.error = "ERROR: Invalid Signature for Payment Request Data"
return False
### 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
else:
self.status = "Supplied CA Not Found in Trusted CA Store."
self.payment_url = self.details.payment_url
self.status = 'Signed by Trusted CA:\n' + CA_OU
return True
def has_expired(self):

View File

@ -75,6 +75,27 @@ class X509(tlslite.X509):
self.subject = self.tbs.getComponentByName('subject')
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):
results = {'CN': None,
'DNS': set(),