Example #1
0
def _verify_envelope_with_key(envelope, key):
    soap_env = detect_soap_env(envelope)

    header = envelope.find(QName(soap_env, "Header"))
    if header is None:
        raise SignatureVerificationFailed()

    security = header.find(QName(ns.WSSE, "Security"))
    signature = security.find(QName(ns.DS, "Signature"))
    # la DIAN no cumple a cabalidad token-profile 1.0
    if signature is None:
        return SignatureVerificationFailed()

    ctx = xmlsec.SignatureContext()

    # Find each signed element and register its ID with the signing context.
    refs = signature.xpath("ds:SignedInfo/ds:Reference",
                           namespaces={"ds": ns.DS})
    for ref in refs:
        # Get the reference URI and cut off the initial '#'
        referenced_id = ref.get("URI")[1:]
        referenced = envelope.xpath("//*[@wsu:Id='%s']" % referenced_id,
                                    namespaces={"wsu": ns.WSU})[0]
        ctx.register_id(referenced, "Id", ns.WSU)

    ctx.key = key

    try:
        ctx.verify(signature)
    except xmlsec.Error:
        # Sadly xmlsec gives us no details about the reason for the failure, so
        # we have nothing to pass on except that verification failed.
        raise SignatureVerificationFailed()
Example #2
0
def _verify_envelope_with_key(envelope, key):
    soap_env = detect_soap_env(envelope)

    header = envelope.find(QName(soap_env, 'Header'))
    if header is None:
        raise SignatureVerificationFailed()

    security = header.find(QName(ns.WSSE, 'Security'))
    signature = security.find(QName(ns.DS, 'Signature'))

    ctx = xmlsec.SignatureContext()

    # Find each signed element and register its ID with the signing context.
    refs = signature.xpath('ds:SignedInfo/ds:Reference',
                           namespaces={'ds': ns.DS})
    for ref in refs:
        # Get the reference URI and cut off the initial '#'
        referenced_id = ref.get('URI')[1:]
        referenced = envelope.xpath(
            "//*[@wsu:Id='%s']" % referenced_id,
            namespaces={'wsu': ns.WSU},
        )[0]
        ctx.register_id(referenced, 'Id', ns.WSU)

    ctx.key = key

    try:
        ctx.verify(signature)
    except xmlsec.Error:
        # Sadly xmlsec gives us no details about the reason for the failure, so
        # we have nothing to pass on except that verification failed.
        raise SignatureVerificationFailed()
Example #3
0
def verify_envelope(envelope, cert):
    """Verify WS-Security signature on given SOAP envelope with given cert.

    Expects a document like that found in the sample XML in the ``sign()``
    docstring.

    Raise SignatureVerificationFailed on failure, silent on success.

    """
    soap_env = detect_soap_env(envelope)

    header = envelope.find(QName(soap_env, "Header"))
    if header is None:
        raise SignatureVerificationFailed()

    security = header.find(QName(ns.WSSE, "Security"))
    signature = security.find(QName(ns.DS, "Signature"))
    key_text = security.find(QName(ns.WSSE, "BinarySecurityToken")).text
    key = x509.load_der_x509_certificate(base64.b64decode(key_text))
    ctx = xmlsig.SignatureContext()
    ctx.public_key = key.public_key()
    try:
        ctx.verify(signature)
    except Exception:
        # Sadly xmlsec gives us no details about the reason for the failure, so
        # we have nothing to pass on except that verification failed.
        raise SignatureVerificationFailed() from None
