Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
vincehartman38 opened this issue Mar 10, 2024 · 0 comments

Comments

@vincehartman38
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant