diff --git a/app/Http/Controllers/KeyController.php b/app/Http/Controllers/KeyController.php
index 88bb7e246d942cff18cb6d179b5eff6b9b736006..a62d249429eb9e55b1dffbda4b4f215f1a6241a2 100644
--- a/app/Http/Controllers/KeyController.php
+++ b/app/Http/Controllers/KeyController.php
@@ -6,37 +6,31 @@ use Cookie;
 use Illuminate\Http\Request;
 use LaravelLocalization;
 use \App\Models\Key;
+use \Carbon\Carbon;
+use Validator;
 
 class KeyController extends Controller
 {
-    public function index(Request $request)
-    {
-        $redirUrl = $request->input('redirUrl', "");
-        $cookie = Cookie::get('key');
-        $key = $request->input('keyToSet', '');
-
-        if (empty($key) && empty($cookie)) {
-            $key = 'enter_key_here';
-        } elseif (empty($key) && !empty($cookie)) {
-            $key = $cookie;
-        } elseif (!empty($key)) {
-            $key = $request->input('key');
-        }
+    // How many Ad Free searches should a user get max when he creates a new key
+    const KEYCHANGE_ADFREE_SEARCHES = 150;
 
+    public function index(\App\Models\Key $key, Request $request)
+    {
         $cookieLink = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('loadSettings', Cookie::get()));
         return view('key')
             ->with('title', trans('titles.key'))
-            ->with('cookie', $key)
+            ->with('keystatus', $key->getStatus())
+            ->with('cookie', $key->key)
             ->with('cookieLink', $cookieLink);
     }
 
     public function setKey(Request $request)
     {
-        $redirUrl = $request->input('redirUrl', "");
         $keyToSet = $request->input('keyToSet');
         $key = new Key($request->input('keyToSet', ''));
 
-        if ($key->getStatus()) {
+        $status = $key->getStatus();
+        if ($status !== null) {
             # Valid Key
             $host = $request->header("X_Forwarded_Host", "");
             if (empty($host)) {
@@ -46,10 +40,7 @@ class KeyController extends Controller
             $settings = Cookie::get();
             $settings['key'] = $keyToSet;
             $cookieLink = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('loadSettings', $settings));
-            return view('key')
-                ->with('title', trans('titles.key'))
-                ->with('cookie', $keyToSet)
-                ->with('cookieLink', $cookieLink);
+            return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
         } else {
             $cookieLink = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('loadSettings', Cookie::get()));
             return view('key')
@@ -74,4 +65,152 @@ class KeyController extends Controller
             return redirect($url);
         }
     }
+
+    public function changeKeyIndex(\App\Models\Key $key, Request $request){
+        if(!$key->canChange()){
+            return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
+        }
+        return view('keychange', [
+            "title" => trans('titles.keychange'),
+            "key" => $key->key,
+            "css" => [mix('css/keychange/index.css')]
+            ]);
+    }
+
+    public function removeCurrent(\App\Models\Key $key, Request $request){
+        if(!$key->canChange()){
+            return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
+        }
+        // Reduce Current Key
+        $res = $key->reduce(self::KEYCHANGE_ADFREE_SEARCHES);
+        if(empty($res) || empty($res->status) || $res->status !== "success"){
+            return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
+        }
+        // Redirect to Cookie Remove URL with redirect to step two
+        $validUntil = Carbon::now("Europe/London")->addDays(2);
+        $format = "Y-m-d H:i:s";
+        $data = [
+            "validUntil" => $validUntil->format($format),
+            "password" => hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY")),
+        ];
+        $targetUrl = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyTwo', $data));
+        $redirUrl = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('removeCookie', [
+            "ir" => $targetUrl
+        ]));
+        return redirect($redirUrl);
+    }
+
+    public function generateNew(\App\Models\Key $key, Request $request){
+        // Validate Request Data
+        $validUntil = $request->input('validUntil', '');
+        $password = $request->input('password', '');
+        $format = "Y-m-d H:i:s";
+
+        // Check if Validuntil
+        $valid = true;
+        if(empty($validUntil)){
+            $valid = false;
+        }else{
+            $validUntil = Carbon::createFromFormat($format, $validUntil, "Europe/London");
+            if(!$validUntil){
+                $valid = false;
+            }
+        }
+
+        if($valid && Carbon::now()->diffInSeconds($validUntil) <= 0){
+            $valid = false;
+        }
+        if($valid){
+            // Check if hash matches
+            $expectedHash = hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY"));
+            if(!hash_equals($expectedHash, $password)){
+                $valid = false;
+            }
+        }
+        if(!$valid){
+            return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
+        }
+
+        // Check if the key already was generated
+        if (!$key->checkForChange("", $password)) {
+            return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
+        }
+
+
+        return view('keychangetwo', [
+            "title" => trans('titles.keychange'),
+            "validUntil" => $validUntil,
+            "css" => [mix('css/keychange/index.css')]
+        ]);
+
+    }
+
+    public function generateNewPost(\App\Models\Key $key, Request $request){
+        // Validate Request Data
+        $validUntil = $request->input('validUntil', '');
+        $password = $request->input('password', '');
+        $format = "Y-m-d H:i:s";
+
+        // Check if Validuntil
+        $valid = true;
+        if(empty($validUntil)){
+            $valid = false;
+        }else{
+            $validUntil = Carbon::createFromFormat($format, $validUntil, "Europe/London");
+            if(!$validUntil){
+                $valid = false;
+            }
+        }
+
+        if($valid && Carbon::now()->diffInSeconds($validUntil) <= 0){
+            $valid = false;
+        }
+        if($valid){
+            // Check if hash matches
+            $expectedHash = hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY"));
+            if(!hash_equals($expectedHash, $password)){
+                $valid = false;
+            }
+        }
+        if(!$valid){
+            return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('keyindex')));
+        }
+
+        $validator = Validator::make($request->all(), [
+            'newkey' => 'required|min:4|max:20',
+        ]);
+        if($validator->fails()) {
+            $data = [
+                "validUntil" => $validUntil->format($format),
+                "password" => hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY")),
+                "newkey" => $request->input('newkey', ''),
+            ];
+            $targetUrl = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyTwo', $data));
+            return redirect($targetUrl);
+        }
+
+        $newkey = $request->input('newkey', '');
+        
+        $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
+        $randomSuffix = "";
+        $suffixCount = 3;
+        for($i = 0; $i < $suffixCount; $i++){
+            $randomSuffix .= $characters[rand(0, strlen($characters)-1)];
+        }
+        $newkey = $newkey . $randomSuffix;
+
+        if($key->checkForChange($newkey, $password)){
+            $result = $key->generateKey(null, self::KEYCHANGE_ADFREE_SEARCHES, $newkey, "Schlüssel gewechselt. Hash $password");
+            if(!empty($result)){
+                Cookie::queue('key', $result, 525600, '/', null, false, false);
+                return redirect(LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyThree', ["newkey" => $result])));
+            }
+        }
+        $data = [
+            "validUntil" => $validUntil->format($format),
+            "password" => hash_hmac("sha256", $validUntil->format($format), env("APP_KEY", "WEAK_KEY")),
+        ];
+        $targetUrl = LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyTwo', $data));
+        return redirect($targetUrl);
+    }
 }