Example #4
0
def _signature_prepare(envelope, key):
    """Prepare envelope and sign."""
    soap_env = detect_soap_env(envelope)

    # Create the Signature node.
    signature = xmlsec.template.create(envelope, xmlsec.Transform.EXCL_C14N,
                                       xmlsec.Transform.RSA_SHA1)

    # Add a KeyInfo node with X509Data child to the Signature. XMLSec will fill
    # in this template with the actual certificate details when it signs.
    key_info = xmlsec.template.ensure_key_info(signature)
    x509_data = xmlsec.template.add_x509_data(key_info)
    xmlsec.template.x509_data_add_issuer_serial(x509_data)
    xmlsec.template.x509_data_add_certificate(x509_data)

    # Insert the Signature node in the wsse:Security header.
    security = get_security_header(envelope)
    security.insert(0, signature)
    security.append(etree.Element(QName(ns.WSU, "Timestamp")))

    # Perform the actual signing.
    ctx = xmlsec.SignatureContext()
    ctx.key = key
    _sign_node(ctx, signature, envelope.find(QName(soap_env, "Body")))
    _sign_node(ctx, signature, security.find(QName(ns.WSU, "Timestamp")))
    ctx.sign(signature)

    # Place the X509 data inside a WSSE SecurityTokenReference within
    # KeyInfo. The recipient expects this structure, but we can't rearrange
    # like this until after signing, because otherwise xmlsec won't populate
    # the X509 data (because it doesn't understand WSSE).
    sec_token_ref = etree.SubElement(key_info,
                                     QName(ns.WSSE, "SecurityTokenReference"))
    return security, sec_token_ref, x509_data
Example #5
0
def sign_envelope(envelope, keyfile, certfile, password=None):
    key = _make_sign_key(keyfile, certfile, password)
    reference_id = 'Reference'
    security = get_security_header(envelope)
    security.set(QName(ns.SOAP_ENV_11, 'mustUnderstand'), '1')
    x509type = 'http://docs.oasis-open.org/wss/2004/01/' \
               'oasis-200401-wss-x509-token-profile-1.0#X509v3'
    encoding_type = "http://docs.oasis-open.org/wss/2004/01/" \
                    "oasis-200401-wss-soap-message-security-1.0#Base64Binary"
    binary_token = etree.SubElement(security,
                                    QName(ns.WSSE, 'BinarySecurityToken'),
                                    attrib={
                                        QName(ns.WSU, 'Id'): reference_id,
                                        'ValueType': x509type,
                                        'EncodingType': encoding_type
                                    })
    binary_token.text = base64.b64encode(
        crypto.dump_certificate(
            crypto.FILETYPE_ASN1,
            crypto.load_pkcs12(keyfile, password).get_certificate()))
    signature = xmlsec.template.create(envelope,
                                       xmlsec.Transform.EXCL_C14N,
                                       xmlsec.Transform.RSA_SHA1,
                                       ns='ds')

    # Add a KeyInfo node with X509Data child to the Signature. XMLSec will fill
    # in this template with the actual certificate details when it signs.
    key_info = xmlsec.template.ensure_key_info(signature)
    sec_token_ref = etree.SubElement(key_info,
                                     QName(ns.WSSE, 'SecurityTokenReference'))
    etree.SubElement(sec_token_ref,
                     QName(ns.WSSE, 'Reference'),
                     attrib={
                         'URI': '#' + reference_id,
                         'ValueType': x509type
                     })
    # Insert the Signature node in the wsse:Security header.

    security.append(signature)

    # Perform the actual signing.
    ctx = xmlsec.SignatureContext()
    ctx.key = key

    timestamp = etree.SubElement(security, QName(ns.WSU, 'Timestamp'))
    now = datetime.now()
    etree.SubElement(timestamp, QName(
        ns.WSU, 'Created')).text = now.strftime('%Y-%m-%dT%H:%M:%SZ')
    exp = now + timedelta(hours=1)
    etree.SubElement(timestamp, QName(
        ns.WSU, 'Expires')).text = exp.strftime('%Y-%m-%dT%H:%M:%SZ')

    soap_env = detect_soap_env(envelope)
    _sign_node(ctx, signature, envelope.find(QName(soap_env, 'Body')))
    _sign_node(ctx, signature, security.find(QName(ns.WSU, 'Timestamp')))

    ctx.sign(signature)
    return etree.fromstring(etree.tostring(envelope, method='c14n'))
Example #6
0
def get_or_create_header(envelope):
    soap_env = detect_soap_env(envelope)

    # look for the Header element and create it if not found
    header_qname = "{%s}Header" % soap_env
    header = envelope.find(header_qname)
    if header is None:
        header = etree.Element(header_qname)
        envelope.insert(0, header)
    return header
