providers/saml: add changeable signature and digest algorithm
This commit is contained in:
parent
41689fe3ce
commit
a5629c5155
|
@ -40,6 +40,8 @@ class SAMLProviderForm(forms.ModelForm):
|
||||||
"assertion_valid_not_on_or_after",
|
"assertion_valid_not_on_or_after",
|
||||||
"session_valid_not_on_or_after",
|
"session_valid_not_on_or_after",
|
||||||
"property_mappings",
|
"property_mappings",
|
||||||
|
"digest_algorithm",
|
||||||
|
"signature_algorithm",
|
||||||
"signing",
|
"signing",
|
||||||
"signing_cert",
|
"signing_cert",
|
||||||
"signing_key",
|
"signing_key",
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Generated by Django 3.0.3 on 2020-02-17 15:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("passbook_providers_saml", "0003_auto_20200216_1109"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="samlprovider",
|
||||||
|
name="digest_algorithm",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[("sha1", "SHA1"), ("sha256", "SHA256")],
|
||||||
|
default="sha256",
|
||||||
|
max_length=50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="samlprovider",
|
||||||
|
name="signature_algorithm",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("rsa-sha1", "RSA-SHA1"),
|
||||||
|
("rsa-sha256", "RSA-SHA256"),
|
||||||
|
("ecdsa-sha256", "ECDSA-SHA256"),
|
||||||
|
("dsa-sha1", "DSA-SHA1"),
|
||||||
|
],
|
||||||
|
default="rsa-sha256",
|
||||||
|
max_length=50,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="samlprovider",
|
||||||
|
name="processor_path",
|
||||||
|
field=models.CharField(choices=[], max_length=255),
|
||||||
|
),
|
||||||
|
]
|
|
@ -55,6 +55,22 @@ class SAMLProvider(Provider):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
digest_algorithm = models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
choices=(("sha1", _("SHA1")), ("sha256", _("SHA256")),),
|
||||||
|
default="sha256",
|
||||||
|
)
|
||||||
|
signature_algorithm = models.CharField(
|
||||||
|
max_length=50,
|
||||||
|
choices=(
|
||||||
|
("rsa-sha1", _("RSA-SHA1")),
|
||||||
|
("rsa-sha256", _("RSA-SHA256")),
|
||||||
|
("ecdsa-sha256", _("ECDSA-SHA256")),
|
||||||
|
("dsa-sha1", _("DSA-SHA1")),
|
||||||
|
),
|
||||||
|
default="rsa-sha256",
|
||||||
|
)
|
||||||
|
|
||||||
signing = models.BooleanField(default=True)
|
signing = models.BooleanField(default=True)
|
||||||
signing_cert = models.TextField(verbose_name=_("Singing Certificate"))
|
signing_cert = models.TextField(verbose_name=_("Singing Certificate"))
|
||||||
signing_key = models.TextField()
|
signing_key = models.TextField()
|
||||||
|
|
|
@ -88,10 +88,5 @@ def get_response_xml(parameters, saml_provider: SAMLProvider, assertion_id=""):
|
||||||
signature_xml = get_signature_xml()
|
signature_xml = get_signature_xml()
|
||||||
params["RESPONSE_SIGNATURE"] = signature_xml
|
params["RESPONSE_SIGNATURE"] = signature_xml
|
||||||
|
|
||||||
signed = sign_with_signxml(
|
signed = sign_with_signxml(raw_response, saml_provider, reference_uri=assertion_id,)
|
||||||
saml_provider.signing_key,
|
|
||||||
raw_response,
|
|
||||||
saml_provider.signing_cert,
|
|
||||||
reference_uri=assertion_id,
|
|
||||||
)
|
|
||||||
return signed
|
return signed
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""Signing code goes here."""
|
"""Signing code goes here."""
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
from lxml import etree # nosec
|
from lxml import etree # nosec
|
||||||
|
@ -7,25 +9,34 @@ from structlog import get_logger
|
||||||
|
|
||||||
from passbook.lib.utils.template import render_to_string
|
from passbook.lib.utils.template import render_to_string
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from passbook.providers.saml.models import SAMLProvider
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
||||||
def sign_with_signxml(private_key, data, cert, reference_uri=None):
|
def sign_with_signxml(data: str, provider: "SAMLProvider", reference_uri=None) -> str:
|
||||||
"""Sign Data with signxml"""
|
"""Sign Data with signxml"""
|
||||||
key = serialization.load_pem_private_key(
|
key = serialization.load_pem_private_key(
|
||||||
str.encode("\n".join([x.strip() for x in private_key.split("\n")])),
|
str.encode("\n".join([x.strip() for x in provider.signing_key.split("\n")])),
|
||||||
password=None,
|
password=None,
|
||||||
backend=default_backend(),
|
backend=default_backend(),
|
||||||
)
|
)
|
||||||
# defused XML is not used here because it messes up XML namespaces
|
# defused XML is not used here because it messes up XML namespaces
|
||||||
# Data is trusted, so lxml is ok
|
# Data is trusted, so lxml is ok
|
||||||
root = etree.fromstring(data) # nosec
|
root = etree.fromstring(data) # nosec
|
||||||
signer = XMLSigner(c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#")
|
signer = XMLSigner(
|
||||||
signed = signer.sign(root, key=key, cert=[cert], reference_uri=reference_uri)
|
c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#",
|
||||||
XMLVerifier().verify(signed, x509_cert=cert)
|
signature_algorithm=provider.signature_algorithm,
|
||||||
|
digest_algorithm=provider.digest_algorithm,
|
||||||
|
)
|
||||||
|
signed = signer.sign(
|
||||||
|
root, key=key, cert=[provider.signing_cert], reference_uri=reference_uri
|
||||||
|
)
|
||||||
|
XMLVerifier().verify(signed, x509_cert=provider.signing_cert)
|
||||||
return etree.tostring(signed).decode("utf-8") # nosec
|
return etree.tostring(signed).decode("utf-8") # nosec
|
||||||
|
|
||||||
|
|
||||||
def get_signature_xml():
|
def get_signature_xml() -> str:
|
||||||
"""Returns XML Signature for subject."""
|
"""Returns XML Signature for subject."""
|
||||||
return render_to_string("saml/xml/signature.xml", {})
|
return render_to_string("saml/xml/signature.xml", {})
|
||||||
|
|
Reference in New Issue