providers/saml: fix ecdsa support (#9537)
* crypto: add option to select which alg to use to generate Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix missing ecdsa options for XML signing Signed-off-by: Jens Langhammer <jens@goauthentik.io> * bump xml libraries and remove disclaimer Signed-off-by: Jens Langhammer <jens@goauthentik.io> * lock djangoframework Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -4,7 +4,7 @@ from django.utils.text import slugify
|
|||||||
|
|
||||||
from authentik.brands.models import Brand
|
from authentik.brands.models import Brand
|
||||||
from authentik.core.models import Group, User
|
from authentik.core.models import Group, User
|
||||||
from authentik.crypto.builder import CertificateBuilder
|
from authentik.crypto.builder import CertificateBuilder, PrivateKeyAlg
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
from authentik.flows.models import Flow, FlowDesignation
|
from authentik.flows.models import Flow, FlowDesignation
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
@ -50,12 +50,10 @@ def create_test_brand(**kwargs) -> Brand:
|
|||||||
return Brand.objects.create(domain=uid, default=True, **kwargs)
|
return Brand.objects.create(domain=uid, default=True, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def create_test_cert(use_ec_private_key=False) -> CertificateKeyPair:
|
def create_test_cert(alg=PrivateKeyAlg.RSA) -> CertificateKeyPair:
|
||||||
"""Generate a certificate for testing"""
|
"""Generate a certificate for testing"""
|
||||||
builder = CertificateBuilder(
|
builder = CertificateBuilder(f"{generate_id()}.self-signed.goauthentik.io")
|
||||||
name=f"{generate_id()}.self-signed.goauthentik.io",
|
builder.alg = alg
|
||||||
use_ec_private_key=use_ec_private_key,
|
|
||||||
)
|
|
||||||
builder.build(
|
builder.build(
|
||||||
subject_alt_names=[f"{generate_id()}.self-signed.goauthentik.io"],
|
subject_alt_names=[f"{generate_id()}.self-signed.goauthentik.io"],
|
||||||
validity_days=360,
|
validity_days=360,
|
||||||
|
@ -14,7 +14,13 @@ from drf_spectacular.types import OpenApiTypes
|
|||||||
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
|
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.fields import CharField, DateTimeField, IntegerField, SerializerMethodField
|
from rest_framework.fields import (
|
||||||
|
CharField,
|
||||||
|
ChoiceField,
|
||||||
|
DateTimeField,
|
||||||
|
IntegerField,
|
||||||
|
SerializerMethodField,
|
||||||
|
)
|
||||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||||
from rest_framework.request import Request
|
from rest_framework.request import Request
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
@ -26,7 +32,7 @@ from authentik.api.authorization import SecretKeyFilter
|
|||||||
from authentik.core.api.used_by import UsedByMixin
|
from authentik.core.api.used_by import UsedByMixin
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
from authentik.crypto.apps import MANAGED_KEY
|
from authentik.crypto.apps import MANAGED_KEY
|
||||||
from authentik.crypto.builder import CertificateBuilder
|
from authentik.crypto.builder import CertificateBuilder, PrivateKeyAlg
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
from authentik.events.models import Event, EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.rbac.decorators import permission_required
|
from authentik.rbac.decorators import permission_required
|
||||||
@ -178,6 +184,7 @@ class CertificateGenerationSerializer(PassiveSerializer):
|
|||||||
common_name = CharField()
|
common_name = CharField()
|
||||||
subject_alt_name = CharField(required=False, allow_blank=True, label=_("Subject-alt name"))
|
subject_alt_name = CharField(required=False, allow_blank=True, label=_("Subject-alt name"))
|
||||||
validity_days = IntegerField(initial=365)
|
validity_days = IntegerField(initial=365)
|
||||||
|
alg = ChoiceField(default=PrivateKeyAlg.RSA, choices=PrivateKeyAlg.choices)
|
||||||
|
|
||||||
|
|
||||||
class CertificateKeyPairFilter(FilterSet):
|
class CertificateKeyPairFilter(FilterSet):
|
||||||
@ -240,6 +247,7 @@ class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet):
|
|||||||
raw_san = data.validated_data.get("subject_alt_name", "")
|
raw_san = data.validated_data.get("subject_alt_name", "")
|
||||||
sans = raw_san.split(",") if raw_san != "" else []
|
sans = raw_san.split(",") if raw_san != "" else []
|
||||||
builder = CertificateBuilder(data.validated_data["common_name"])
|
builder = CertificateBuilder(data.validated_data["common_name"])
|
||||||
|
builder.alg = data.validated_data["alg"]
|
||||||
builder.build(
|
builder.build(
|
||||||
subject_alt_names=sans,
|
subject_alt_names=sans,
|
||||||
validity_days=int(data.validated_data["validity_days"]),
|
validity_days=int(data.validated_data["validity_days"]),
|
||||||
|
@ -9,20 +9,28 @@ from cryptography.hazmat.primitives import hashes, serialization
|
|||||||
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
from cryptography.hazmat.primitives.asymmetric import ec, rsa
|
||||||
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
|
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
|
||||||
from cryptography.x509.oid import NameOID
|
from cryptography.x509.oid import NameOID
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from authentik import __version__
|
from authentik import __version__
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
|
|
||||||
|
|
||||||
|
class PrivateKeyAlg(models.TextChoices):
|
||||||
|
"""Algorithm to create private key with"""
|
||||||
|
|
||||||
|
RSA = "rsa", _("rsa")
|
||||||
|
ECDSA = "ecdsa", _("ecdsa")
|
||||||
|
|
||||||
|
|
||||||
class CertificateBuilder:
|
class CertificateBuilder:
|
||||||
"""Build self-signed certificates"""
|
"""Build self-signed certificates"""
|
||||||
|
|
||||||
common_name: str
|
common_name: str
|
||||||
|
alg: PrivateKeyAlg
|
||||||
|
|
||||||
_use_ec_private_key: bool
|
def __init__(self, name: str):
|
||||||
|
self.alg = PrivateKeyAlg.RSA
|
||||||
def __init__(self, name: str, use_ec_private_key=False):
|
|
||||||
self._use_ec_private_key = use_ec_private_key
|
|
||||||
self.__public_key = None
|
self.__public_key = None
|
||||||
self.__private_key = None
|
self.__private_key = None
|
||||||
self.__builder = None
|
self.__builder = None
|
||||||
@ -42,11 +50,13 @@ class CertificateBuilder:
|
|||||||
|
|
||||||
def generate_private_key(self) -> PrivateKeyTypes:
|
def generate_private_key(self) -> PrivateKeyTypes:
|
||||||
"""Generate private key"""
|
"""Generate private key"""
|
||||||
if self._use_ec_private_key:
|
if self.alg == PrivateKeyAlg.ECDSA:
|
||||||
return ec.generate_private_key(curve=ec.SECP256R1())
|
return ec.generate_private_key(curve=ec.SECP256R1())
|
||||||
return rsa.generate_private_key(
|
if self.alg == PrivateKeyAlg.RSA:
|
||||||
public_exponent=65537, key_size=4096, backend=default_backend()
|
return rsa.generate_private_key(
|
||||||
)
|
public_exponent=65537, key_size=4096, backend=default_backend()
|
||||||
|
)
|
||||||
|
raise ValueError(f"Invalid alg: {self.alg}")
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
self,
|
self,
|
||||||
|
@ -10,6 +10,7 @@ from jwt import PyJWKSet
|
|||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
from authentik.core.tests.utils import create_test_cert, create_test_flow
|
from authentik.core.tests.utils import create_test_cert, create_test_flow
|
||||||
|
from authentik.crypto.builder import PrivateKeyAlg
|
||||||
from authentik.crypto.models import CertificateKeyPair
|
from authentik.crypto.models import CertificateKeyPair
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.providers.oauth2.models import OAuth2Provider
|
from authentik.providers.oauth2.models import OAuth2Provider
|
||||||
@ -82,7 +83,7 @@ class TestJWKS(OAuthTestCase):
|
|||||||
client_id="test",
|
client_id="test",
|
||||||
authorization_flow=create_test_flow(),
|
authorization_flow=create_test_flow(),
|
||||||
redirect_uris="http://local.invalid",
|
redirect_uris="http://local.invalid",
|
||||||
signing_key=create_test_cert(use_ec_private_key=True),
|
signing_key=create_test_cert(PrivateKeyAlg.ECDSA),
|
||||||
)
|
)
|
||||||
app = Application.objects.create(name="test", slug="test", provider=provider)
|
app = Application.objects.create(name="test", slug="test", provider=provider)
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-05-01 15:32
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_providers_saml", "0013_samlprovider_default_relay_state"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="samlprovider",
|
||||||
|
name="digest_algorithm",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("http://www.w3.org/2000/09/xmldsig#sha1", "SHA1"),
|
||||||
|
("http://www.w3.org/2001/04/xmlenc#sha256", "SHA256"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#sha384", "SHA384"),
|
||||||
|
("http://www.w3.org/2001/04/xmlenc#sha512", "SHA512"),
|
||||||
|
],
|
||||||
|
default="http://www.w3.org/2001/04/xmlenc#sha256",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="samlprovider",
|
||||||
|
name="signature_algorithm",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("http://www.w3.org/2000/09/xmldsig#rsa-sha1", "RSA-SHA1"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "RSA-SHA256"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", "RSA-SHA384"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "RSA-SHA512"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1", "ECDSA-SHA1"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", "ECDSA-SHA256"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", "ECDSA-SHA384"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", "ECDSA-SHA512"),
|
||||||
|
("http://www.w3.org/2000/09/xmldsig#dsa-sha1", "DSA-SHA1"),
|
||||||
|
],
|
||||||
|
default="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -11,6 +11,10 @@ from authentik.crypto.models import CertificateKeyPair
|
|||||||
from authentik.lib.utils.time import timedelta_string_validator
|
from authentik.lib.utils.time import timedelta_string_validator
|
||||||
from authentik.sources.saml.processors.constants import (
|
from authentik.sources.saml.processors.constants import (
|
||||||
DSA_SHA1,
|
DSA_SHA1,
|
||||||
|
ECDSA_SHA1,
|
||||||
|
ECDSA_SHA256,
|
||||||
|
ECDSA_SHA384,
|
||||||
|
ECDSA_SHA512,
|
||||||
RSA_SHA1,
|
RSA_SHA1,
|
||||||
RSA_SHA256,
|
RSA_SHA256,
|
||||||
RSA_SHA384,
|
RSA_SHA384,
|
||||||
@ -92,8 +96,7 @@ class SAMLProvider(Provider):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
digest_algorithm = models.CharField(
|
digest_algorithm = models.TextField(
|
||||||
max_length=50,
|
|
||||||
choices=(
|
choices=(
|
||||||
(SHA1, _("SHA1")),
|
(SHA1, _("SHA1")),
|
||||||
(SHA256, _("SHA256")),
|
(SHA256, _("SHA256")),
|
||||||
@ -102,13 +105,16 @@ class SAMLProvider(Provider):
|
|||||||
),
|
),
|
||||||
default=SHA256,
|
default=SHA256,
|
||||||
)
|
)
|
||||||
signature_algorithm = models.CharField(
|
signature_algorithm = models.TextField(
|
||||||
max_length=50,
|
|
||||||
choices=(
|
choices=(
|
||||||
(RSA_SHA1, _("RSA-SHA1")),
|
(RSA_SHA1, _("RSA-SHA1")),
|
||||||
(RSA_SHA256, _("RSA-SHA256")),
|
(RSA_SHA256, _("RSA-SHA256")),
|
||||||
(RSA_SHA384, _("RSA-SHA384")),
|
(RSA_SHA384, _("RSA-SHA384")),
|
||||||
(RSA_SHA512, _("RSA-SHA512")),
|
(RSA_SHA512, _("RSA-SHA512")),
|
||||||
|
(ECDSA_SHA1, _("ECDSA-SHA1")),
|
||||||
|
(ECDSA_SHA256, _("ECDSA-SHA256")),
|
||||||
|
(ECDSA_SHA384, _("ECDSA-SHA384")),
|
||||||
|
(ECDSA_SHA512, _("ECDSA-SHA512")),
|
||||||
(DSA_SHA1, _("DSA-SHA1")),
|
(DSA_SHA1, _("DSA-SHA1")),
|
||||||
),
|
),
|
||||||
default=RSA_SHA256,
|
default=RSA_SHA256,
|
||||||
|
@ -7,13 +7,14 @@ from lxml import etree # nosec
|
|||||||
|
|
||||||
from authentik.core.models import Application
|
from authentik.core.models import Application
|
||||||
from authentik.core.tests.utils import create_test_cert, create_test_flow
|
from authentik.core.tests.utils import create_test_cert, create_test_flow
|
||||||
|
from authentik.crypto.builder import PrivateKeyAlg
|
||||||
from authentik.lib.generators import generate_id
|
from authentik.lib.generators import generate_id
|
||||||
from authentik.lib.tests.utils import load_fixture
|
from authentik.lib.tests.utils import load_fixture
|
||||||
from authentik.lib.xml import lxml_from_string
|
from authentik.lib.xml import lxml_from_string
|
||||||
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
|
from authentik.providers.saml.models import SAMLBindings, SAMLPropertyMapping, SAMLProvider
|
||||||
from authentik.providers.saml.processors.metadata import MetadataProcessor
|
from authentik.providers.saml.processors.metadata import MetadataProcessor
|
||||||
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
|
from authentik.providers.saml.processors.metadata_parser import ServiceProviderMetadataParser
|
||||||
from authentik.sources.saml.processors.constants import NS_MAP, NS_SAML_METADATA
|
from authentik.sources.saml.processors.constants import ECDSA_SHA256, NS_MAP, NS_SAML_METADATA
|
||||||
|
|
||||||
|
|
||||||
class TestServiceProviderMetadataParser(TestCase):
|
class TestServiceProviderMetadataParser(TestCase):
|
||||||
@ -107,12 +108,41 @@ class TestServiceProviderMetadataParser(TestCase):
|
|||||||
load_fixture("fixtures/cert.xml").replace("/apps/user_saml", "")
|
load_fixture("fixtures/cert.xml").replace("/apps/user_saml", "")
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_signature(self):
|
def test_signature_rsa(self):
|
||||||
"""Test signature validation"""
|
"""Test signature validation (RSA)"""
|
||||||
provider = SAMLProvider.objects.create(
|
provider = SAMLProvider.objects.create(
|
||||||
name=generate_id(),
|
name=generate_id(),
|
||||||
authorization_flow=self.flow,
|
authorization_flow=self.flow,
|
||||||
signing_kp=create_test_cert(),
|
signing_kp=create_test_cert(PrivateKeyAlg.RSA),
|
||||||
|
)
|
||||||
|
Application.objects.create(
|
||||||
|
name=generate_id(),
|
||||||
|
slug=generate_id(),
|
||||||
|
provider=provider,
|
||||||
|
)
|
||||||
|
request = self.factory.get("/")
|
||||||
|
metadata = MetadataProcessor(provider, request).build_entity_descriptor()
|
||||||
|
|
||||||
|
root = fromstring(metadata.encode())
|
||||||
|
xmlsec.tree.add_ids(root, ["ID"])
|
||||||
|
signature_nodes = root.xpath("/md:EntityDescriptor/ds:Signature", namespaces=NS_MAP)
|
||||||
|
signature_node = signature_nodes[0]
|
||||||
|
ctx = xmlsec.SignatureContext()
|
||||||
|
key = xmlsec.Key.from_memory(
|
||||||
|
provider.signing_kp.certificate_data,
|
||||||
|
xmlsec.constants.KeyDataFormatCertPem,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
ctx.key = key
|
||||||
|
ctx.verify(signature_node)
|
||||||
|
|
||||||
|
def test_signature_ecdsa(self):
|
||||||
|
"""Test signature validation (ECDSA)"""
|
||||||
|
provider = SAMLProvider.objects.create(
|
||||||
|
name=generate_id(),
|
||||||
|
authorization_flow=self.flow,
|
||||||
|
signing_kp=create_test_cert(PrivateKeyAlg.ECDSA),
|
||||||
|
signature_algorithm=ECDSA_SHA256,
|
||||||
)
|
)
|
||||||
Application.objects.create(
|
Application.objects.create(
|
||||||
name=generate_id(),
|
name=generate_id(),
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-05-01 15:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_sources_saml", "0013_samlsource_verification_kp_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="samlsource",
|
||||||
|
name="digest_algorithm",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("http://www.w3.org/2000/09/xmldsig#sha1", "SHA1"),
|
||||||
|
("http://www.w3.org/2001/04/xmlenc#sha256", "SHA256"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#sha384", "SHA384"),
|
||||||
|
("http://www.w3.org/2001/04/xmlenc#sha512", "SHA512"),
|
||||||
|
],
|
||||||
|
default="http://www.w3.org/2001/04/xmlenc#sha256",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="samlsource",
|
||||||
|
name="signature_algorithm",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("http://www.w3.org/2000/09/xmldsig#rsa-sha1", "RSA-SHA1"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "RSA-SHA256"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", "RSA-SHA384"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "RSA-SHA512"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1", "ECDSA-SHA1"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", "ECDSA-SHA256"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", "ECDSA-SHA384"),
|
||||||
|
("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", "ECDSA-SHA512"),
|
||||||
|
("http://www.w3.org/2000/09/xmldsig#dsa-sha1", "DSA-SHA1"),
|
||||||
|
],
|
||||||
|
default="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -15,6 +15,10 @@ from authentik.flows.models import Flow
|
|||||||
from authentik.lib.utils.time import timedelta_string_validator
|
from authentik.lib.utils.time import timedelta_string_validator
|
||||||
from authentik.sources.saml.processors.constants import (
|
from authentik.sources.saml.processors.constants import (
|
||||||
DSA_SHA1,
|
DSA_SHA1,
|
||||||
|
ECDSA_SHA1,
|
||||||
|
ECDSA_SHA256,
|
||||||
|
ECDSA_SHA384,
|
||||||
|
ECDSA_SHA512,
|
||||||
RSA_SHA1,
|
RSA_SHA1,
|
||||||
RSA_SHA256,
|
RSA_SHA256,
|
||||||
RSA_SHA384,
|
RSA_SHA384,
|
||||||
@ -143,8 +147,7 @@ class SAMLSource(Source):
|
|||||||
verbose_name=_("Signing Keypair"),
|
verbose_name=_("Signing Keypair"),
|
||||||
)
|
)
|
||||||
|
|
||||||
digest_algorithm = models.CharField(
|
digest_algorithm = models.TextField(
|
||||||
max_length=50,
|
|
||||||
choices=(
|
choices=(
|
||||||
(SHA1, _("SHA1")),
|
(SHA1, _("SHA1")),
|
||||||
(SHA256, _("SHA256")),
|
(SHA256, _("SHA256")),
|
||||||
@ -153,13 +156,16 @@ class SAMLSource(Source):
|
|||||||
),
|
),
|
||||||
default=SHA256,
|
default=SHA256,
|
||||||
)
|
)
|
||||||
signature_algorithm = models.CharField(
|
signature_algorithm = models.TextField(
|
||||||
max_length=50,
|
|
||||||
choices=(
|
choices=(
|
||||||
(RSA_SHA1, _("RSA-SHA1")),
|
(RSA_SHA1, _("RSA-SHA1")),
|
||||||
(RSA_SHA256, _("RSA-SHA256")),
|
(RSA_SHA256, _("RSA-SHA256")),
|
||||||
(RSA_SHA384, _("RSA-SHA384")),
|
(RSA_SHA384, _("RSA-SHA384")),
|
||||||
(RSA_SHA512, _("RSA-SHA512")),
|
(RSA_SHA512, _("RSA-SHA512")),
|
||||||
|
(ECDSA_SHA1, _("ECDSA-SHA1")),
|
||||||
|
(ECDSA_SHA256, _("ECDSA-SHA256")),
|
||||||
|
(ECDSA_SHA384, _("ECDSA-SHA384")),
|
||||||
|
(ECDSA_SHA512, _("ECDSA-SHA512")),
|
||||||
(DSA_SHA1, _("DSA-SHA1")),
|
(DSA_SHA1, _("DSA-SHA1")),
|
||||||
),
|
),
|
||||||
default=RSA_SHA256,
|
default=RSA_SHA256,
|
||||||
|
@ -26,9 +26,16 @@ SAML_BINDING_REDIRECT = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
|||||||
|
|
||||||
DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"
|
DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"
|
||||||
RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
|
RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
|
||||||
|
# https://datatracker.ietf.org/doc/html/rfc4051#section-2.3.2
|
||||||
RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
|
RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
|
||||||
RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
|
RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
|
||||||
RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
|
RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
|
||||||
|
# https://datatracker.ietf.org/doc/html/rfc4051#section-2.3.6
|
||||||
|
ECDSA_SHA1 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1"
|
||||||
|
ECDSA_SHA224 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha224"
|
||||||
|
ECDSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"
|
||||||
|
ECDSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384"
|
||||||
|
ECDSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512"
|
||||||
|
|
||||||
SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
|
SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
|
||||||
SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256"
|
SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256"
|
||||||
@ -41,6 +48,11 @@ SIGN_ALGORITHM_TRANSFORM_MAP = {
|
|||||||
RSA_SHA256: xmlsec.constants.TransformRsaSha256,
|
RSA_SHA256: xmlsec.constants.TransformRsaSha256,
|
||||||
RSA_SHA384: xmlsec.constants.TransformRsaSha384,
|
RSA_SHA384: xmlsec.constants.TransformRsaSha384,
|
||||||
RSA_SHA512: xmlsec.constants.TransformRsaSha512,
|
RSA_SHA512: xmlsec.constants.TransformRsaSha512,
|
||||||
|
ECDSA_SHA1: xmlsec.constants.TransformEcdsaSha1,
|
||||||
|
ECDSA_SHA224: xmlsec.constants.TransformEcdsaSha224,
|
||||||
|
ECDSA_SHA256: xmlsec.constants.TransformEcdsaSha256,
|
||||||
|
ECDSA_SHA384: xmlsec.constants.TransformEcdsaSha384,
|
||||||
|
ECDSA_SHA512: xmlsec.constants.TransformEcdsaSha512,
|
||||||
}
|
}
|
||||||
|
|
||||||
DIGEST_ALGORITHM_TRANSLATION_MAP = {
|
DIGEST_ALGORITHM_TRANSLATION_MAP = {
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-05-01 15:32
|
||||||
|
|
||||||
|
import authentik.lib.utils.time
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_tenants", "0002_tenant_default_token_duration_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="tenant",
|
||||||
|
name="default_token_duration",
|
||||||
|
field=models.TextField(
|
||||||
|
default="days=1",
|
||||||
|
help_text="Default token duration",
|
||||||
|
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -4131,6 +4131,10 @@
|
|||||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
||||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
|
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
|
||||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
|
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1",
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256",
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384",
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512",
|
||||||
"http://www.w3.org/2000/09/xmldsig#dsa-sha1"
|
"http://www.w3.org/2000/09/xmldsig#dsa-sha1"
|
||||||
],
|
],
|
||||||
"title": "Signature algorithm"
|
"title": "Signature algorithm"
|
||||||
@ -4935,6 +4939,10 @@
|
|||||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
|
||||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
|
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
|
||||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
|
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1",
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256",
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384",
|
||||||
|
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512",
|
||||||
"http://www.w3.org/2000/09/xmldsig#dsa-sha1"
|
"http://www.w3.org/2000/09/xmldsig#dsa-sha1"
|
||||||
],
|
],
|
||||||
"title": "Signature algorithm"
|
"title": "Signature algorithm"
|
||||||
|
1914
poetry.lock
generated
1914
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -89,6 +89,7 @@ channels = { version = "*", extras = ["daphne"] }
|
|||||||
channels-redis = "*"
|
channels-redis = "*"
|
||||||
codespell = "*"
|
codespell = "*"
|
||||||
colorama = "*"
|
colorama = "*"
|
||||||
|
cryptography = "*"
|
||||||
dacite = "*"
|
dacite = "*"
|
||||||
deepmerge = "*"
|
deepmerge = "*"
|
||||||
defusedxml = "*"
|
defusedxml = "*"
|
||||||
@ -101,7 +102,7 @@ django-redis = "*"
|
|||||||
django-storages = { extras = ["s3"], version = "*" }
|
django-storages = { extras = ["s3"], version = "*" }
|
||||||
# See https://github.com/django-tenants/django-tenants/pull/997
|
# See https://github.com/django-tenants/django-tenants/pull/997
|
||||||
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch="authentik-fixes" }
|
django-tenants = { git = "https://github.com/rissson/django-tenants.git", branch="authentik-fixes" }
|
||||||
djangorestframework = "*"
|
djangorestframework = "3.14.0"
|
||||||
djangorestframework-guardian = "*"
|
djangorestframework-guardian = "*"
|
||||||
docker = "*"
|
docker = "*"
|
||||||
drf-spectacular = "*"
|
drf-spectacular = "*"
|
||||||
@ -115,17 +116,11 @@ gunicorn = "*"
|
|||||||
jsonpatch = "*"
|
jsonpatch = "*"
|
||||||
kubernetes = "*"
|
kubernetes = "*"
|
||||||
ldap3 = "*"
|
ldap3 = "*"
|
||||||
lxml = [
|
lxml = "*"
|
||||||
# 5.0.0 works with libxml2 2.11.x, which is standard on brew
|
|
||||||
{ version = "5.0.0", platform = "darwin" },
|
|
||||||
# 4.9.x works with previous libxml2 versions, which is what we get on linux
|
|
||||||
{ version = "4.9.4", platform = "linux" },
|
|
||||||
]
|
|
||||||
opencontainers = { extras = ["reggie"], version = "*" }
|
opencontainers = { extras = ["reggie"], version = "*" }
|
||||||
packaging = "*"
|
packaging = "*"
|
||||||
paramiko = "*"
|
paramiko = "*"
|
||||||
psycopg = { extras = ["c"], version = "*" }
|
psycopg = { extras = ["c"], version = "*" }
|
||||||
pycryptodome = "*"
|
|
||||||
pydantic = "*"
|
pydantic = "*"
|
||||||
pydantic-scim = "*"
|
pydantic-scim = "*"
|
||||||
pyjwt = "*"
|
pyjwt = "*"
|
||||||
|
21
schema.yml
21
schema.yml
@ -17051,6 +17051,10 @@ paths:
|
|||||||
enum:
|
enum:
|
||||||
- http://www.w3.org/2000/09/xmldsig#dsa-sha1
|
- http://www.w3.org/2000/09/xmldsig#dsa-sha1
|
||||||
- http://www.w3.org/2000/09/xmldsig#rsa-sha1
|
- http://www.w3.org/2000/09/xmldsig#rsa-sha1
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512
|
||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
|
||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
|
||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
|
||||||
@ -20910,6 +20914,10 @@ paths:
|
|||||||
enum:
|
enum:
|
||||||
- http://www.w3.org/2000/09/xmldsig#dsa-sha1
|
- http://www.w3.org/2000/09/xmldsig#dsa-sha1
|
||||||
- http://www.w3.org/2000/09/xmldsig#rsa-sha1
|
- http://www.w3.org/2000/09/xmldsig#rsa-sha1
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512
|
||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
|
||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
|
||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
|
||||||
@ -30450,6 +30458,11 @@ components:
|
|||||||
- pending_user
|
- pending_user
|
||||||
- pending_user_avatar
|
- pending_user_avatar
|
||||||
- type
|
- type
|
||||||
|
AlgEnum:
|
||||||
|
enum:
|
||||||
|
- rsa
|
||||||
|
- ecdsa
|
||||||
|
type: string
|
||||||
App:
|
App:
|
||||||
type: object
|
type: object
|
||||||
description: Serialize Application info
|
description: Serialize Application info
|
||||||
@ -32107,6 +32120,10 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
validity_days:
|
validity_days:
|
||||||
type: integer
|
type: integer
|
||||||
|
alg:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/AlgEnum'
|
||||||
|
default: rsa
|
||||||
required:
|
required:
|
||||||
- common_name
|
- common_name
|
||||||
- validity_days
|
- validity_days
|
||||||
@ -43658,6 +43675,10 @@ components:
|
|||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
|
||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
|
||||||
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
|
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384
|
||||||
|
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512
|
||||||
- http://www.w3.org/2000/09/xmldsig#dsa-sha1
|
- http://www.w3.org/2000/09/xmldsig#dsa-sha1
|
||||||
type: string
|
type: string
|
||||||
Source:
|
Source:
|
||||||
|
@ -29,5 +29,9 @@ export const signatureAlgorithmOptions = toOptions([
|
|||||||
["RSA-SHA256", SignatureAlgorithmEnum._200104XmldsigMorersaSha256, true],
|
["RSA-SHA256", SignatureAlgorithmEnum._200104XmldsigMorersaSha256, true],
|
||||||
["RSA-SHA384", SignatureAlgorithmEnum._200104XmldsigMorersaSha384],
|
["RSA-SHA384", SignatureAlgorithmEnum._200104XmldsigMorersaSha384],
|
||||||
["RSA-SHA512", SignatureAlgorithmEnum._200104XmldsigMorersaSha512],
|
["RSA-SHA512", SignatureAlgorithmEnum._200104XmldsigMorersaSha512],
|
||||||
|
["ECDSA-SHA1", SignatureAlgorithmEnum._200104XmldsigMoreecdsaSha1],
|
||||||
|
["ECDSA-SHA256", SignatureAlgorithmEnum._200104XmldsigMoreecdsaSha256],
|
||||||
|
["ECDSA-SHA384", SignatureAlgorithmEnum._200104XmldsigMoreecdsaSha384],
|
||||||
|
["ECDSA-SHA512", SignatureAlgorithmEnum._200104XmldsigMoreecdsaSha512],
|
||||||
["DSA-SHA1", SignatureAlgorithmEnum._200009XmldsigdsaSha1],
|
["DSA-SHA1", SignatureAlgorithmEnum._200009XmldsigdsaSha1],
|
||||||
]);
|
]);
|
||||||
|
@ -6,7 +6,12 @@ import { msg } from "@lit/localize";
|
|||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
|
|
||||||
import { CertificateGenerationRequest, CertificateKeyPair, CryptoApi } from "@goauthentik/api";
|
import {
|
||||||
|
AlgEnum,
|
||||||
|
CertificateGenerationRequest,
|
||||||
|
CertificateKeyPair,
|
||||||
|
CryptoApi,
|
||||||
|
} from "@goauthentik/api";
|
||||||
|
|
||||||
@customElement("ak-crypto-certificate-generate-form")
|
@customElement("ak-crypto-certificate-generate-form")
|
||||||
export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
|
export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
|
||||||
@ -40,6 +45,29 @@ export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
|
|||||||
?required=${true}
|
?required=${true}
|
||||||
>
|
>
|
||||||
<input class="pf-c-form-control" type="number" value="365" />
|
<input class="pf-c-form-control" type="number" value="365" />
|
||||||
</ak-form-element-horizontal>`;
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${msg("Private key Algorithm")}
|
||||||
|
?required=${true}
|
||||||
|
name="alg"
|
||||||
|
>
|
||||||
|
<ak-radio
|
||||||
|
.options=${[
|
||||||
|
{
|
||||||
|
label: msg("RSA"),
|
||||||
|
value: AlgEnum.Rsa,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: msg("ECDSA"),
|
||||||
|
value: AlgEnum.Ecdsa,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
</ak-radio>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${msg("Algorithm used to generate the private key.")}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal> `;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,6 @@ If you use locally installed databases, the PostgreSQL credentials given to auth
|
|||||||
Depending on your platform, some native dependencies might be required. On macOS, run `brew install libxmlsec1 libpq`, and for the CLI tools `brew install postgresql redis node@20`
|
Depending on your platform, some native dependencies might be required. On macOS, run `brew install libxmlsec1 libpq`, and for the CLI tools `brew install postgresql redis node@20`
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::info
|
|
||||||
As long as [this issue](https://github.com/xmlsec/python-xmlsec/issues/252) about `libxmlsec-1.3.0` is open, a workaround is required to install a compatible version of `libxmlsec1` with brew, see [this comment](https://github.com/xmlsec/python-xmlsec/issues/254#issuecomment-1612005910).
|
|
||||||
:::
|
|
||||||
|
|
||||||
1. Create an isolated Python environment. To create the environment and install dependencies, run the following commands in the same directory as your local authentik git repository:
|
1. Create an isolated Python environment. To create the environment and install dependencies, run the following commands in the same directory as your local authentik git repository:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
Reference in New Issue
Block a user