Example #7
0
def verify_envelope(envelope, certfile, cert_format=None):
    """Verify WS-Security signature on given SOAP envelope with given cert.

    Expects a document like that found in the sample XML in the ``sign()``
    docstring.

    Raise SignatureVerificationFailed on failure, silent on success.

    """
    soap_env = detect_soap_env(envelope)

    header = envelope.find(QName(soap_env, 'Header'))
    if not header:
        raise SignatureVerificationFailed()

    security = header.find(QName(ns.WSSE, 'Security'))
    signature = security.find(QName(ns.DS, 'Signature'))

    ctx = xmlsec.SignatureContext()

    # Find each signed element and register its ID with the signing context.
    refs = signature.xpath('ds:SignedInfo/ds:Reference',
                           namespaces={'ds': ns.DS})
    for ref in refs:
        # Get the reference URI and cut off the initial '#'
        referenced_id = ref.get('URI')[1:]
        referenced = envelope.xpath(
            "//*[@wsu:Id='%s']" % referenced_id,
            namespaces={'wsu': ns.WSU},
        )[0]
        ctx.register_id(referenced, 'Id', ns.WSU)

    if not cert_format:
        cert_format = xmlsec.KeyFormat.CERT_PEM
    key = xmlsec.Key.from_file(certfile, cert_format, None)
    ctx.key = key

    try:
        ctx.verify(signature)
    except xmlsec.Error:
        # Sadly xmlsec gives us no details about the reason for the failure, so
        # we have nothing to pass on except that verification failed.
        raise SignatureVerificationFailed()
Example #8
0
def _verify_envelope_with_key(envelope, key):
    """Verify WS-Security signature on given SOAP envelope with given cert.

    Copy from zeep.wsse.signature except it does bail out if no signature is found.
    """
    soap_env = detect_soap_env(envelope)

    header = envelope.find(QName(soap_env, 'Header'))
    if header is None:
        raise SignatureVerificationFailed()

    security = header.find(QName(ns.WSSE, 'Security'))
    if security is None:
        raise SignatureVerificationFailed()

    signature = security.find(QName(ns.DS, 'Signature'))

    # Skip signature validation if not present, otherwise call the library function
    if signature is None:
        return
    else:
        zeep_verify_envelope(envelope, key)
Example #9
0
def sign_envelope(envelope, public_cert, private_key, certfile, password):
    reference_id = "Reference"
    security = get_security_header(envelope)
    security.set(QName(ns.SOAP_ENV_11, "mustUnderstand"), "1")
    x509type = (
        "http://docs.oasis-open.org/wss/2004/01/"
        "oasis-200401-wss-x509-token-profile-1.0#X509v3"
    )
    encoding_type = (
        "http://docs.oasis-open.org/wss/2004/01/"
        "oasis-200401-wss-soap-message-security-1.0#Base64Binary"
    )
    binary_token = etree.SubElement(
        security,
        QName(ns.WSSE, "BinarySecurityToken"),
        attrib={
            QName(ns.WSU, "Id"): reference_id,
            "ValueType": x509type,
            "EncodingType": encoding_type,
        },
    )
    binary_token.text = base64.b64encode(
        public_cert.public_bytes(encoding=serialization.Encoding.DER)
    )
    signature = xmlsig.template.create(
        c14n_method=xmlsig.constants.TransformExclC14N,
        sign_method=xmlsig.constants.TransformRsaSha1,
        ns="ds",
    )
    envelope.append(signature)

    # Add a KeyInfo node with X509Data child to the Signature. XMLSec will fill
    # in this template with the actual certificate details when it signs.
    key_info = xmlsig.template.ensure_key_info(signature)
    sec_token_ref = etree.SubElement(key_info, QName(ns.WSSE, "SecurityTokenReference"))
    etree.SubElement(
        sec_token_ref,
        QName(ns.WSSE, "Reference"),
        attrib={"URI": "#" + reference_id, "ValueType": x509type},
    )
    # Insert the Signature node in the wsse:Security header.

    security.append(signature)

    # Perform the actual signing.
    ctx = xmlsig.SignatureContext()
    ctx.x509 = public_cert
    ctx.public_key = public_cert.public_key()
    ctx.private_key = private_key

    timestamp = etree.SubElement(security, QName(ns.WSU, "Timestamp"))
    now = datetime.now()
    etree.SubElement(timestamp, QName(ns.WSU, "Created")).text = now.strftime(
        "%Y-%m-%dT%H:%M:%SZ"
    )
    exp = now + timedelta(hours=1)
    etree.SubElement(timestamp, QName(ns.WSU, "Expires")).text = exp.strftime(
        "%Y-%m-%dT%H:%M:%SZ"
    )

    soap_env = detect_soap_env(envelope)
    _sign_node(ctx, signature, envelope.find(QName(soap_env, "Body")))
    _sign_node(ctx, signature, security.find(QName(ns.WSU, "Timestamp")))

    ctx.sign(signature)
    return etree.fromstring(etree.tostring(envelope, method="c14n"))
