pysaml2 icon indicating copy to clipboard operation
pysaml2 copied to clipboard

Help Creating a Subject/SubjectConfirmation/SubjectConfirmationData/KeyInfo/KeyValue/RSAKeyValue Element

Open vincehartman38 opened this issue 1 year ago • 0 comments

We need the SAML Assertion to create the Subject element with the following structure: Subject/SubjectConfirmation/SubjectConfirmationData/KeyInfo/KeyValue/RSAKeyValue element. Otherwise Signature verification fails based on our partner requirements. I have updated the SubjectConfirmationData() element for SCM_HOLDER_OF_KEY. I am unsure though how to have the KeyInfo -> KeyInfoValue -> RSAKeyValue element to populate in this section during signing. I have really tried to figure this out but unsure based on the documentation.

Here is the code in question:

    ###Create the SAML assertion ###
    ###the audience should be the url of the server that will receive the SAML assertion
    ###the role should be the role of the requester of the data, e.g. MedicalDoctor
    ###the purposeOfUse should be the purpose of use of the data, e.g. TREATMENT, possible values are: https://www.hl7.org/fhir/codesystem-nhin-purposeofuse.html
    issuer = ISSUER
    not_on_or_after = self.issued_at + timedelta(hours=1)
    refID = str(uuid.uuid4())

    # Create SAML assertion
    issuer = Issuer(name_qualifier=NAMEID_FORMAT_X509SUBJECTNAME, text=CERT_SUBJECT)
    subjectConfirmationData = SubjectConfirmationData()
    subject = Subject(
       name_id=NameID(format=NAMEID_FORMAT_X509SUBJECTNAME, text=CERT_SUBJECT),
       subject_confirmation=SubjectConfirmation(
          method=SCM_HOLDER_OF_KEY,
          subject_confirmation_data=subjectConfirmationData
          )
    )

    # Create the attribute statement
    attributes = [
        Attribute(
            name="urn:oasis:names:tc:xspa:1.0:subject:subject-id",
            friendly_name="XSPA Subject",
            name_format="urn:oasis:names:tc:SAML:2.0:attrname-format:basic",
            attribute_value=AttributeValue("valid")
        ),
        Attribute(
            name="urn:oasis:names:tc:xspa:1.0:subject:organization",
            friendly_name="XSPA Organization",
            name_format="urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
            attribute_value=AttributeValue(ORGANIZATION)
        ),
        Attribute(
            name="urn:oasis:names:tc:xspa:1.0:subject:organization-id",
            friendly_name="XSPA Organization ID",
            name_format="urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
            attribute_value=AttributeValue(ORGANIZATION_ID)
        ),
        Attribute(
            name="urn:ihe:iti:xca:2010:homeCommunityId",
            friendly_name="XCA Home Community ID",
            name_format="urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
            attribute_value=AttributeValue(HOME_COMMUNITY_ID)
        ),
        Attribute(
            name="urn:oasis:names:tc:xspa:1.0:subject:purposeofuse",
            friendly_name="Purpose of Use",
            attribute_value=AttributeValue(purposeOfUse)
        ),
        Attribute(
            name="urn:oasis:names:tc:xacml:2.0:subject:role",
            friendly_name="HL7 Role",
            attribute_value=AttributeValue(role)
        )
    ]

    attribute_statement = AttributeStatement(attribute=attributes)
    conditions = Conditions(
        not_before=self.issued_at.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z',
        not_on_or_after=not_on_or_after.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z',
        audience_restriction=AudienceRestriction([
            Audience(audience)
        ])
    )

    authn_statement = AuthnStatement(
      authn_instant=self.issued_at.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z',
      authn_context=AuthnContext(
        authn_context_class_ref=AuthnContextClassRef(
          text="urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
        )
      )
    )

    assertion = Assertion(
        id="_"+refID,
        issue_instant=self.issued_at.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z',
        issuer=issuer,
        subject=subject,
        conditions=conditions,
        attribute_statement=attribute_statement,
        version="2.0",
        authn_statement=authn_statement
    )

    assertion_string = str(assertion)
    
    assertion_string = assertion_string.replace("Issuer>","Issuer>" + "<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" Id=\"placeholder\"></ds:Signature>")
    assertion_string = assertion_string.replace("ns0", "samlns")


    signed_saml_root = XMLSigner(method=signxml.methods.enveloped, c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#")\
        .sign(saml_root, key=self.key, cert=self.cert, always_add_key_value=True)
    verified_data = XMLVerifier().verify(signed_saml_root, x509_cert=self.cert).signed_xml

Appreciate if someone could advise on how to do this with

vincehartman38 avatar Mar 10 '24 01:03 vincehartman38