Verschlüsselung und Entschlüsselung von JavaScript-Zeichenfolgen?


151

Ich bin daran interessiert, eine kleine App für den persönlichen Gebrauch zu erstellen, die Informationen auf der Clientseite mit JavaScript verschlüsselt und entschlüsselt. Die verschlüsselten Informationen werden in einer Datenbank auf einem Server gespeichert, jedoch niemals in der entschlüsselten Version.

Es muss nicht super duper sicher sein, aber ich möchte einen derzeit ungebrochenen Algorithmus verwenden.

Im Idealfall könnte ich so etwas tun

var gibberish = encrypt(string, salt, key);

um die codierte Zeichenfolge zu generieren, und so ähnlich

var sensical = decrypt(gibberish, key);

um es später zu entschlüsseln.

Bisher habe ich Folgendes gesehen: http://bitwiseshiftleft.github.io/sjcl/

Gibt es noch andere Bibliotheken, die ich mir ansehen sollte?


2
Werfen


10
Einige Begriffe hier sind deaktiviert. Hier ist eine einfache Version 1. Salze werden zu Informationen (normalerweise Passwörtern) hinzugefügt, die gehasht werden. Ihr Zweck ist es, den Hasch anders zu machen als ohne das Salz. Dies ist nützlich, da Hashes vorgeneriert werden, wenn Ihre Datenbank gehackt wird und gehashte Benutzerkennwörter herauskommen. 2. Hashing ist eine Einwegoperation, die Eingabe in Ausgabe umwandelt. Es kann nicht einfach rückgängig gemacht oder rückgängig gemacht werden. 3. Codierung ist keine Verschlüsselung. base64_encode, urlencode usw.
des

Antworten:


159

 var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
//U2FsdGVkX18ZUVvShFSES21qHsQEqZXMxQ9zgHy+bu0=

var decrypted = CryptoJS.AES.decrypt(encrypted, "Secret Passphrase");
//4d657373616765


document.getElementById("demo1").innerHTML = encrypted;
document.getElementById("demo2").innerHTML = decrypted;
document.getElementById("demo3").innerHTML = decrypted.toString(CryptoJS.enc.Utf8);
Full working sample actually is:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js" integrity="sha256-/H4YS+7aYb9kJ5OKhFYPUjSJdrtV6AeyJOtTkw6X72o=" crossorigin="anonymous"></script>

<br><br>
<label>encrypted</label>
<div id="demo1"></div>
<br>

<label>decrypted</label>
<div id="demo2"></div>

<br>
<label>Actual Message</label>
<div id="demo3"></div>


8
Encrypted ist eigentlich ein Objekt, aber Sie können encrypted.toString () aufrufen, um die Zeichenfolge abzurufen. Sie können diese Zeichenfolge später entschlüsseln: jsbin.com/kofiqokoku/1
Tomas Kirda

9
Aber wie können wir die geheime Passphrase sichern?
Duykhoa

9
Es scheint, dass crypto js ein archiviertes Projekt ist. Es gibt einen Klon auf github: github.com/sytelus/CryptoJS , der jedoch seit zwei Jahren nicht mehr aktualisiert wurde. Ist dies immer noch die beste Option für die js-Verschlüsselung?
Syonip

2
Ich würde mit diesem gehen: github.com/brix/crypto-js es ist auch über NPM verfügbar
Tomas Kirda

1
@stom es liegt an dir, wie und wo du es aufbewahrst. Ich weiß nicht, ob es wirklich sichere Möglichkeiten gibt, es in einem Browser zu speichern. Fordern Sie sie vom Server an und speichern Sie sie im Speicher.
Tomas Kirda

62

Wie wäre es mit CryptoJS ?

Es ist eine solide Kryptobibliothek mit vielen Funktionen. Es implementiert Hashers, HMAC, PBKDF2 und Chiffren. In diesem Fall benötigen Sie Chiffren. Schauen Sie sich das Schnellstart-Quide auf der Homepage des Projekts an.

Sie könnten so etwas wie mit dem AES machen:

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/aes.js"></script>

<script>
    var encryptedAES = CryptoJS.AES.encrypt("Message", "My Secret Passphrase");
    var decryptedBytes = CryptoJS.AES.decrypt(encryptedAES, "My Secret Passphrase");
    var plaintext = decryptedBytes.toString(CryptoJS.enc.Utf8);
</script>

