providers/saml: switch to new crypto
This commit is contained in:
parent
dc8b89a6b9
commit
80a50f9bdb
|
@ -24,9 +24,7 @@ class SAMLProviderSerializer(ModelSerializer):
|
||||||
"property_mappings",
|
"property_mappings",
|
||||||
"digest_algorithm",
|
"digest_algorithm",
|
||||||
"signature_algorithm",
|
"signature_algorithm",
|
||||||
"signing",
|
"singing_kp",
|
||||||
"signing_cert",
|
|
||||||
"signing_key",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ from passbook.providers.saml.models import (
|
||||||
SAMLProvider,
|
SAMLProvider,
|
||||||
get_provider_choices,
|
get_provider_choices,
|
||||||
)
|
)
|
||||||
from passbook.providers.saml.utils.cert import CertificateBuilder
|
|
||||||
|
|
||||||
|
|
||||||
class SAMLProviderForm(forms.ModelForm):
|
class SAMLProviderForm(forms.ModelForm):
|
||||||
|
@ -19,13 +18,6 @@ class SAMLProviderForm(forms.ModelForm):
|
||||||
choices=get_provider_choices(), label="Processor"
|
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:
|
class Meta:
|
||||||
|
|
||||||
model = SAMLProvider
|
model = SAMLProvider
|
||||||
|
@ -41,9 +33,7 @@ class SAMLProviderForm(forms.ModelForm):
|
||||||
"property_mappings",
|
"property_mappings",
|
||||||
"digest_algorithm",
|
"digest_algorithm",
|
||||||
"signature_algorithm",
|
"signature_algorithm",
|
||||||
"signing",
|
"singing_kp",
|
||||||
"signing_cert",
|
|
||||||
"signing_key",
|
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
"name": forms.TextInput(),
|
"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 structlog import get_logger
|
||||||
|
|
||||||
from passbook.core.models import PropertyMapping, Provider
|
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.reflection import class_to_path, path_to_class
|
||||||
from passbook.lib.utils.template import render_to_string
|
from passbook.lib.utils.template import render_to_string
|
||||||
from passbook.providers.saml.processors.base import Processor
|
from passbook.providers.saml.processors.base import Processor
|
||||||
|
@ -74,9 +75,13 @@ class SAMLProvider(Provider):
|
||||||
default="rsa-sha256",
|
default="rsa-sha256",
|
||||||
)
|
)
|
||||||
|
|
||||||
signing = models.BooleanField(default=True)
|
singing_kp = models.ForeignKey(
|
||||||
signing_cert = models.TextField(verbose_name=_("Singing Certificate"))
|
CertificateKeyPair,
|
||||||
signing_key = models.TextField()
|
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"
|
form = "passbook.providers.saml.forms.SAMLProviderForm"
|
||||||
_processor = None
|
_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,
|
digest_algorithm=provider.digest_algorithm,
|
||||||
)
|
)
|
||||||
signed = signer.sign(
|
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
|
return etree.tostring(signed).decode("utf-8") # nosec
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -274,9 +274,9 @@ class DescriptorDownloadView(AccessRequiredView):
|
||||||
kwargs={"application": provider.application.slug},
|
kwargs={"application": provider.application.slug},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
pubkey = strip_pem_header(provider.signing_cert.replace("\r", "")).replace(
|
pubkey = strip_pem_header(
|
||||||
"\n", ""
|
provider.singing_kp.certificate_data.replace("\r", "")
|
||||||
)
|
).replace("\n", "")
|
||||||
subject_format = provider.processor.subject_format
|
subject_format = provider.processor.subject_format
|
||||||
ctx = {
|
ctx = {
|
||||||
"entity_id": entity_id,
|
"entity_id": entity_id,
|
||||||
|
|
Reference in New Issue