Example #10
0
def _signature_prepare(envelope,
                       key,
                       signature_method,
                       digest_method,
                       signatures=None):
    """Prepare all the data for signature.

    Mostly copied from zeep.wsse.signature.
    """
    soap_env = detect_soap_env(envelope)
    # Create the Signature node.
    signature = xmlsec.template.create(
        envelope,
        xmlsec.Transform.EXCL_C14N,  # type: ignore
        signature_method or xmlsec.Transform.RSA_SHA1)  # type: ignore

    # Add a KeyInfo node with X509Data child to the Signature. XMLSec will fill
    # in this template with the actual certificate details when it signs.
    key_info = xmlsec.template.ensure_key_info(signature)
    x509_data = xmlsec.template.add_x509_data(key_info)
    xmlsec.template.x509_data_add_issuer_serial(x509_data)
    xmlsec.template.x509_data_add_certificate(x509_data)

    # Insert the Signature node in the wsse:Security header.
    security = get_security_header(envelope)
    security.insert(0, signature)

    # Prepare Timestamp
    timestamp = Element(QName(ns.WSU, 'Timestamp'))
    created = Element(QName(ns.WSU, 'Created'))
    created.text = get_timestamp()
    expires = Element(QName(ns.WSU, 'Expires'))
    expires.text = get_timestamp(datetime.datetime.utcnow() +
                                 datetime.timedelta(minutes=5))
    timestamp.append(created)
    timestamp.append(expires)
    security.insert(0, timestamp)

    # Perform the actual signing.
    ctx = xmlsec.SignatureContext()
    ctx.key = key
    # Sign default elements
    _sign_node(ctx, signature, security.find(QName(ns.WSU, 'Timestamp')),
               digest_method)

    # Sign elements defined in WSDL
    if signatures is not None:
        if signatures['body'] or signatures['everything']:
            _sign_node(ctx, signature, envelope.find(QName(soap_env, 'Body')),
                       digest_method)
        header = get_or_create_header(envelope)
        if signatures['everything']:
            for node in header.iterchildren():
                # Everything doesn't mean everything ...
                if node.nsmap.get(node.prefix) not in OMITTED_HEADERS:
                    _sign_node(ctx, signature, node, digest_method)
        else:
            for node in signatures['header']:
                _sign_node(ctx, signature,
                           header.find(QName(node['Namespace'], node['Name'])),
                           digest_method)

    # Remove newlines from signature...
    for element in signature.iter():
        if element.text is not None and '\n' in element.text:
            element.text = element.text.replace('\n', '')
        if element.tail is not None and '\n' in element.tail:
            element.tail = element.tail.replace('\n', '')

    ctx.sign(signature)

    # Place the X509 data inside a WSSE SecurityTokenReference within
    # KeyInfo. The recipient expects this structure, but we can't rearrange
    # like this until after signing, because otherwise xmlsec won't populate
    # the X509 data (because it doesn't understand WSSE).
    sec_token_ref = SubElement(key_info,
                               QName(ns.WSSE, 'SecurityTokenReference'))
    return security, sec_token_ref, x509_data