Aus Sicherheitsgründen wird der AES-Algorithmus zum Zeitpunkt meines Schreibens als ungebrochen angesehen

Bearbeiten:

Die Online-URL scheint nicht verfügbar zu sein. Sie können die heruntergeladenen Dateien zur Verschlüsselung über den unten angegebenen Link verwenden und die entsprechenden Dateien in Ihrem Stammordner der Anwendung ablegen.

https://code.google.com/archive/p/crypto-js/downloads

oder ein anderes CDN wie https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js verwendet


Was ist der Unterschied zwischen Rollups und Komponenten unter Ordner 3.1.2?
Kanagavelu Sugumar

Nach einigem Spielen sind die Komponenten die getrennten Teile. Sie müssen wissen, welche Komponenten (und in welcher Reihenfolge) verwendet werden müssen, damit sie funktionieren. Die Rollup-Dateien enthalten alles, was Sie benötigen, damit es mit nur einer Skriptreferenz funktioniert (viel besser, da die harte Arbeit bereits erledigt ist).
Shahar Eldad

2
Aber wie können wir die geheime Passphrase sichern?
Shaijut

@shaijut Du nicht. Sie speichern es nicht einmal irgendwo anders als im RAM, wenn Sie den Klartext verschlüsseln / entschlüsseln. Die Passphrase sollte nur im Gehirn des Benutzers (oder in einem Passwort-Manager)
gespeichert werden

39

Ich habe eine unsichere, aber einfache Textverschlüsselung / -entschlüsselung erstellt. Keine Abhängigkeiten mit einer externen Bibliothek.

Dies sind die Funktionen

const cipher = salt => {
    const textToChars = text => text.split('').map(c => c.charCodeAt(0));
    const byteHex = n => ("0" + Number(n).toString(16)).substr(-2);
    const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);

    return text => text.split('')
        .map(textToChars)
        .map(applySaltToChar)
        .map(byteHex)
        .join('');
}

const decipher = salt => {
    const textToChars = text => text.split('').map(c => c.charCodeAt(0));
    const applySaltToChar = code => textToChars(salt).reduce((a,b) => a ^ b, code);
    return encoded => encoded.match(/.{1,2}/g)
        .map(hex => parseInt(hex, 16))
        .map(applySaltToChar)
        .map(charCode => String.fromCharCode(charCode))
        .join('');
}

Und Sie können sie wie folgt verwenden:

// To create a cipher
const myCipher = cipher('mySecretSalt')

//Then cipher any text:
myCipher('the secret string')   // --> "7c606d287b6d6b7a6d7c287b7c7a61666f"

//To decipher, you need to create a decipher and use it:
const myDecipher = decipher('mySecretSalt')
myDecipher("7c606d287b6d6b7a6d7c287b7c7a61666f")    // --> 'the secret string'

4
let myDecipher = decipher ('CartelSystem') - Dieses Salt entschlüsselt auch die Zeichenfolge. Sie müssen nicht das genaue Wort "mySecretSalt"
Dror Bar

Wird SaltChars in der Entschlüsselung nicht verwendet?
Dror Bar

1
Noch ein Beitrag, in dem jemand blindlings benutzt let. 😒︎
John

1
Ist das nicht a) super kaputt und unsicher und b) das "Salz" ist tatsächlich Ihr "geheimer Schlüssel", da nicht erwartet wird, dass Salze privat sind? Ich denke, es ist sehr gefährlich, einen solchen Code ohne Kommentare zu posten, dass dieser Fun-Code nicht für den realen Gebrauch bestimmt ist. Die Anzahl der Upvotes ist besorgniserregend. crypto.stackexchange.com/questions/11466/…
lschmierer

1
Zumindest verwenden sie Sound-Krypto. Was Sie tun, ist im Grunde eine Caesar-Chiffre (die jedem Charakter den gleichen Schlüssel zuweist). En.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher In Bezug auf die anderen Antworten ... Ich erwarte, dass es offensichtlich ist, dass etwas, das als "Geheimnis" bezeichnet wird voraussichtlich geheim gehalten (vom Benutzer)
lschmierer

18

Die vorhandenen Antworten, die SJCL, CryptoJS und / oder WebCrypto nutzen, sind nicht unbedingt falsch, aber nicht so sicher, wie Sie zunächst vermuten. Im Allgemeinen möchten Sie libsodium verwenden . Zuerst erkläre ich warum, dann wie.

