diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..0d36f0e
--- /dev/null
+++ b/.env.example
@@ -0,0 +1 @@
+ENCRYPTION_KEY=b5df1f760870a9a150bd25bbe665e0753f481210e4d90eeeacff075e6b51d6c6
diff --git a/jest.config.js b/jest.config.js
index 41794cf..c619449 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -2,6 +2,7 @@
 module.exports = {
   preset: 'ts-jest',
   testEnvironment: 'node',
+  setupFiles: ["<rootDir>/tests/setup.ts"],
   moduleNameMapper: {
     '^@/(.*)$': '<rootDir>/src/$1',
   },
diff --git a/package-lock.json b/package-lock.json
index 3108156..f70b65a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
       "name": "yubico-validation-server",
       "version": "0.1.0",
       "dependencies": {
+        "cryptr": "^6.3.0",
         "next": "14.1.0",
         "react": "^18",
         "react-dom": "^18",
@@ -19,6 +20,7 @@
         "@types/react": "^18",
         "@types/react-dom": "^18",
         "autoprefixer": "^10.0.1",
+        "dotenv": "^16.3.2",
         "eslint": "^8",
         "eslint-config-next": "14.1.0",
         "jest": "^29.7.0",
@@ -2708,6 +2710,11 @@
         "node": ">= 8"
       }
     },
+    "node_modules/cryptr": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/cryptr/-/cryptr-6.3.0.tgz",
+      "integrity": "sha512-TA4byAuorT8qooU9H8YJhBwnqD151i1rcauHfJ3Divg6HmukHB2AYMp0hmjv2873J2alr4t15QqC7zAnWFrtfQ=="
+    },
     "node_modules/cssesc": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -2872,6 +2879,18 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/dotenv": {
+      "version": "16.3.2",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz",
+      "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/motdotla/dotenv?sponsor=1"
+      }
+    },
     "node_modules/eastasianwidth": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
diff --git a/package.json b/package.json
index 5541f2c..02d05c4 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,7 @@
     "test": "jest --passWithNoTests"
   },
   "dependencies": {
+    "cryptr": "^6.3.0",
     "next": "14.1.0",
     "react": "^18",
     "react-dom": "^18",
@@ -21,6 +22,7 @@
     "@types/react": "^18",
     "@types/react-dom": "^18",
     "autoprefixer": "^10.0.1",
+    "dotenv": "^16.3.2",
     "eslint": "^8",
     "eslint-config-next": "14.1.0",
     "jest": "^29.7.0",
diff --git a/src/lib/encryption.ts b/src/lib/encryption.ts
new file mode 100644
index 0000000..fdc0f8f
--- /dev/null
+++ b/src/lib/encryption.ts
@@ -0,0 +1,10 @@
+import Cryptr from 'cryptr';
+const cryptr = new Cryptr(process.env.ENCRYPTION_KEY || '');
+
+export const encrypt = (string: string): string => {
+  return cryptr.encrypt(string);
+};
+
+export const decrypt = (encryptedString: string): string => {
+  return cryptr.decrypt(encryptedString);
+};
diff --git a/tests/lib/encryption.test.ts b/tests/lib/encryption.test.ts
new file mode 100644
index 0000000..5a83cba
--- /dev/null
+++ b/tests/lib/encryption.test.ts
@@ -0,0 +1,17 @@
+import { encrypt, decrypt } from '@/lib/encryption';
+
+const strings: {plain: string, encrypted: string | null} = {
+  plain: 'hey',
+  encrypted: null,
+};
+
+describe('Encryption module', () => {
+  it('Must encrypt string', () => {
+    strings.encrypted = encrypt(strings.plain);
+    expect(strings.encrypted).not.toBe(strings.plain);
+  });
+
+  it('Must decrypt string', () => {
+    expect(decrypt(strings.encrypted as string)).toBe(strings.plain);
+  });
+});
diff --git a/tests/setup.ts b/tests/setup.ts
new file mode 100644
index 0000000..6e92481
--- /dev/null
+++ b/tests/setup.ts
@@ -0,0 +1,3 @@
+import dotenv from 'dotenv';
+
+dotenv.config({ path: '.env.example' });