providers/saml: switch to new crypto
This commit is contained in:
		@ -24,9 +24,7 @@ class SAMLProviderSerializer(ModelSerializer):
 | 
			
		||||
            "property_mappings",
 | 
			
		||||
            "digest_algorithm",
 | 
			
		||||
            "signature_algorithm",
 | 
			
		||||
            "signing",
 | 
			
		||||
            "signing_cert",
 | 
			
		||||
            "signing_key",
 | 
			
		||||
            "singing_kp",
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,6 @@ from passbook.providers.saml.models import (
 | 
			
		||||
    SAMLProvider,
 | 
			
		||||
    get_provider_choices,
 | 
			
		||||
)
 | 
			
		||||
from passbook.providers.saml.utils.cert import CertificateBuilder
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SAMLProviderForm(forms.ModelForm):
 | 
			
		||||
@ -19,13 +18,6 @@ class SAMLProviderForm(forms.ModelForm):
 | 
			
		||||
        choices=get_provider_choices(), label="Processor"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        builder = CertificateBuilder()
 | 
			
		||||
        builder.build()
 | 
			
		||||
        self.fields["signing_cert"].initial = builder.certificate
 | 
			
		||||
        self.fields["signing_key"].initial = builder.private_key
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
 | 
			
		||||
        model = SAMLProvider
 | 
			
		||||
