diff --git a/.gitignore b/.gitignore
index 71fe82ac49a540cf810a901a45a1c318025f7fa5..80c937db63ab263c4e838b12a6025ce160770f61 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ CACHE
 .env
 app/keys_home/static/react/
 node_modules/
+venv/
diff --git a/app/keys/settings.py b/app/keys/settings.py
index a408d755dc32c42863cec4a18437ddd5c4279369..6a5a03a51a8b3ce42daddd2e580e9a5833a0138d 100644
--- a/app/keys/settings.py
+++ b/app/keys/settings.py
@@ -72,6 +72,7 @@ INSTALLED_APPS = [
     'owlca',
     'pgp',
     'ssh',
+    'wireguard',
     'guardian',
     'fontawesome-free',
     'crispy_forms',
diff --git a/app/keys/urls.py b/app/keys/urls.py
index dea577d3d143706f64f8a442d389ce67a6f8ebd8..859a857ad4d75042ed0968288e7dfcaa1cd0ec71 100644
--- a/app/keys/urls.py
+++ b/app/keys/urls.py
@@ -21,6 +21,7 @@ urlpatterns = [
     path('pgp/', include('pgp.urls')),
     path('ssh/', include('ssh.urls')),
     path('pks/', include('hkp.urls')),
+    path('wireguard/', include('wireguard.urls')),
     path('.well-known/openpgpkey/', include('wkd.urls')),
     path('admin/', admin.site.urls),
     path('', include('keys_home.urls')),
diff --git a/app/keys_home/static/keys_home/css/main.scss b/app/keys_home/static/keys_home/css/main.scss
index 3b50237254cbcf9c08b73c10defd3809c260f5ea..8b8ee9c0984c46e6be1d6d7784b98096b265a898 100644
--- a/app/keys_home/static/keys_home/css/main.scss
+++ b/app/keys_home/static/keys_home/css/main.scss
@@ -440,3 +440,61 @@ body {
     @extend .my-3;
   }
 }
+
+/********************************************************/
+/* Wireguard Key                                              */
+/********************************************************/
+.wireguardkey-item {
+  @extend .border, .my-3, .rounded;
+
+  // every (except last) content box should have a border below
+  & > div {
+    @extend .border-bottom, .px-4, .py-2;
+  }
+  & > div:last-child {
+    border-bottom: none !important;
+    @extend .rounded;
+  }
+
+  /********************************/
+  /* Heading                      */
+  /********************************/
+  h3 {
+    // dark heading per key
+    position: relative; // stretched link inside
+    justify-content: space-between;
+    display: flex;
+    @extend .rounded-top, .text-light, .bg-dark, .p-2;
+    margin-bottom: 0;
+    a {
+      @extend .text-light, .stretched-link;
+    }
+  }
+
+  /********************************/
+  /* Fingerprint                  */
+  /********************************/
+  .wireguardkey-fingerprint {
+    @extend .bg-light;
+  }
+
+  .wireguardkey-fingerprint-title {
+    @extend .text-muted, .mb-1;
+    i, svg {
+      @extend .mr-1;
+    }
+  }
+  .wireguardkey-fingerprint-hash {
+    @extend .input-group, .mb-2;
+    input {
+      @extend .form-control, .bg-white;
+      font-family: monospace;
+    }
+    input + div {
+      @extend .input-group-append;
+      span { 
+        @extend .input-group-text;
+      }
+    }
+  }
+}
diff --git a/app/keys_home/templates/keys_home/base.html b/app/keys_home/templates/keys_home/base.html
index e2fd7b3f7a0751a793d0ceac57c3df1cb5bf2421..317efa893ab7ba1875165960bd063e5af7006e59 100644
--- a/app/keys_home/templates/keys_home/base.html
+++ b/app/keys_home/templates/keys_home/base.html
@@ -51,6 +51,11 @@
               <i class="fas fa-network-wired"></i> PKI
             </a>
           </li>
+          <li>
+            <a href="{% url 'wireguard-list' %}">
+              <i class="fas fa-dragon"></i> Wireguard
+            </a>
+          </li>
           {% if request.user.is_anonymous %}
           <li class="navbar-login">
             <a href="{% url 'login' %}">
diff --git a/app/keys_home/templates/keys_home/home.html b/app/keys_home/templates/keys_home/home.html
index 17756a63c554bf9556ef3a03b695621bb521fdab..3c0348fa5696fa8eb343d8eca75cee842cc88658 100644
--- a/app/keys_home/templates/keys_home/home.html
+++ b/app/keys_home/templates/keys_home/home.html
@@ -16,6 +16,10 @@
         <i class="fas fa-network-wired mb-2"></i><br />
         CAs and Certificates
     </a></h3></li>
+    <li><h3><a href="{% url 'wireguard-list' %}">
+        <i class="fas fa-dragon mb-2"></i><br />
+        Wireguard
+    </a></h3></li>
 </ul>
 
 {% endblock %}
diff --git a/app/wireguard/__init__.py b/app/wireguard/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/app/wireguard/admin.py b/app/wireguard/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..09b49fdda78c5ccd6d92d71982074f049f585e7b
--- /dev/null
+++ b/app/wireguard/admin.py
@@ -0,0 +1,8 @@
+from django.contrib import admin
+from guardian.admin import GuardedModelAdmin
+from . import models
+
+class WireguardPublicKeyAdmin(GuardedModelAdmin):
+    pass
+
+admin.site.register(models.WireguardPublicKey, WireguardPublicKeyAdmin)
diff --git a/app/wireguard/apps.py b/app/wireguard/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..84ea2f5d755257488cb08c8d728587c83e1b872e
--- /dev/null
+++ b/app/wireguard/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class WireguardConfig(AppConfig):
+    name = 'wireguard'
diff --git a/app/wireguard/forms.py b/app/wireguard/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..a011c727549a7376dbc2882985eddcb487db6bf3
--- /dev/null
+++ b/app/wireguard/forms.py
@@ -0,0 +1,9 @@
+
+from django import forms
+from django.core.exceptions import ValidationError
+
+
+class PublicKeyCreateForm(forms.Form):
+    interface = forms.CharField(max_length=128)
+    key = forms.CharField(max_length=128)
+    public = forms.BooleanField(required=False)
diff --git a/app/wireguard/migrations/0001_initial.py b/app/wireguard/migrations/0001_initial.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4dc94a97c0541377f008d19e727603ec01014de
--- /dev/null
+++ b/app/wireguard/migrations/0001_initial.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.1.14 on 2022-06-10 08:28
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='WireguardPublicKey',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('interface', models.CharField(max_length=128)),
+                ('key', models.CharField(max_length=128)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+            ],
+        ),
+    ]
diff --git a/app/wireguard/migrations/__init__.py b/app/wireguard/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/app/wireguard/models.py b/app/wireguard/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..be23f34f2adef3d4d2cde449ce10df8f4737430d
--- /dev/null
+++ b/app/wireguard/models.py
@@ -0,0 +1,9 @@
+from django.db import models
+
+class WireguardPublicKey(models.Model):
+    interface = models.CharField(max_length=128)
+    key = models.CharField(max_length=128)
+    created = models.DateTimeField(auto_now_add=True)
+
+    def __str__(self):
+        return f"{self.interface}"
diff --git a/app/wireguard/templates/wireguard/wireguardpublickey_create.html b/app/wireguard/templates/wireguard/wireguardpublickey_create.html
new file mode 100644
index 0000000000000000000000000000000000000000..40f021610c56171bfb62411bfc664c681a6fee9a
--- /dev/null
+++ b/app/wireguard/templates/wireguard/wireguardpublickey_create.html
@@ -0,0 +1,22 @@
+{% extends 'keys_home/base.html' %}
+{% load crispy_forms_tags %}
+
+{% block content %}
+<nav class="nav-breadcrumb" aria-label="breadcrumb">
+<ol>
+  <li><a href="{% url 'home' %}">Home</a></li>
+  <li><a href="{% url 'wireguard-list' %}">Wireguard Keys</a></li>
+  <li aria-current="page">
+    <a href="{% url 'wireguard-create' %}">Add</a>
+  </li>
+</ol>
+</nav>
+
+<h2>Add Wireguard Public Key</h2>
+<form action="" method="post">
+  {% csrf_token %}
+  {{ form|crispy }}
+  <button class="btn btn-primary" type="submit">Add public key</button>
+</form>
+
+{% endblock %}
diff --git a/app/wireguard/templates/wireguard/wireguardpublickey_item.html b/app/wireguard/templates/wireguard/wireguardpublickey_item.html
new file mode 100644
index 0000000000000000000000000000000000000000..acddf35400374cb0b059efa7c76c6eb425baf041
--- /dev/null
+++ b/app/wireguard/templates/wireguard/wireguardpublickey_item.html
@@ -0,0 +1,20 @@
+<li class="wireguardkey-item">
+  <h3>
+    <div>
+        <i class="fas fa-dragon" title="Interface"></i>
+        {{ publickey.interface }}
+      </a>
+    </div>
+  </h3>
+
+  <div class="wireguardkey-fingerprint">
+    <div class="wireguardkey-fingerprint-title">
+      <i class="fas fa-fingerprint"></i> Public key
+    </div>
+
+    <div class="wireguardkey-fingerprint-hash">
+      <input title="publickey" type="text" readonly value="{{publickey.key }}" />
+      <div><span>Base64</span></div>
+    </div>
+  </div>
+</li>
diff --git a/app/wireguard/templates/wireguard/wireguardpublickey_list.html b/app/wireguard/templates/wireguard/wireguardpublickey_list.html
new file mode 100644
index 0000000000000000000000000000000000000000..cc8addc595ac2c10b735e9379c81ec8cc6ec60b8
--- /dev/null
+++ b/app/wireguard/templates/wireguard/wireguardpublickey_list.html
@@ -0,0 +1,33 @@
+{% extends 'keys_home/base.html' %}
+
+{% block content %}
+
+<nav class="nav-breadcrumb" aria-label="breadcrumb">
+<ol>
+  <li><a href="{% url 'home' %}">Home</a></li>
+  <li aria-current="page">
+    <a href="{% url 'wireguard-list' %}">Wireguard Keys</a>
+  </li>
+</ol>
+</nav>
+
+<h2 class="h-control">
+  <div>Wireguard Public Keys</div>
+  {% if perms.wireguard.add_wireguardpublickey %}
+  <a class="btn btn-outline-primary" href="{% url 'wireguard-create' %}">
+    <i class="fas fa-plus"></i>
+  </a>
+  {% endif %}
+</h2>
+
+
+{% if wireguardpublickey_list %}
+<ul class="list-unstyled">
+{% for publickey in wireguardpublickey_list %}
+  {% include 'wireguard/wireguardpublickey_item.html' with fingerprint_only=True %}
+{% endfor %}
+</ul>
+{% else %}
+<p>There are not public keys.</p>
+{% endif %}
+{% endblock %}
diff --git a/app/wireguard/tests.py b/app/wireguard/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..2978de64290c690e7cc08ae478e562c1546b4b49
--- /dev/null
+++ b/app/wireguard/tests.py
@@ -0,0 +1,31 @@
+from django.test import TestCase
+from django.urls import reverse
+from django.contrib.auth.models import Group
+from guardian.shortcuts import assign_perm
+from . import models
+
+class KeyDecodeerror(TestCase):
+    """Check pages load even if the key cannot be decoded"""
+
+    def setUp(self):
+        self.toykey = models.SSHPublicKey.objects.create(
+            hostname="example.com",
+            keytype="",
+            key="no such thing",
+        )
+        any_user = Group.objects.get(name="any-user")
+        assign_perm('view_sshpublickey', any_user, self.toykey)
+
+    def test_detail(self):
+        """Check that the detail page loads successfully"""
+        response = self.client.get(
+            reverse('ssh-detail', args=[self.toykey.pk])
+        )
+        self.assertEqual(response.status_code, 200)
+
+    def test_list(self):
+        """Check that the list page loads successfully"""
+        response = self.client.get(
+            reverse('ssh-list')
+        )
+        self.assertEqual(response.status_code, 200)
diff --git a/app/wireguard/urls.py b/app/wireguard/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..a161b6d7d5c7f16656489073550dd32f87cb41c3
--- /dev/null
+++ b/app/wireguard/urls.py
@@ -0,0 +1,9 @@
+
+from django.urls import path
+
+from . import views
+
+urlpatterns = [
+    path("", views.PublicKeyListView.as_view(), name="wireguard-list"),
+    path("new", views.publickey_create, name="wireguard-create"),
+]
diff --git a/app/wireguard/views.py b/app/wireguard/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..c6f5799cec54aa4f3e6f5961c2450039070cc001
--- /dev/null
+++ b/app/wireguard/views.py
@@ -0,0 +1,41 @@
+from django.shortcuts import render, reverse
+from django.views.generic import DetailView, ListView, CreateView
+from django.http import HttpResponseRedirect
+from django.contrib.auth.models import Group
+from guardian.mixins import PermissionRequiredMixin, PermissionListMixin
+from guardian.decorators import permission_required
+from guardian.shortcuts import assign_perm
+from . import models
+from . import forms
+
+
+class PublicKeyListView(PermissionListMixin, ListView):
+    model = models.WireguardPublicKey
+    permission_required = ['view_wireguardpublickey']
+
+
+@permission_required('ssh.add_wireguardpublickey')
+def publickey_create(request):
+    if request.method == 'POST':
+        form = forms.PublicKeyCreateForm(request.POST)
+        if form.is_valid():
+            pk = models.WireguardPublicKey()
+            pk.interface = form.cleaned_data['interface']
+            pk.key = form.cleaned_data['key']
+            pk.save()
+
+            assign_perm('view_wireguardpublickey', request.user, pk)
+            assign_perm('change_wireguardpublickey', request.user, pk)
+
+            if form.cleaned_data['public']:
+                any_user = Group.objects.get(name="any-user")
+                assign_perm('view_wireguardpublickey', any_user, pk)
+            return HttpResponseRedirect(reverse('wireguard-list'))
+
+    # if a GET (or any other method) we'll create a blank form
+    else:
+        form = forms.PublicKeyCreateForm()
+
+    return render(request,
+                  'wireguard/wireguardpublickey_create.html',
+                  {'form': form})