Example #11
0
def sign_envelope(envelope, keyfile, certfile, password=None):
    """Sign given SOAP envelope with WSSE sig using given key and cert.

    Sign the wsu:Timestamp node in the wsse:Security header and the soap:Body;
    both must be present.

    Add a ds:Signature node in the wsse:Security header containing the
    signature.

    Use EXCL-C14N transforms to normalize the signed XML (so that irrelevant
    whitespace or attribute ordering changes don't invalidate the
    signature). Use SHA1 signatures.

    Expects to sign an incoming document something like this (xmlns attributes
    omitted for readability):

    <soap:Envelope>
      <soap:Header>
        <wsse:Security mustUnderstand="true">
          <wsu:Timestamp>
            <wsu:Created>2015-06-25T21:53:25.246276+00:00</wsu:Created>
            <wsu:Expires>2015-06-25T21:58:25.246276+00:00</wsu:Expires>
          </wsu:Timestamp>
        </wsse:Security>
      </soap:Header>
      <soap:Body>
        ...
      </soap:Body>
    </soap:Envelope>

    After signing, the sample document would look something like this (note the
    added wsu:Id attr on the soap:Body and wsu:Timestamp nodes, and the added
    ds:Signature node in the header, with ds:Reference nodes with URI attribute
    referencing the wsu:Id of the signed nodes):

    <soap:Envelope>
      <soap:Header>
        <wsse:Security mustUnderstand="true">
          <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="#id-d0f9fd77-f193-471f-8bab-ba9c5afa3e76">
                <Transforms>
                  <Transform
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                </Transforms>
                <DigestMethod
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <DigestValue>nnjjqTKxwl1hT/2RUsBuszgjTbI=</DigestValue>
              </Reference>
              <Reference URI="#id-7c425ac1-534a-4478-b5fe-6cae0690f08d">
                <Transforms>
                  <Transform
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                </Transforms>
                <DigestMethod
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <DigestValue>qAATZaSqAr9fta9ApbGrFWDuCCQ=</DigestValue>
              </Reference>
            </SignedInfo>
            <SignatureValue>Hz8jtQb...bOdT6ZdTQ==</SignatureValue>
            <KeyInfo>
              <wsse:SecurityTokenReference>
                <X509Data>
                  <X509Certificate>MIIDnzC...Ia2qKQ==</X509Certificate>
                  <X509IssuerSerial>
                    <X509IssuerName>...</X509IssuerName>
                    <X509SerialNumber>...</X509SerialNumber>
                  </X509IssuerSerial>
                </X509Data>
              </wsse:SecurityTokenReference>
            </KeyInfo>
          </Signature>
          <wsu:Timestamp wsu:Id="id-7c425ac1-534a-4478-b5fe-6cae0690f08d">
            <wsu:Created>2015-06-25T22:00:29.821700+00:00</wsu:Created>
            <wsu:Expires>2015-06-25T22:05:29.821700+00:00</wsu:Expires>
          </wsu:Timestamp>
        </wsse:Security>
      </soap:Header>
      <soap:Body wsu:Id="id-d0f9fd77-f193-471f-8bab-ba9c5afa3e76">
        ...
      </soap:Body>
    </soap:Envelope>

    """
    # Create the Signature node.
    signature = xmlsec.template.create(
        envelope,
        xmlsec.Transform.EXCL_C14N,
        xmlsec.Transform.RSA_SHA1,
    )

    # Add a KeyInfo node with X509Data child to the Signature. XMLSec will fill
    # in this template with the actual certificate details when it signs.
    key_info = xmlsec.template.ensure_key_info(signature)
    x509_data = xmlsec.template.add_x509_data(key_info)
    xmlsec.template.x509_data_add_issuer_serial(x509_data)
    xmlsec.template.x509_data_add_certificate(x509_data)

    # Load the signing key and certificate.
    key = xmlsec.Key.from_file(keyfile,
                               xmlsec.KeyFormat.PEM,
                               password=password)
    key.load_cert_from_file(certfile, xmlsec.KeyFormat.PEM)

    # Insert the Signature node in the wsse:Security header.
    security = get_security_header(envelope)
    security.insert(0, signature)

    # Perform the actual signing.
    ctx = xmlsec.SignatureContext()
    ctx.key = key

    security.append(etree.Element(QName(ns.WSU, 'Timestamp')))

    soap_env = detect_soap_env(envelope)
    _sign_node(ctx, signature, envelope.find(QName(soap_env, 'Body')))
    _sign_node(ctx, signature, security.find(QName(ns.WSU, 'Timestamp')))

    ctx.sign(signature)

    # Place the X509 data inside a WSSE SecurityTokenReference within
    # KeyInfo. The recipient expects this structure, but we can't rearrange
    # like this until after signing, because otherwise xmlsec won't populate
    # the X509 data (because it doesn't understand WSSE).
    sec_token_ref = etree.SubElement(key_info,
                                     QName(ns.WSSE, 'SecurityTokenReference'))
    sec_token_ref.append(x509_data)