@ -41,9 +33,7 @@ class SAMLProviderForm(forms.ModelForm):
 | 
			
		||||
            "property_mappings",
 | 
			
		||||
            "digest_algorithm",
 | 
			
		||||
            "signature_algorithm",
 | 
			
		||||
            "signing",
 | 
			
		||||
            "signing_cert",
 | 
			
		||||
            "signing_key",
 | 
			
		||||
            "singing_kp",
 | 
			
		||||
        ]
 | 
			
		||||
        widgets = {
 | 
			
		||||
            "name": forms.TextInput(),
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,29 @@
 | 
			
		||||
# Generated by Django 3.0.3 on 2020-03-03 21:57
 | 
			
		||||
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("passbook_crypto", "0001_initial"),
 | 
			
		||||
        ("passbook_providers_saml", "0006_auto_20200217_2031"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(model_name="samlprovider", name="signing",),
 | 
			
		||||
        migrations.RemoveField(model_name="samlprovider", name="signing_cert",),
 | 
			
		||||
        migrations.RemoveField(model_name="samlprovider", name="signing_key",),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="samlprovider",
 | 
			
		||||
            name="singing_kp",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                default=None,
 | 
			
		||||
                help_text="Singing is enabled upon selection of a Key Pair.",
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.SET_NULL,
 | 
			
		||||
                to="passbook_crypto.CertificateKeyPair",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@ -8,6 +8,7 @@ from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from structlog import get_logger
 | 
			
		||||
 | 
			
		||||
from passbook.core.models import PropertyMapping, Provider
 | 
			
		||||
from passbook.crypto.models import CertificateKeyPair
 | 
			
		||||
from passbook.lib.utils.reflection import class_to_path, path_to_class
 | 
			
		||||
from passbook.lib.utils.template import render_to_string
 | 
			
		||||
from passbook.providers.saml.processors.base import Processor
 | 
			
		||||
@ -74,9 +75,13 @@ class SAMLProvider(Provider):
 | 
			
		||||
        default="rsa-sha256",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    signing = models.BooleanField(default=True)
 | 
			
		||||
    signing_cert = models.TextField(verbose_name=_("Singing Certificate"))
 | 
			
		||||
    signing_key = models.TextField()
 | 
			
		||||
    singing_kp = models.ForeignKey(
 | 
			
		||||
        CertificateKeyPair,
 | 
			
		||||
        default=None,
 | 
			
		||||
        null=True,
 | 
			
		||||
        help_text=_("Singing is enabled upon selection of a Key Pair."),
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    form = "passbook.providers.saml.forms.SAMLProviderForm"
 | 
			
		||||
    _processor = None
 | 
			
		||||
 | 
			
		||||
@ -1,84 +0,0 @@
 | 
			
		||||
"""Create self-signed certificates"""
 | 
			
		||||
import datetime
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
from cryptography import x509
 | 
			
		||||
from cryptography.hazmat.backends import default_backend
 | 
			
		||||
from cryptography.hazmat.primitives import hashes, serialization
 | 
			
		||||
from cryptography.hazmat.primitives.asymmetric import rsa
 | 
			
		||||
from cryptography.x509.oid import NameOID
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CertificateBuilder:
 | 
			
		||||
    """Build self-signed certificates"""
 | 
			
		||||
 | 
			
		||||
    __public_key = None
 | 
			
		||||
    __private_key = None
 | 
			
		||||
    __builder = None
 | 
			
		||||
    __certificate = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.__public_key = None
 | 
			
		||||
        self.__private_key = None
 | 
			
		||||
        self.__builder = None
 | 
			
		||||
        self.__certificate = None
 | 
			
		||||
 | 
			
		||||
    def build(self):
 | 
			
		||||
        """Build self-signed certificate"""
 | 
			
		||||
        one_day = datetime.timedelta(1, 0, 0)
 | 
			
		||||
        self.__private_key = rsa.generate_private_key(
 | 
			
		||||
            public_exponent=65537, key_size=2048, backend=default_backend()
 | 
			
		||||
        )
 | 
			
		||||
        self.__public_key = self.__private_key.public_key()
 | 
			
		||||
        self.__builder = (
 | 
			
		||||
            x509.CertificateBuilder()
 | 
			
		||||
            .subject_name(
 | 
			
		||||
                x509.Name(
 | 
			
		||||
                    [
 | 
			
		||||
                        x509.NameAttribute(
 | 
			
		||||
                            NameOID.COMMON_NAME,
 | 
			
		||||
                            u"passbook Self-signed SAML Certificate",
 | 
			
		||||
                        ),
 | 
			
		||||
                        x509.NameAttribute(NameOID.ORGANIZATION_NAME, u"passbook"),
 | 
			
		||||
                        x509.NameAttribute(
 | 
			
		||||
                            NameOID.ORGANIZATIONAL_UNIT_NAME, u"Self-signed"
 | 
			
		||||
                        ),
 | 
			
		||||
                    ]
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            .issuer_name(
 | 
			
		||||
                x509.Name(
 | 
			
		||||
                    [
 | 
			
		||||
                        x509.NameAttribute(
 | 
			
		||||
                            NameOID.COMMON_NAME,
 | 
			
		||||
                            u"passbook Self-signed SAML Certificate",
 | 
			
		||||
                        ),
 | 
			
		||||
                    ]
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            .not_valid_before(datetime.datetime.today() - one_day)
 | 
			
		||||
            .not_valid_after(datetime.datetime.today() + datetime.timedelta(days=365))
 | 
			
		||||
            .serial_number(int(uuid.uuid4()))
 | 
			
		||||
            .public_key(self.__public_key)
 | 
			
		||||
        )
 | 
			
		||||
        self.__certificate = self.__builder.sign(
 | 
			
		||||
            private_key=self.__private_key,
 | 
			
		||||
            algorithm=hashes.SHA256(),
 | 
			
		||||
            backend=default_backend(),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def private_key(self):
 | 
			
		||||
        """Return private key in PEM format"""
 | 
			
		||||
        return self.__private_key.private_bytes(
 | 
			
		||||
            encoding=serialization.Encoding.PEM,
 | 
			
		||||
            format=serialization.PrivateFormat.TraditionalOpenSSL,
 | 
			
		||||
            encryption_algorithm=serialization.NoEncryption(),
 | 
			
		||||
        ).decode("utf-8")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def certificate(self):
 | 
			
		||||
        """Return certificate in PEM format"""
 | 
			
		||||
        return self.__certificate.public_bytes(
 | 
			
		||||
            encoding=serialization.Encoding.PEM,
 | 
			
		||||
        ).decode("utf-8")
 | 
			
		||||
@ -31,9 +31,12 @@ def sign_with_signxml(data: str, provider: "SAMLProvider", reference_uri=None) -
 | 
			
		||||
        digest_algorithm=provider.digest_algorithm,
 | 
			
		||||
    )
 | 
			
		||||
    signed = signer.sign(
 | 
			
		||||
        root, key=key, cert=[provider.signing_cert], reference_uri=reference_uri
 | 
			
		||||
        root,
 | 
			
		||||
        key=key,
 | 
			
		||||
        cert=[provider.singing_kp.certificate_data],
 | 
			
		||||
        reference_uri=reference_uri,
 | 
			
		||||
    )
 | 
			
		||||
    XMLVerifier().verify(signed, x509_cert=provider.signing_cert)
 | 
			
		||||
    XMLVerifier().verify(signed, x509_cert=provider.singing_kp.certificate_data)
 | 
			
		||||
    return etree.tostring(signed).decode("utf-8")  # nosec
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -274,9 +274,9 @@ class DescriptorDownloadView(AccessRequiredView):
 | 
			
		||||
                kwargs={"application": provider.application.slug},
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        pubkey = strip_pem_header(provider.signing_cert.replace("\r", "")).replace(
 | 
			
		||||
            "\n", ""
 | 
			
		||||
        )
 | 
			
		||||
        pubkey = strip_pem_header(
 | 
			
		||||
            provider.singing_kp.certificate_data.replace("\r", "")
 | 
			
		||||
        ).replace("\n", "")
 | 
			
		||||
        subject_format = provider.processor.subject_format
 | 
			
		||||
        ctx = {
 | 
			
		||||
            "entity_id": entity_id,
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user