App för att kryptera meddelanden

Marcus Olsson,

Flera gånger de senaste månaderna har jag tagit emot leveranser av t.ex. lösenord och API-nycklar, och det blir alltid lite krångel med hur man ska leverera dem. Man vill ju gärna inte skicka dem direkt i ett mail.

Dela via 1Password? Helt okej. Krypterad .zip med separat leverans av lösenord via SMS? Funkar för det mesta. "Okända" externa tjänster börja bli lite mer känsligt … och att be folk att signa upp för t.ex. Keybase kan vara mycket begärt (men ändå enklare än att be någon köra PGP lokalt).

Så jag snickrade ihop en enkel webbapp med Alpine.js och OpenPGP.js som läser in min publika PHP-nyckel och tillåter en att kryptera meddelanden ämnade för mig. Därefter kan jag dekryptera meddelandet enkelt med min privata nyckel.

Själva appen blev på knappt 30 rader:

1document.addEventListener('alpine:init', () => {
2 Alpine.data('encryptor', () => ({
3 message: '',
4 encryptedMessage: '',
5 
6 async fetchPGPKey(url) {
7 const response = await fetch(url);
8 return response.text();
9 },
10 async encryptMessage(publicKeyArmored, message) {
11 const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
12 const encryptedMessage = await openpgp.encrypt({
13 message: await openpgp.createMessage({ text: message }),
14 encryptionKeys: publicKey,
15 });
16 
17 return encryptedMessage;
18 },
19 async encrypt() {
20 const publicKeyURL = '-----BEGIN PGP PUBLIC KEY BLOCK-----xxx';
21 try {
22 const publicKeyArmored = await this.fetchPGPKey(publicKeyURL);
23 this.encryptedMessage = await this.encryptMessage(publicKeyArmored, this.message);
24 } catch (error) {
25 console.error('Encryption failed:', error);
26 this.encryptedMessage = 'Encryption error.';
27 }
28 }
29 }));
30});
1document.addEventListener('alpine:init', () => {
2 Alpine.data('encryptor', () => ({
3 message: '',
4 encryptedMessage: '',
5 
6 async fetchPGPKey(url) {
7 const response = await fetch(url);
8 return response.text();
9 },
10 async encryptMessage(publicKeyArmored, message) {
11 const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
12 const encryptedMessage = await openpgp.encrypt({
13 message: await openpgp.createMessage({ text: message }),
14 encryptionKeys: publicKey,
15 });
16 
17 return encryptedMessage;
18 },
19 async encrypt() {
20 const publicKeyURL = '-----BEGIN PGP PUBLIC KEY BLOCK-----xxx';
21 try {
22 const publicKeyArmored = await this.fetchPGPKey(publicKeyURL);
23 this.encryptedMessage = await this.encryptMessage(publicKeyArmored, this.message);
24 } catch (error) {
25 console.error('Encryption failed:', error);
26 this.encryptedMessage = 'Encryption error.';
27 }
28 }
29 }));
30});

Och därtill lite HTML:

1<div
2 x-data="{...encryptor()}"
3>
4 <div x-show="!encryptedMessage">
5 <textarea x-model="message"></textarea>
6 <button @click="encrypt">Kryptera</button>
7 </div>
8 <div x-show="encryptedMessage">
9 <textarea x-model="encryptedMessage" disabled></textarea>
10 </div>
11</div>
1<div
2 x-data="{...encryptor()}"
3>
4 <div x-show="!encryptedMessage">
5 <textarea x-model="message"></textarea>
6 <button @click="encrypt">Kryptera</button>
7 </div>
8 <div x-show="encryptedMessage">
9 <textarea x-model="encryptedMessage" disabled></textarea>
10 </div>
11</div>

Funkar riktigt bra för mina ganska enkla behov. Själva appen hittar du här.