def sending(self, context): '''Signs XML before sending''' signature_template = ''' <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> <Reference URI="#%(REFERENCE_ID)s"> <Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> <DigestValue></DigestValue> </Reference> </SignedInfo> <SignatureValue /> <KeyInfo> <X509Data> <X509Certificate /> </X509Data> </KeyInfo> </Signature> ''' envelope_element = Parser().parse(string=context.envelope).root() envelope_element.refitPrefixes() body = envelope_element.getChild('Body') payload = body[0] qname = payload.qname() if 'Echo' in qname: return reference_id = "refId:%s" % uuid4() payload.set('Id', reference_id) signature_template %= {'REFERENCE_ID': reference_id} signature_element = Parser().parse(string=signature_template).root() payload.append(signature_element) envelope = self.DTD_TEST_ID % qname envelope += envelope_element.str() envelope = envelope.encode('utf-8') signer = XMLDSIG() signer.load_key(self.key_path, password=self.key_passphrase, cert_path=self.cert_path) context.envelope = signer.sign(envelope) context.envelope = self.RE_DTD_TEST.sub('', context.envelope)
def received(self, context): '''Verifies XML signature of received message''' def _extract_keyinfo_cert(payload): '''Extract the signing certificate from KeyInfo.''' cert_der = payload.getChild('Signature') cert_der = cert_der.getChild('KeyInfo') cert_der = cert_der.getChild('X509Data') cert_der = cert_der.getChild('X509Certificate').getText().strip() cert_der = cert_der.decode('base64') return cert_der def _verify_cn(cert, cis_cert_cn): '''Verify signature certificate common name''' common_name = cert.get_subject().commonName if common_name != cis_cert_cn: raise Exception('Invalid certificate common name in response: ' '%s != %s' % (cis_cert_cn, common_name)) def _fault(code, msg): '''Generate fault XML''' faultcode = Element('faultcode').setText(code) faultstring = Element('faultstring').setText(msg) fault = Element('Fault').append([faultcode, faultstring]) body = Element('Body').append(fault) envelope = Element('Envelope', ns=soap_envns) envelope.append(body) envelope.refitPrefixes() return envelope.str() valid_signature = False try: if not self.cis_ca_path: raise Exception('Certificate Authority not defined') reply_element = Parser().parse(string=context.reply).root() body = reply_element.getChild('Body') payload = body[0] qname = payload.qname() cert_der = _extract_keyinfo_cert(payload) cert = crypto.load_certificate(crypto.FILETYPE_ASN1, cert_der) if 'Echo' in qname or 'Fault' in qname: LOGGER.warning('Not verifying certificate for qname: %s', qname) return if self.cis_cert_cn: _verify_cn(cert, self.cis_cert_cn) else: LOGGER.warning('CIS certificate common name not configured') reply = self.DTD_TEST_ID % qname reply += self.RE_XML_HEADER.sub('', context.reply) verifier = XMLDSIG() verifier.load_cert(self.cis_ca_path) valid_signature = verifier.verify(reply) except Exception as exc: LOGGER.exception('%s: %s', exc, context.reply) context.reply = _fault('Client', 'Invalid response signature: %s' % exc) else: if not valid_signature: LOGGER.error('Invalid response signature: %s', context.reply) context.reply = _fault('Client', 'Invalid response signature')