Warum nicht SJCL, CryptoJS, WebCrypto usw.?

Kurze Antwort: Damit Ihre Verschlüsselung tatsächlich sicher ist, erwarten diese Bibliotheken, dass Sie zu viele Entscheidungen treffen, z. B. den Blockverschlüsselungsmodus (CBC, CTR, GCM; wenn Sie nicht sagen können, für welche der drei oben aufgeführten Verschlüsselungen sicher ist verwenden und unter welchen Einschränkungen sollten Sie mit dieser Art von Auswahl überhaupt nicht belastet werden ).

Sofern Ihre Berufsbezeichnung nicht Kryptografietechniker ist , stehen die Chancen gut, dass Sie sie sicher implementieren.

Warum CryptoJS vermeiden?

CryptoJS bietet eine Handvoll Bausteine ​​und erwartet, dass Sie wissen, wie Sie diese sicher verwenden können. Standardmäßig wird sogar der CBC-Modus verwendet ( archiviert ).

Warum ist der CBC-Modus schlecht?

Lesen Sie diesen Artikel über AES-CBC-Schwachstellen .

Warum sollte man WebCrypto vermeiden?

WebCrypto ist ein Potluck-Standard, der vom Komitee für Zwecke entwickelt wurde, die orthogonal zur Kryptografietechnik sind. Insbesondere sollte WebCrypto Flash ersetzen und keine Sicherheit bieten .

Warum SJCL vermeiden?

Die öffentliche API und Dokumentation von SJCL fordert Benutzer auf, Daten mit einem vom Menschen gespeicherten Passwort zu verschlüsseln. Dies ist selten, wenn überhaupt, das, was Sie in der realen Welt tun möchten.

Zusätzlich: Die Standardanzahl der PBKDF2-Runden ist ungefähr 86-mal so klein, wie Sie es möchten . AES-128-CCM ist wahrscheinlich in Ordnung.

Von den drei oben genannten Optionen endet SJCL am seltensten in Tränen. Es gibt jedoch bessere Optionen.

Warum ist Libsodium besser?

Sie müssen nicht zwischen einem Menü mit Verschlüsselungsmodi, Hash-Funktionen und anderen unnötigen Optionen wählen. Sie werden niemals riskieren, Ihre Parameter zu vermasseln und die gesamte Sicherheit aus Ihrem Protokoll zu entfernen .

Stattdessen bietet libsodium nur einfache Optionen, die auf maximale Sicherheit und minimalistische APIs abgestimmt sind.

  • crypto_box()Ich crypto_box_open()biete eine authentifizierte Verschlüsselung mit öffentlichem Schlüssel an.
    • Der fragliche Algorithmus kombiniert X25519 (ECDH über Curve25519) und XSalsa20-Poly1305, aber Sie müssen dies nicht wissen (oder sich auch nur darum kümmern), um es sicher zu verwenden
  • crypto_secretbox()Ich crypto_secretbox_open()biete eine authentifizierte Verschlüsselung mit gemeinsamem Schlüssel an.
    • Der fragliche Algorithmus ist XSalsa20-Poly1305, aber Sie müssen es nicht wissen / pflegen

Darüber hinaus verfügt libsodium über Bindungen in Dutzenden gängiger Programmiersprachen. Daher ist es sehr wahrscheinlich, dass libsodium nur funktioniert, wenn versucht wird, mit einem anderen Programmierstapel zusammenzuarbeiten. Außerdem ist libsodium in der Regel sehr schnell, ohne die Sicherheit zu beeinträchtigen.

Wie verwende ich Libsodium in JavaScript?

Zunächst müssen Sie eines entscheiden:

  1. Möchten Sie nur Daten verschlüsseln / entschlüsseln (und vielleicht trotzdem den Klartext in Datenbankabfragen sicher verwenden) und sich nicht um die Details kümmern? Oder...
  2. Müssen Sie ein bestimmtes Protokoll implementieren?

Wenn Sie die erste Option ausgewählt haben , rufen Sie CipherSweet.js ab .

Die Dokumentation ist online verfügbar . EncryptedFieldist für die meisten Anwendungsfälle ausreichend, aber die APIs EncryptedRowund EncryptedMultiRowskönnen einfacher sein, wenn Sie viele unterschiedliche Felder haben, die Sie verschlüsseln möchten.

Mit CipherSweet müssen Sie nicht einmal wissen, was ein Nonce / IV ist , um es sicher zu verwenden.

