diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8814aaca96d66ae9fbf04eed8b440f44843a3509..bd3470f0b2971751657a0ebf8cd99c111574f325 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,6 +16,17 @@ test:
   tags:
     - test
 
+crypto-test:
+  stage: test
+  script:
+    - pip install .[crypto]
+    - python -m unittest djangoldp_crypto.tests.runner
+  except:
+    - master
+    - tags
+  tags:
+    - test
+
 publish:
   stage: release
   before_script:
diff --git a/djangoldp_crypto/README.md b/djangoldp_crypto/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d493c3d2af60135eb762138144a5f9c36a19746e
--- /dev/null
+++ b/djangoldp_crypto/README.md
@@ -0,0 +1,26 @@
+# djangoldp-crypto
+
+Packages like [djangoldp](https://git.startinblox.com/djangoldp-packages/djangoldp) and [django-webidoidc-provider](https://git.startinblox.com/djangoldp-packages/django-webidoidc-provider) have some models and utilities which make use of cryptography. In general, we want to re-use that code in a supporting package to avoid duplication of effort. However, until it is more clear what ca be re-used, we are using this separate django app in this package. See [this ticket](https://git.startinblox.com/djangoldp-packages/djangoldp/issues/236) for more.
+
+## Install
+
+```bash
+$ python -m pip install 'djangoldp[crypto]'
+```
+
+Tnen add the app to your `settings.yml` like so:
+
+```yaml
+INSTALLED_APPS:
+  - djangoldp_crypto
+```
+
+## Management commands
+
+- `creatersakey`: Randomly generate a new RSA key for the DjangoLDP server
+
+## Test
+
+```bash
+$ python -m unittest djangoldp_crypto.tests.runner
+```
diff --git a/djangoldp_crypto/__init__.py b/djangoldp_crypto/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/djangoldp_crypto/admin.py b/djangoldp_crypto/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..9bd35f635d3cc44248fe1b30b7c86585b482acb6
--- /dev/null
+++ b/djangoldp_crypto/admin.py
@@ -0,0 +1,13 @@
+from django.contrib import admin
+
+from djangoldp_crypto.models import RSAKey
+
+
+@admin.register(RSAKey)
+class RSAKeyAdmin(admin.ModelAdmin):
+
+    readonly_fields = ['kid', 'pub_key']
+
+    def save_model(self, request, obj, form, change):
+        obj.priv_key.replace('\r', '')
+        super().save_model(request, obj, form, change)
diff --git a/djangoldp_crypto/management/__init__.py b/djangoldp_crypto/management/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/djangoldp_crypto/management/commands/__init__.py b/djangoldp_crypto/management/commands/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/djangoldp_crypto/management/commands/creatersakey.py b/djangoldp_crypto/management/commands/creatersakey.py
new file mode 100644
index 0000000000000000000000000000000000000000..7f56c563c43d14e6ea2fa2bb6f612f2ad929d306
--- /dev/null
+++ b/djangoldp_crypto/management/commands/creatersakey.py
@@ -0,0 +1,19 @@
+from Cryptodome.PublicKey import RSA
+from django.core.management.base import BaseCommand
+from djangoldp_crypto.models import RSAKey
+
+
+class Command(BaseCommand):
+    help = 'Randomly generate a new RSA key for the DjangoLDP server'
+
+    def handle(self, *args, **options):
+        try:
+            key = RSA.generate(2048)
+            rsakey = RSAKey(priv_key=key.exportKey('PEM').decode('utf8'))
+            rsakey.save()
+            self.stdout.write('RSA key successfully created')
+            self.stdout.write(u'Private key: \n{0}'.format(rsakey.priv_key))
+            self.stdout.write(u'Public key: \n{0}'.format(rsakey.pub_key))
+            self.stdout.write(u'Key ID: \n{0}'.format(rsakey.kid))
+        except Exception as e:
+            self.stdout.write('Something goes wrong: {0}'.format(e))
diff --git a/djangoldp_crypto/migrations/0001_initial.py b/djangoldp_crypto/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6d6add67090f9e51a23f9af974d756969ba65fa
--- /dev/null
+++ b/djangoldp_crypto/migrations/0001_initial.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.2.19 on 2021-03-15 10:35
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='RSAKey',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('priv_key', models.TextField(help_text='Paste your private RSA Key here.', unique=True, verbose_name='Key')),
+            ],
+            options={
+                'verbose_name': 'RSA Key',
+                'verbose_name_plural': 'RSA Keys',
+            },
+        ),
+    ]
diff --git a/djangoldp_crypto/migrations/__init__.py b/djangoldp_crypto/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/djangoldp_crypto/models.py b/djangoldp_crypto/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..07c64013593577373c11ca48644a328ef9069d39
--- /dev/null
+++ b/djangoldp_crypto/models.py
@@ -0,0 +1,37 @@
+from hashlib import md5
+
+from Cryptodome.PublicKey import RSA
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+
+class RSAKey(models.Model):
+
+    priv_key = models.TextField(
+        verbose_name=_(u'Key'), unique=True,
+        help_text=_(u'Paste your private RSA Key here.'))
+
+    class Meta:
+        verbose_name = _(u'RSA Key')
+        verbose_name_plural = _(u'RSA Keys')
+
+    def __str__(self):
+        return u'{0}'.format(self.kid)
+
+    def __unicode__(self):
+        return self.__str__()
+
+    @property
+    def kid(self):
+        if not self.priv_key:
+            return ''
+
+        return u'{0}'.format(md5(self.priv_key.encode('utf-8')).hexdigest())
+
+    @property
+    def pub_key(self):
+        if not self.priv_key:
+            return ''
+
+        _pub_key = RSA.importKey(self.priv_key).publickey()
+        return _pub_key.export_key().decode('utf-8')
diff --git a/djangoldp_crypto/tests/runner.py b/djangoldp_crypto/tests/runner.py
new file mode 100644
index 0000000000000000000000000000000000000000..65ba2d2e890f1b76113df7d0ddfc44806f3fc619
--- /dev/null
+++ b/djangoldp_crypto/tests/runner.py
@@ -0,0 +1,23 @@
+import sys
+
+import django
+import yaml
+from django.conf import settings as django_settings
+from djangoldp.conf.ldpsettings import LDPSettings
+from djangoldp_crypto.tests.settings_default import yaml_config
+
+# load test config
+config = yaml.safe_load(yaml_config)
+ldpsettings = LDPSettings(config)
+django_settings.configure(ldpsettings)
+
+django.setup()
+from django.test.runner import DiscoverRunner
+
+test_runner = DiscoverRunner(verbosity=1)
+
+failures = test_runner.run_tests([
+    'djangoldp_crypto.tests.tests_rsakey',
+])
+if failures:
+    sys.exit(failures)
diff --git a/djangoldp_crypto/tests/settings_default.py b/djangoldp_crypto/tests/settings_default.py
new file mode 100644
index 0000000000000000000000000000000000000000..da76f6c5124b4fa4c8d1519d88a1fc3ef5f61820
--- /dev/null
+++ b/djangoldp_crypto/tests/settings_default.py
@@ -0,0 +1,12 @@
+"""This module contains YAML configurations for djangoldp_crypto testing."""
+
+yaml_config = """
+dependencies:
+
+ldppackages:
+  - djangoldp_crypto.tests
+
+server:
+  INSTALLED_APPS:
+    - djangoldp_crypto
+"""
diff --git a/djangoldp_crypto/tests/tests_rsakey.py b/djangoldp_crypto/tests/tests_rsakey.py
new file mode 100644
index 0000000000000000000000000000000000000000..d41e1fa7d352a46bd17fa85c889ba560bb751fd7
--- /dev/null
+++ b/djangoldp_crypto/tests/tests_rsakey.py
@@ -0,0 +1,12 @@
+from Cryptodome.PublicKey import RSA
+from django.db import IntegrityError
+from django.test import TestCase
+from djangoldp_crypto.models import RSAKey
+
+
+class TestRSAKey(TestCase):
+    def test_rsakey_unique(self):
+        priv_key = RSA.generate(2048)
+        RSAKey.objects.create(priv_key=priv_key)
+        with self.assertRaises(IntegrityError):
+            RSAKey.objects.create(priv_key=priv_key)
diff --git a/setup.cfg b/setup.cfg
index 8858c1aeb26a6d23e1878fa9c6e01b35e28a901e..3dd375f7bcf7ee82ba5088a2158008f509649c99 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -38,6 +38,8 @@ console_scripts =
 dev =
     validators
     factory_boy >= 2.11.0
+crypto =
+    pycryptodomex~=3.10
 
 [semantic_release]
 version_source = tag