diff --git a/app/Http/Controllers/MailController.php b/app/Http/Controllers/MailController.php
index 4c4ebcb90dd4a29e80830d1bd51f38bee9721cd0..17f847dcde9289f82f4248d7d7a99ccf4ca4c1af 100644
--- a/app/Http/Controllers/MailController.php
+++ b/app/Http/Controllers/MailController.php
@@ -220,7 +220,7 @@ class MailController extends Controller
             $betrag = round($betrag, 2, PHP_ROUND_HALF_DOWN);
 
             # Generating personalised key for donor
-            $key = app('App\Models\Key')->generateKey($betrag);
+            $key = app('App\Models\Key')->generateKey($betrag, null, null, 'Für ' . $betrag . '€ aufgeladen am '. date("d.m.Y"));
 
             try {
                 $postdata = [
diff --git a/app/Http/Middleware/RemoveKey.php b/app/Http/Middleware/RemoveKey.php
index 5b1c0be94cb16640e1d7021cf61c743995df7590..a65dbf1daa5326666ace41380aa7260ef7f5e530 100644
--- a/app/Http/Middleware/RemoveKey.php
+++ b/app/Http/Middleware/RemoveKey.php
@@ -17,7 +17,7 @@ class RemoveKey
     public function handle($request, Closure $next)
     {
         // Check if a wrong Key Cookie is set and if so remove it
-        if(Cookie::has("key") && !app('App\Models\Key')->getStatus()){
+        if(Cookie::has("key") && app('App\Models\Key')->getStatus() === null){
             return redirect(route("removeCookie", ["ir" => url()->full()]));
         }
         return $next($request);
diff --git a/app/MetaGer.php b/app/MetaGer.php
index f68f6608100977fc3e88be6500d1cf669bf4f69c..6b5959f797fed0916b9fbce69b1b27d254b2e30c 100644
--- a/app/MetaGer.php
+++ b/app/MetaGer.php
@@ -1502,7 +1502,6 @@ class MetaGer
                 $logEntry .= " time=" . round((microtime(true) - $this->starttime), 2) . " serv=" . $this->fokus;
                 $logEntry .= " interface=" . LaravelLocalization::getCurrentLocale();
                 $logEntry .= " sprachfilter=" . $this->lang;
-                $logEntry .= " key=" . $this->apiKey;
                 $logEntry .= " eingabe=" . $this->eingabe;
 
                 $logEntry = preg_replace("/\n+/", " ", $logEntry);
diff --git a/app/Models/Key.php b/app/Models/Key.php
index caef0430f53badc80ab164a30c463a049db8567d..f078b3e560e0208ac8ae7c59ec93a1cb6c07deae 100644
--- a/app/Models/Key.php
+++ b/app/Models/Key.php
@@ -4,12 +4,14 @@ namespace App\Models;
 
 use Illuminate\Support\Facades\Redis;
 use Request;
+use \Carbon\Carbon;
 
 class Key
 {
     public $key;
-    public $status; # valid key = true, invalid key = false, unidentified key = null
+    public $status; # Null If Key invalid | false if valid but has no adFreeSearches | true if valid and has adFreeSearches
     private $keyserver = "https://key.metager.de/";
+    private $keyinfo;
 
     public function __construct($key, $status = null)
     {
@@ -25,35 +27,29 @@ class Key
     {
         if ($this->key !== '' && $this->status === null) {
             $this->updateStatus();
-            if(empty($this->status)){
+            if($this->status === null){
                 // The user provided an invalid key which we will log to fail2ban
                 $fail2banEnabled = config("metager.metager.fail2ban_enabled");
-                if(empty($fail2banEnabled) || !$fail2banEnabled || !env("fail2banurl", false) || !env("fail2banuser") || !env("fail2banpassword")){
-                    return false;
+                if (!empty($fail2banEnabled) && $fail2banEnabled && !empty(env("fail2banurl", false)) && !empty(env("fail2banuser")) && !empty(env("fail2banpassword"))) {
+                    // Submit fetch job to worker
+                    $mission = [
+                            "resulthash" => "captcha",
+                            "url" => env("fail2banurl") . "/mgkeytry/",
+                            "useragent" => "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0",
+                            "username" => env("fail2banuser"),
+                            "password" => env("fail2banpassword"),
+                            "headers" => [
+                                "ip" => Request::ip()
+                            ],
+                            "cacheDuration" => 0,
+                            "name" => "Captcha",
+                        ];
+                    $mission = json_encode($mission);
+                    Redis::rpush(\App\MetaGer::FETCHQUEUE_KEY, $mission);
                 }
-
-                // Submit fetch job to worker
-                $mission = [
-                        "resulthash" => "captcha",
-                        "url" => env("fail2banurl") . "/mgkeytry/",
-                        "useragent" => "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0",
-                        "username" => env("fail2banuser"),
-                        "password" => env("fail2banpassword"),
-                        "headers" => [
-                            "ip" => Request::ip()
-                        ],
-                        "cacheDuration" => 0,
-                        "name" => "Captcha",
-                    ];
-                $mission = json_encode($mission);
-                Redis::rpush(\App\MetaGer::FETCHQUEUE_KEY, $mission);
             }
         }
-        if ($this->status === null || $this->status === false) {
-            return false;
-        } else {
-            return true;
-        }
+        return $this->status;
     }
 
     public function updateStatus()
@@ -71,14 +67,15 @@ class Key
         try {
             $link = $this->keyserver . "v2/key/". urlencode($this->key);
             $result = json_decode(file_get_contents($link, false, $context));
-            if ($result->{'apiAccess'} == 'unlimited') {
-                $this->status = true;
-                return true;
-            } else if ($result->{'apiAccess'} == 'normal' && $result->{'adFreeSearches'} > 0){
-                $this->status = true;
+            if(!empty($result)){
+                $this->keyinfo = $result;
+                if($this->keyinfo->adFreeSearches > 0 || $this->keyinfo->apiAccess === "unlimited"){
+                    $this->status = true;
+                }else{
+                    $this->status = false;
+                }
                 return true;
-            } else {
-                $this->status = false;
+            }else{
                 return false;
             }
         } catch (\ErrorException $e) {
@@ -118,15 +115,26 @@ class Key
             return false;
         }
     }
-    public function generateKey($payment)
+    public function generateKey($payment = null, $adFreeSearches = null, $key = null, $notes = "")
     {
         $authKey = base64_encode(env("KEY_USER", "test") . ':' . env("KEY_PASSWORD", "test"));
-        $postdata = http_build_query(array(
-            'payment' => $payment,
+        $postdata = array(
             'apiAccess' => 'normal',
-            'notes' => 'Fuer ' . $payment . '€ aufgeladen am '. date("d.m.Y"),
-            'expiresAfterDays' => 365
-        ));
+            'expiresAfterDays' => 365,
+            'notes' => $notes
+        );
+        if(!empty($key)){
+            $postdata["key"] = $key;
+        }
+    
+        if(!empty($payment)){
+            $postdata["payment"] = $payment;
+        }else if(!empty($adFreeSearches)){
+            $postdata["adFreeSearches"] = $adFreeSearches;
+        }else{
+            return false;
+        }
+        $postdata = http_build_query($postdata, "", "&", PHP_QUERY_RFC3986);
         $opts = array(
             'http' => array(
                 'method' => 'POST',
@@ -149,4 +157,89 @@ class Key
             return false;
         }
     }
+
+    public function reduce($count){
+        $authKey = base64_encode(env("KEY_USER", "test") . ':' . env("KEY_PASSWORD", "test"));
+        $postdata = http_build_query(array(
+            'adFreeSearches' => $count,
+        ));
+        $opts = array(
+            'http' => array(
+                'method' => 'POST',
+                'header' => [
+                    'Content-type: application/x-www-form-urlencoded',
+                    'Authorization: Basic ' . $authKey
+                ],
+                'content' => $postdata,
+                'timeout' => 5
+            ),
+        );
+
+        $context = stream_context_create($opts);
+
+        try {
+            $link = $this->keyserver . "v2/key/" . $this->key . "/reduce-searches";
+            $result = json_decode(file_get_contents($link, false, $context));
+            return $result;
+        } catch (\ErrorException $e) {
+            return false;
+        }
+    }
+
+    /**
+     * Tells if this key is liable to change to a custom key
+     * Currently only members are allowed to do so and only every 2 days
+     * Also only the original member key is allowed to be changed
+     * 
+     * @return boolean
+     */
+    public function canChange(){
+        if(empty($this->status) || !preg_match("/^Mitgliederschlüssel\./", $this->keyinfo->notes) || $this->keyinfo->adFreeSearches < \App\Http\Controllers\KeyController::KEYCHANGE_ADFREE_SEARCHES){
+            return false;
+        }
+        if(!empty($this->keyinfo->KeyChangedAt)){
+            // "2021-03-09T09:19:44.000Z"
+            $keyChangedAt = Carbon::createFromTimeString($this->keyinfo->KeyChangedAt, 'Europe/London');
+            if($keyChangedAt->diffInSeconds(Carbon::now()) > (2 * 24 * 60 * 60)){
+                return true;
+            }else{
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public function checkForChange($newkey = "", $hash){
+        $authKey = base64_encode(env("KEY_USER", "test") . ':' . env("KEY_PASSWORD", "test"));
+        $postdata = http_build_query(array(
+            'hash' => $hash,
+            'key' => $newkey,
+        ));
+        $opts = array(
+            'http' => array(
+                'method' => 'POST',
+                'header' => [
+                    'Content-type: application/x-www-form-urlencoded',
+                    'Authorization: Basic ' . $authKey
+                ],
+                'content' => $postdata,
+                'timeout' => 5
+            ),
+        );
+
+        $context = stream_context_create($opts);
+
+        try {
+            $link = $this->keyserver . "v2/key/can-change";
+            $result = json_decode(file_get_contents($link, false, $context));
+
+            if(!empty($result) && $result->status === "success" && empty($result->results)){
+                return true;
+            }else{
+                return false;
+            }
+        } catch (\ErrorException $e) {
+            return false;
+        }
+    }
 }
diff --git a/composer.json b/composer.json
index 1255c1b3e02d7dd9dd1159753a3419414ba9adb6..ac2b1300eb9bb125c18f359d44e7c397705931dc 100644
--- a/composer.json
+++ b/composer.json
@@ -22,7 +22,7 @@
     "require-dev": {
         "beyondcode/laravel-dump-server": "^1.0",
         "browserstack/browserstack-local": "^1.1",
-        "filp/whoops": "^2.0",
+        "filp/whoops": "^2.9",
         "fzaninotto/faker": "^1.4",
         "laravel/dusk": "^5.0",
         "mockery/mockery": "^1.0",
diff --git a/resources/lang/de/key.php b/resources/lang/de/key.php
index f29f434b0c955872eb77b9fe56ae78593892bc20..3c8076ed4e032c58de46cc318d0d16f6a0e266e6 100644
--- a/resources/lang/de/key.php
+++ b/resources/lang/de/key.php
@@ -4,7 +4,7 @@ return [
     'h1' => "Schlüssel für Ihre werbefreie Suche",
     'p1' => 'MetaGer bietet <a href=":url1">SUMA-EV Mitgliedern</a> und großzügigen <a href=":url2">Spendern</a> einen Schlüssel an, mit dem sie Zugriff auf ein Kontingent an werbefreien Suchen haben.',
     'p2' => 'Auf dieser Seite können Sie Ihren Schlüssel (sofern bekannt) eingeben. Wir speichern diesen mit Hilfe eines Cookies auf Ihrem PC. Auf diese Weise sendet Ihr Browser den Schlüssel automatisch bei jeder durchgeführten Suche an uns, sodass wir die Werbung für Sie entfernen können.',
-    'p3' => 'Wenn Sie sich den Cookie anschauen steht dort drin "key=xxxx". Wir verwenden diesen dementsprechend nicht für Tracking-Zwecke. Er wird auch zu keinem Zeitpunkt in irgendeiner Form von uns gespeichert oder geloggt.',
+    'p3' => 'Wenn Sie sich den Cookie anschauen steht dort drin "key=xxxx". Er wird zu keinem Zeitpunkt in irgendeiner Form von uns gespeichert oder geloggt. Wir verwenden diesen insbesondere auch nicht für Tracking-Zwecke',
     'p4' => 'Wichtig: Um diese Funktion nutzen zu können, müssen Sie Cookies in Ihrem Browser zugelassen haben. Die Einstellung bleibt dann solange gespeichert, wie Ihr Browser Cookies speichert.',
     'p5' => 'Um den Schlüssel darüber hinausgehend speichern zu können haben Sie folgende Möglichkeiten:',
     'li1' => 'Richten Sie sich folgenden Link als Startseite/Lesezeichen ein:',
@@ -12,5 +12,9 @@ return [
     'placeholder1' => 'Schlüssel eingeben...',
     'removeKey' => 'aktuellen Schlüssel entfernen',
     'invalidKey' => 'Der eingegebene Schlüssel ist ungültig',
+    'empty' => 'Ihr Schlüssel ist zwar gültig, enthält aber keine werbefreien Suchen mehr.',
     'backLink' => 'Zurück zur letzten Seite',
+    'custom.h3' => 'Wunsch-Schlüssel',
+    'custom.p1' => 'Mitglieder des SUMA-EV haben die Möglichkeit, sich einen eigenen Schlüssel auszusuchen.',
+    'custom.a1' => 'Wunsch Schlüssel einrichten'
 ];
diff --git a/resources/lang/de/keychange.php b/resources/lang/de/keychange.php
new file mode 100644
index 0000000000000000000000000000000000000000..8c44147c6acd9cb47606368f93965a2920cc265e
--- /dev/null
+++ b/resources/lang/de/keychange.php
@@ -0,0 +1,23 @@
+<?php
+
+return [
+    'h1' => 'Wunsch Schlüssel',
+    'p1' => 'Mit diesem Tool haben Sie die Möglichkeit Ihren aktuellen Mitgliederschlüssel ":key" zu wechseln. Mitgliederschlüssel sind an Ihre Mitgliedschaft im SUMA-EV gekoppelt. So kann dieser automatisch erneuert werden. Auch wenn wir das nicht tun, hätten wir dadurch natürlich theoretisch die Möglichkeit eine Verknüpfung zwischen Mitgliederschlüssel und durchgeführten Suchen zu schaffen. Mit dem Wechsel auf einen Wunsch Schlüssel wird eine solche Verknüpfung theoretisch und praktisch für uns unmöglich.',
+    'p2' => 'Außerdem können Sie mit diesem Tool einen Schlüssel erstellen, der leichter zu merken ist. Bitte heben Sie sich den ursprünglichen Schlüssel dennoch auf. Dadurch, dass der neue Schlüssel nicht mehr automatisch erneuert werden kann, wird dieser irgendwann ungültig. In dem Fall müssen Sie diesen Prozess wiederholen, indem Sie Ihren ursprünglichen Mitgliederschlüssel (:key) bei MetaGer eingeben und dieses Tool erneut aufrufen.',
+    'p3' => 'Heben Sie sich deshalb beide Schlüssel gut auf und beachten Sie insbesondere, dass wir nicht in der Lage sind Ihnen den neuen, durch diesen Wechsel entstandenen, Schlüssel bei Verlust mitzuteilen oder wiederherzustellen.',
+    'p4' => 'Dieses Tool führt Sie durch folgende Schritte um einen neuen anonymen Wunsch Schlüssel zu erstellen:',
+    'ol1.li1' => 'Entfernen aller Informationen über den aktuellen Mitgliederschlüssel aus Ihrem Browser',
+    'ol1.li2' => 'Erzeugen einer URL mit der ein neuer Schlüssel erstellt werden kann. Diese URL enthält keine Informationen mehr über den Mitgliederschlüssel.',
+    'ol1.li3' => 'Erzeugen eines neuen Wunsch Schlüssels',
+    'ol1.li4' => 'Speichern des neuen Wunsch Schlüssels im Browser',
+    'a1' => 'Gelesen - Wunsch Schlüssel erstellen',
+
+    'p5' => 'Ihr bisheriger Mitgliederschlüssel wurde nun aus dem Browser gelöscht. Außerdem haben wir eine URL erzeugt, mit der Sie sich Ihren Wunschschlüssel erstellen können. Dies ist innerhalb von :validUntil jederzeit und von jedem Gerät aus möglich indem Sie folgende URL im Browser aufrufen, oder direkt das Formular auf dieser Seite verwenden.',
+    'p6' => 'In das nachfolgende Textfeld können, Sie nun Ihren gewünschten Schlüssel eintragen. Um Überschneidungen zu vermeiden fügen wir noch ein paar Zeichen an den Schlüssel an. Mit dem Klick auf generieren wird Ihr neuer Schlüssel dann erzeugt und auf der folgenden Seite angezeigt, damit Sie Ihn sich abspeichern können.',
+    'input1label' => 'Wunsch Schlüssel eintragen (Mindestens 4 und maximal 20 Zeichen)',
+    'input1' => 'Wunsch Schlüssel eintragen',
+    'button1' => 'Wunsch Schlüssel generieren',
+
+    'p7' => ' Ihr neuer Wunsch Schlüssel wurde erfolgreich eingerichtet. Bitte merken Sie ihn sich gut. Er kann bei Verlust nämlich nicht wiederhergestellt werden. Er wird außerdem irgendwann ungültig werden. Wenn der Fall eintritt, tragen Sie bei MetaGer bitte Ihren regulären Mitgliederschlüssel ein und wiederholen Sie diesen Vorgang.',
+    'p8' => 'Ihr neuer Schlüssel lautet:',
+];
\ No newline at end of file
diff --git a/resources/lang/de/titles.php b/resources/lang/de/titles.php
index d216ce29334bdfcf6b6d979ed2abdb6a032cdbd7..15a4e4144082c629b771cbae8e1e7e7b778fb009 100644
--- a/resources/lang/de/titles.php
+++ b/resources/lang/de/titles.php
@@ -24,6 +24,7 @@ return [
     'asso' => 'Assoziator - MetaGer',
     'plugin' => 'Plugin - MetaGer',
     'key' => 'Mitgliederschlüssel - MetaGer',
+    'keychange' => 'Mitgliederschlüssel wechseln',
     'settings' => 'Sucheinstellungen (:fokus)',
     'allSettings' => 'Alle Sucheinstellungen - MetaGer',
     'transparency' => 'Transparenzerklärung - MetaGer',
diff --git a/resources/lang/en/keychange.php b/resources/lang/en/keychange.php
new file mode 100644
index 0000000000000000000000000000000000000000..210c70d3f534a1e0b5384cd9c0444fed91d2f37f
--- /dev/null
+++ b/resources/lang/en/keychange.php
@@ -0,0 +1,23 @@
+<?php
+
+return [
+    'h1' => 'Favorite Key',
+    'p1' => 'With this tool you have the possibility to change your current membership key ":key". Membership keys are linked to your membership in SUMA-EV. So it can be renewed automatically. Even if we don\'t do that, we would of course theoretically have the possibility of creating a link between the membership key and the searches carried out. With the change to a desired key, such a link is theoretically and practically impossible for us.',
+    'p2' => 'Also, you can use this tool to create a key that is easier to remember. Please keep the original key anyway. Because the new key can no longer be renewed automatically, it will eventually become invalid. In that case you will have to repeat this process by entering your original membership key (:key) into MetaGer and calling up this tool again.',
+    'p3' => 'You should therefore keep both keys in a safe place and pay particular attention to the fact that we are not able to restore the new one if it is lost.',
+    'p4' => 'This tool guides you through the following steps to create a new anonymous favorite key:',
+    'ol1.li1' => 'Remove all information about the current member key from your browser',
+    'ol1.li2' => 'Generating a URL with which a new key can be created. This URL no longer contains information about the member key.',
+    'ol1.li3' => 'Generate a new favorite key',
+    'ol1.li4' => 'Save the new favorite key in the browser',
+    'a1' => 'Read - create a favorite key',
+
+    'p5' => 'Your previous membership key has now been deleted from the browser. We have also created a URL that you can use to create your desired key. This is possible within :validUntil at any time and from any device by calling up the following URL in the browser, or directly using the form on this page.',
+    'p6' => 'You can now enter your desired key in the following text field. To avoid overlapping, we add a few more characters to the key. By clicking on generate, your new key will be generated and displayed on the following page so that you can save it.',
+    'input1label' => 'Enter the desired key (at least 4 and a maximum of 20 characters)',
+    'input1' => 'Enter the desired key',
+    'button1' => 'Generate favorite key',
+
+    'p7' => 'Your new desired key has been successfully set up. Please make a note of it. It cannot be restored if it is lost. It will also expire at some point. If this occurs, please enter your regular membership key at MetaGer and repeat this process.',
+    'p8' => 'Your new key is:',
+];
\ No newline at end of file
diff --git a/resources/less/metager/pages/key-dark.less b/resources/less/metager/pages/key-dark.less
index 7a84e3ad7e7d3ecd9f7edacc9297b3d380bbde70..ecfdd63f4d77bdc99228f7798ac4dbbded9986f9 100644
--- a/resources/less/metager/pages/key-dark.less
+++ b/resources/less/metager/pages/key-dark.less
@@ -8,7 +8,7 @@
         .card-medium;
         margin: 8px 0;
         .error {
-            color: red;
+            color: #fd5757;
             text-align: center;
             font-weight: bold;
         }
diff --git a/resources/less/metager/pages/key.less b/resources/less/metager/pages/key.less
index c1367a3f85fcd2c64db80a0c7090b136d85e0030..53f581e492ff486ed028d2172b8e374859dbfcc6 100644
--- a/resources/less/metager/pages/key.less
+++ b/resources/less/metager/pages/key.less
@@ -24,4 +24,14 @@
         text-align: center;
         margin-top: 16px;
     }
+
+    #remove-key {
+        margin-top: 8px;
+    }
+
+    #enter-key-form {
+        display: flex;
+        justify-content: center;
+        gap: 4px;
+    }
 }
\ No newline at end of file
diff --git a/resources/less/metager/pages/keychange/index.less b/resources/less/metager/pages/keychange/index.less
new file mode 100644
index 0000000000000000000000000000000000000000..1fa3e44f6be161d32508cc08faabe9a162378aa0
--- /dev/null
+++ b/resources/less/metager/pages/keychange/index.less
@@ -0,0 +1,16 @@
+#steps {
+    display: flex;
+    justify-content: space-between;
+    padding: 16px;
+    counter-reset: steps;
+    > div {
+        counter-increment: steps;
+        padding: 8px;
+        &::before {
+            content: counter(steps)". ";
+        }
+        &.active {
+            background-color: #2879025c;
+        }
+    }
+}
\ No newline at end of file
diff --git a/resources/views/key.blade.php b/resources/views/key.blade.php
index eec467c0a73225d9dbf3054474f0059f1f8ec135..7edb1652bde04f626ab87c2f52c1dc636f32e447 100644
--- a/resources/views/key.blade.php
+++ b/resources/views/key.blade.php
@@ -19,12 +19,13 @@
         <p>{{ trans('key.p2') }}</p>
         <p>{{ trans('key.p3') }}</p>
         <p>{{ trans('key.p4') }}</p>
+        @if(isset($keystatus) && $keystatus !== null)
         <p>{{ trans('key.p5') }}</p>
         <ol>
             <li>
                 @lang ('key.li1')
                 <div class="copyLink">
-                    <input id="loadSettings" class="loadSettings" type="text" value="{{$cookieLink}}">
+                    <input id="loadSettings" class="loadSettings" type="text" value="{{$cookieLink}}" readonly>
                     <button class="js-only btn btn-default" onclick="var copyText = document.getElementById('loadSettings');copyText.select();copyText.setSelectionRange(0, 99999);document.execCommand('copy');">@lang('settings.copy')</button>
                 </div>
             </li>
@@ -32,30 +33,41 @@
             <li>
                 @lang('key.li2')
                 <div class="copyLink">
-                    <input id="searchString" class="loadSettings" type="text" value="{{route("resultpage", ["key" => $cookie]) . "&eingabe=%s"}}">
+                    <input id="searchString" class="loadSettings" type="text" value="{{route("resultpage", ["key" => $cookie]) . "&eingabe=%s"}}" readonly>
                     <button class="js-only btn btn-default" onclick="var copyText = document.getElementById('searchString');copyText.select();copyText.setSelectionRange(0, 99999);document.execCommand('copy');">@lang('settings.copy')</button>
                 </div>
             </li>
         </ol>
+        @endif
+    </div>
+    @if(app('App\Models\Key')->canChange())
+    <div class="section">
+        <h3>@lang('key.custom.h3')</h3>
+        <p>@lang('key.custom.p1')</p>
+        <a class="btn btn-default" href="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyOne')) }}">@lang('key.custom.a1')</a>
     </div>
+    @endif
     <div class="section">
+        @if(isset($keystatus) && $keystatus === false)
+        <p class="error">@lang('key.empty')</p>
+        @endif
+        @if(isset($keyValid) && $keyValid === false)
+        <p class="error">@lang('key.invalidKey')</p>
+        @endif
         <div id="form-wrapper">
-            <form method="post">
+            <form id="enter-key-form" method="post">
                 <input type="hidden" name="redirUrl" value="{{ Request::input('redirUrl', '') }}" />
-                <input type="text" name="keyToSet" value="{{$cookie === 'enter_key_here' ? '' : $cookie}}" placeholder="@lang('key.placeholder1')" autofocus>
-                <button type="submit" class="btn btn-success">OK</button>
+                <input type="text" name="keyToSet" value="{{$cookie === '' ? '' : $cookie}}" placeholder="@lang('key.placeholder1')" autofocus>
+                <button type="submit" class="btn btn-default">OK</button>
             </form>
-            @if($cookie !== 'enter_key_here')
-            <form method="post" action="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), action('KeyController@removeKey', ['redirUrl' => url()->full()])) }}">
+            @if(isset($keystatus) && $keystatus !== null)
+            <form id="remove-key" method="post" action="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), action('KeyController@removeKey', ['redirUrl' => url()->full()])) }}">
                 <input type="hidden" name="redirUrl" value="{{ Request::input('redirUrl', '') }}" />
                 <button type="submit" class="btn btn-default">@lang('key.removeKey')</button>
             </form>
             @endif
-        </div>
-        @if(isset($keyValid) && $keyValid === false)
-        <p class="error">@lang('key.invalidKey')</p>
-        @endif
-        @if(Request::input('redirUrl', '') !== '')
+        </div>      
+        @if(Request::input('redirUrl', '') !== '' && parse_url(Request::input('redirUrl', ''), PHP_URL_HOST) === parse_url(url()->full(), PHP_URL_HOST))
         <div id="back-link"><a href="{{Request::input('redirUrl')}}">@lang('key.backLink')</a></div>
         @endif
     </div>
diff --git a/resources/views/keychange.blade.php b/resources/views/keychange.blade.php
new file mode 100644
index 0000000000000000000000000000000000000000..f1b5329b3417887fd68ab69c457fdc16a549f497
--- /dev/null
+++ b/resources/views/keychange.blade.php
@@ -0,0 +1,27 @@
+@extends('layouts.subPages', ['page' => 'key'])
+
+@section('title', $title )
+
+@section('content')
+<div class="card-heavy" id="steps">
+    <div class="step-one active">Entfernen des aktuellen Schlüssels</div>
+    <div class="step-two">Generieren des neuen Schlüssels</div>
+    <div class="step-three">Speichern des neuen Schlüssels</div>
+</div>
+<div class="card-heavy">
+<h1>@lang('keychange.h1')</h1>
+<p>@lang('keychange.p1', ["key" => $key])</p>
+<p>@lang('keychange.p2', ["key" => $key])</p>
+<p>@lang('keychange.p3')</p>
+<p>@lang('keychange.p4')</p>
+<ol>
+    <li>@lang('keychange.ol1.li1')</li>
+    <li>@lang('keychange.ol1.li2')</li>
+    <li>@lang('keychange.ol1.li3')</li>
+    <li>@lang('keychange.ol1.li4')</li>
+</ol>
+<form action="" method="post">
+    <button class="btn btn-default">@lang('keychange.a1')</button>
+</form>
+</div>
+@endsection
diff --git a/resources/views/keychangethree.blade.php b/resources/views/keychangethree.blade.php
new file mode 100644
index 0000000000000000000000000000000000000000..518852f51f13729a0f3b1a59d76d34ec7e9bf7a3
--- /dev/null
+++ b/resources/views/keychangethree.blade.php
@@ -0,0 +1,20 @@
+@extends('layouts.subPages', ['page' => 'key'])
+
+@section('title', $title )
+
+@section('content')
+<div class="card-heavy" id="steps">
+    <div class="step-one">Entfernen des aktuellen Schlüssels</div>
+    <div class="step-two">Generieren des neuen Schlüssels</div>
+    <div class="step-three active">Speichern des neuen Schlüssels</div>
+</div>
+<div class="card-heavy">
+    <h1>@lang('keychange.h1')</h1>
+    <p>@lang('keychange.p7')</p>
+    <p>@lang('keychange.p8')</p>
+    <div class="copyLink">
+        <input id="searchString" class="loadSettings" type="text" value="{{ Request::input('newkey', '') }}" readonly>
+        <button class="js-only btn btn-default" onclick="var copyText = document.getElementById('searchString');copyText.select();copyText.setSelectionRange(0, 99999);document.execCommand('copy');">@lang('settings.copy')</button>
+    </div>
+</div>
+@endsection
diff --git a/resources/views/keychangetwo.blade.php b/resources/views/keychangetwo.blade.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d224dd8833fb531bc41deef87a170a5ed95cca6
--- /dev/null
+++ b/resources/views/keychangetwo.blade.php
@@ -0,0 +1,35 @@
+@extends('layouts.subPages', ['page' => 'key'])
+
+@section('title', $title )
+
+@section('content')
+<div class="card-heavy" id="steps">
+    <div class="step-one">Entfernen des aktuellen Schlüssels</div>
+    <div class="step-two active">Generieren des neuen Schlüssels</div>
+    <div class="step-three">Speichern des neuen Schlüssels</div>
+</div>
+<div class="card-heavy">
+    <h1>@lang('keychange.h1')</h1>
+    <p>@lang('keychange.p5', [
+        'validUntil' => trim(str_replace(" später", "", $validUntil->longRelativeDiffForHumans(Carbon::now("Europe/London"), 1)))
+    ])</p>
+    <div class="copyLink">
+        <input id="searchString" class="loadSettings" type="text" value="{{ url()->full() }}" readonly>
+        <button class="js-only btn btn-default" onclick="var copyText = document.getElementById('searchString');copyText.select();copyText.setSelectionRange(0, 99999);document.execCommand('copy');">@lang('settings.copy')</button>
+    </div>
+    <br>
+    <p>@lang('keychange.p6')</p>
+    
+    <form action="{{ LaravelLocalization::getLocalizedURL(LaravelLocalization::getCurrentLocale(), route('changeKeyTwo')) }}" method="post">
+        <input type="hidden" name="validUntil" value="{{ Request::input('validUntil', '') }}">
+        <input type="hidden" name="password" value="{{ Request::input('password', '') }}">
+        @if(Request::filled('newkey'))
+        <p style="color: red">@lang('keychange.input1label')</p>
+        @else
+        <p><label for="mewkey">@lang('keychange.input1label')</label></p>
+        @endif
+        <input type="text" name="newkey" id="newkey" required placeholder="@lang('keychange.input1')" value="{{ Request::input('newkey', '') }}">
+        <button type="submit" class="btn btn-default">@lang('keychange.button1')</button>
+    </form>
+</div>
+@endsection
diff --git a/routes/cookie.php b/routes/cookie.php
index 3a6bc2cfca3407e15e9d1d561a8b8c2f7f44706d..afdcc09f807147b5d88598e89f32fc6d21d0f958 100644
--- a/routes/cookie.php
+++ b/routes/cookie.php
@@ -6,10 +6,27 @@ Route::group(
     'middleware' => [ 'localeSessionRedirect', 'localizationRedirect' ]*/
     ],
     function () {
-        Route::get('meta/key', "KeyController@index")->middleware('removekey');
+        Route::get('meta/key', "KeyController@index")->middleware('removekey')->name('keyindex');
         Route::post('meta/key', 'KeyController@setKey');
         Route::any('meta/key/remove', 'KeyController@removeKey')->name("removeCookie");
 
+        Route::group([
+            'prefix' => 'meta/key/change'
+        ], function() {
+            // Routes to change the key for members
+            Route::get('remove-current', 'KeyController@changeKeyIndex')->middleware('removekey')->name('changeKeyOne');
+            Route::post('remove-current', 'KeyController@removeCurrent')->middleware('removekey');
+
+            Route::get('generate-new', 'KeyController@generateNew')->name('changeKeyTwo');
+            Route::post('generate-new', 'KeyController@generateNewPost');
+
+            Route::get('save-new', function(){
+                return view('keychangethree', ["title" => trans('titles.keychange'), "css" => [mix('css/keychange/index.css')]]);
+            })->name('changeKeyThree');
+        });
+        
+
+
         Route::group([
             'prefix' => 'meta/settings',
         ],
diff --git a/webpack.mix.js b/webpack.mix.js
index a3b4e918884801252dbd44555cc517d25d42aa9b..3e1b9cdcffae4b1c81eec11c74ac6d78c04a683e 100644
--- a/webpack.mix.js
+++ b/webpack.mix.js
@@ -58,6 +58,9 @@ mix
   .less("resources/less/metager/pages/spende/danke.less", "public/css/spende/danke.css", {
     strictMath: true
   })
+  .less("resources/less/metager/pages/keychange/index.less", "public/css/keychange/index.css", {
+    strictMath: true
+  })
   // js
   .babel(
     [