Darüber hinaus ist diese Griffe int/ floatVerschlüsselung ohne Fakten über den Inhalt durch verschlüsselte Text Größe undicht.

Andernfalls benötigen Sie Natrium-Plus , ein benutzerfreundliches Frontend für verschiedene Libsodium-Wrapper. Mit Sodium-Plus können Sie performanten, asynchronen, plattformübergreifenden Code schreiben, der einfach zu prüfen und zu begründen ist.

Um Natrium-Plus zu installieren, führen Sie einfach ...

npm install sodium-plus

Derzeit gibt es kein öffentliches CDN für die Browserunterstützung. Dies wird sich bald ändern. Sie können jedoch bei Bedarf sodium-plus.min.jsaus der neuesten Github-Version greifen .

const { SodiumPlus } = require('sodium-plus');
let sodium;

(async function () {
    if (!sodium) sodium = await SodiumPlus.auto();
    let plaintext = 'Your message goes here';
    let key = await sodium.crypto_secretbox_keygen();
    let nonce = await sodium.randombytes_buf(24);
    let ciphertext = await sodium.crypto_secretbox(
        plaintext,
        nonce,
        key    
    );
    console.log(ciphertext.toString('hex'));

    let decrypted = await sodium.crypto_secretbox_open(
        ciphertext,
        nonce,
        key
    );

    console.log(decrypted.toString());
})();

Die Dokumentation zu Natrium-Plus ist auf Github verfügbar.

Wenn Sie eine Schritt-für-Schritt-Anleitung wünschen , finden Sie in diesem dev.to-Artikel genau das, wonach Sie suchen.



4

Bevor Sie dies implementieren, lesen Sie bitte die Antwort von Scott Arciszewski .

Ich möchte, dass Sie sehr vorsichtig mit dem sind, was ich mitteilen möchte, da ich wenig bis gar keine Sicherheitskenntnisse habe (es besteht eine hohe Wahrscheinlichkeit, dass ich die unten stehende API missbrauche). Daher würde ich diese Antwort gerne aktualisieren mit Hilfe der Gemeinschaft .

Wie @richardtallent in seiner Antwort erwähnt hat , wird die Web Crypto API unterstützt, daher verwendet dieses Beispiel den Standard. Zum jetzigen Zeitpunkt gibt es 95,88% der weltweiten Browserunterstützung .

Ich werde ein Beispiel mit der Web Crypto API teilen

Bevor wir fortfahren, beachten Sie bitte ( Zitat aus MDN ):

Diese API bietet eine Reihe von kryptografischen Grundelementen auf niedriger Ebene. Es ist sehr leicht, sie zu missbrauchen , und die damit verbundenen Fallstricke können sehr subtil sein .

Selbst wenn Sie die grundlegenden kryptografischen Funktionen korrekt verwenden, sind die sichere Schlüsselverwaltung und das gesamte Design des Sicherheitssystems äußerst schwierig und sind im Allgemeinen die Domäne spezialisierter Sicherheitsexperten.

Fehler im Design und in der Implementierung des Sicherheitssystems können die Sicherheit des Systems völlig unwirksam machen.

Wenn Sie nicht sicher sind, was Sie tun, sollten Sie diese API wahrscheinlich nicht verwenden .

Ich respektiere die Sicherheit sehr und habe sogar zusätzliche Teile von MDN hervorgehoben ... Sie wurden gewarnt.

Nun zum eigentlichen Beispiel ...


JSFiddle:

Hier zu finden: https://jsfiddle.net/superjose/rm4e0gqa/5/

Hinweis:

Beachten Sie die Verwendung von awaitSchlüsselwörtern. Verwenden Sie es innerhalb einer asyncFunktion oder verwenden Sie .then()und .catch().

Generieren Sie den Schlüssel:

// https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey
// https://developer.mozilla.org/en-US/docs/Web/API/RsaHashedKeyGenParams
// https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
    const stringToEncrypt = 'https://localhost:3001';
    // https://github.com/diafygi/webcrypto-examples#rsa-oaep---generatekey
    // The resultant publicKey will be used to encrypt
    // and the privateKey will be used to decrypt. 
    // Note: This will generate new keys each time, you must store both of them in order for 
    // you to keep encrypting and decrypting.
    //
    // I warn you that storing them in the localStorage may be a bad idea, and it gets out of the scope
    // of this post. 
    const key = await crypto.subtle.generateKey({
      name: 'RSA-OAEP',
      modulusLength: 4096,
      publicExponent:  new Uint8Array([0x01, 0x00, 0x01]),
      hash: {name: 'SHA-512'},
      
    }, true,
    // This depends a lot on the algorithm used
    // Go to https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto
    // and scroll down to see the table. Since we're using RSA-OAEP we have encrypt and decrypt available
    ['encrypt', 'decrypt']);

    // key will yield a key.publicKey and key.privateKey property.

Verschlüsseln:

    const encryptedUri = await crypto.subtle.encrypt({
      name: 'RSA-OAEP'
    }, key.publicKey, stringToArrayBuffer(stringToEncrypt))
    
    console.log('The encrypted string is', encryptedUri);

Entschlüsseln

   const msg = await  crypto.subtle.decrypt({
      name: 'RSA-OAEP',
    }, key.privateKey, encryptedUri);
    console.log(`Derypted Uri is ${arrayBufferToString(msg)}`)

Konvertieren von ArrayBuffer von String hin und her (Fertig in TypeScript):

  private arrayBufferToString(buff: ArrayBuffer) {
    return String.fromCharCode.apply(null, new Uint16Array(buff) as unknown as number[]);
  }

  private stringToArrayBuffer(str: string) {
    const buff = new ArrayBuffer(str.length*2) // Because there are 2 bytes for each char.
    const buffView = new Uint16Array(buff);
    for(let i = 0, strLen = str.length; i < strLen; i++) {
      buffView[i] = str.charCodeAt(i);
    }
    return buff;
  }

Weitere Beispiele finden Sie hier (ich bin nicht der Eigentümer): // https://github.com/diafygi/webcrypto-examples


2

CryptoJS wird nicht mehr unterstützt. Wenn Sie es weiterhin verwenden möchten, können Sie zu dieser URL wechseln:

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>


Was ist der Unterschied zwischen Rollups und Komponenten unter Ordner 3.1.2?
Kanagavelu Sugumar

1
Crypto empfiehlt die Forge- Bibliothek, wenn Sie ihre Site betreten.
Dror Bar

1

Verwenden Sie SimpleCrypto

Verwenden von encrypt () und decrypt ()

Um SimpleCrypto zu verwenden, erstellen Sie zunächst eine SimpleCrypto-Instanz mit einem geheimen Schlüssel (Kennwort). Der geheime Schlüsselparameter MUSS beim Erstellen einer SimpleCrypto-Instanz definiert werden.

Verwenden Sie zum Ver- und Entschlüsseln von Daten einfach die Funktionen encrypt () und decrypt () einer Instanz. Dies verwendet einen AES-CBC-Verschlüsselungsalgorithmus.

var _secretKey = "some-unique-key";

var simpleCrypto = new SimpleCrypto(_secretKey);

var plainText = "Hello World!";
var chiperText = simpleCrypto.encrypt(plainText);
console.log("Encryption process...");
console.log("Plain Text    : " + plainText);
console.log("Cipher Text   : " + cipherText);
var decipherText = simpleCrypto.decrypt(cipherText);
console.log("... and then decryption...");
console.log("Decipher Text : " + decipherText);
console.log("... done.");

3
SimpleCrypto verwendet nicht authentifiziertes AES-CBC und ist daher anfällig für Angriffe mit ausgewähltem Chiffretext.
Scott Arciszewski

-6

Einfache Funktionen,


function Encrypt(value) 
{
  var result="";
  for(i=0;i<value.length;i++)
  {
    if(i<value.length-1)
    {
        result+=value.charCodeAt(i)+10;
        result+="-";
    }
    else
    {
        result+=value.charCodeAt(i)+10;
    }
  }
  return result;
}
function Decrypt(value)
{
  var result="";
  var array = value.split("-");

  for(i=0;i<array.length;i++)
  {
    result+=String.fromCharCode(array[i]-10);
  }
  return result;
} 

4
Während dieses Code-Snippet die Lösung sein kann, hilft das Hinzufügen einer Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage in Zukunft für Leser beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen.
Johan

1
Dies ist kein sicherer Algorithmus (beachten Sie, dass Encrypt keinen Schlüsselparameter verwendet) und kann leicht rückgängig gemacht werden. Das OP bat um etwas, das Sicherheit hatte.
Mike S
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.