Enkripsi dan dekripsi string JavaScript?


153

Saya tertarik membangun aplikasi kecil untuk penggunaan pribadi yang akan mengenkripsi dan mendekripsi informasi di sisi klien menggunakan JavaScript. Informasi terenkripsi akan disimpan dalam database di server, tetapi tidak pernah versi yang dienkripsi.

Tidak harus super duper aman, tapi saya ingin menggunakan algoritma yang saat ini tidak terputus.

Idealnya saya bisa melakukan sesuatu seperti

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

untuk menghasilkan string yang disandikan, dan sesuatu seperti

var sensical = decrypt(gibberish, key);

untuk memecahkan kode nanti.

Sejauh ini saya telah melihat ini: http://bitwiseshiftleft.github.io/sjcl/

Ada perpustakaan lain yang harus saya lihat?




10
Beberapa terminologi di sini mati, Ini versi sederhana 1. Garam ditambahkan ke informasi (biasanya kata sandi) yang di-hash. Tujuan mereka adalah untuk membuat hash berbeda dari yang seharusnya tanpa garam. Ini berguna karena membuat hash yang dihasilkan sebelumnya jika basis data Anda diretas dan kata sandi pengguna yang di-hash keluar. 2. Hashing adalah operasi satu arah yang menerjemahkan input menjadi output. Itu tidak bisa dengan mudah dibalik atau dibatalkan. 3. Pengkodean bukan enkripsi. base64_encode, urlencode, dll.
des

Jawaban:


160

 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
Dienkripsi sebenarnya adalah objek, tetapi Anda dapat memanggil encrypted.toString () untuk mendapatkan string. Anda dapat mendekripsi string itu nanti: jsbin.com/kofiqokoku/1
Tomas Kirda

9
Tetapi bagaimana kita dapat mengamankan frasa sandi Rahasia?
duykhoa

9
Tampaknya crypto js adalah proyek yang diarsipkan. Ada klon di github: github.com/sytelus/CryptoJS tetapi belum diperbarui dalam dua tahun. Apakah ini masih pilihan terbaik untuk enkripsi js?
syonip

2
Saya akan menggunakan yang ini: github.com/brix/crypto-js juga tersedia melalui NPM
Tomas Kirda

1
@stom terserah Anda bagaimana dan di mana Anda menyimpannya. Saya tidak tahu apakah ada cara yang benar-benar aman untuk menyimpannya di browser. Minta mereka dari server dan simpan di memori.
Tomas Kirda

62

Bagaimana dengan CryptoJS ?

Ini adalah perpustakaan crypto yang solid, dengan banyak fungsi. Ini mengimplementasikan hashers, HMAC, PBKDF2 dan cipher. Dalam hal ini, sandi adalah yang Anda butuhkan. Lihat rangkuman mulai cepat di beranda proyek.

Anda dapat melakukan sesuatu seperti dengan AES:

<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>

Sedangkan untuk keamanan, saat ini saya menulis algoritma AES dianggap tidak terputus

Edit:

Tampaknya URL online tidak aktif & Anda dapat menggunakan file yang diunduh untuk enkripsi dari tautan yang diberikan di bawah ini & letakkan file tersebut di folder root aplikasi Anda.

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

atau menggunakan CDN lain seperti https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js


Apa perbedaan antara rollup dan komponen di bawah folder 3.1.2?
Kanagavelu Sugumar

Setelah diputar sedikit komponen adalah bagian yang terpisah. Anda perlu mengetahui komponen mana yang harus diambil (dan dalam urutan apa) untuk membuatnya berfungsi. File-file rollup berisi semua yang Anda butuhkan untuk membuatnya berfungsi hanya dengan satu referensi skrip (jauh lebih baik dari kerja keras yang sudah dilakukan).
shahar eldad

2
Tetapi bagaimana kita dapat mengamankan frasa sandi Rahasia?
shaijut

@ shaijut Anda tidak. Anda bahkan tidak menyimpannya di mana pun kecuali dalam RAM saat mengenkripsi / mendekripsi plaintext. Frasa sandi hanya boleh disimpan di otak pengguna (atau pengelola kata sandi)
slebetman

39

Saya membuat util text cipher / decipher sederhana namun tidak aman. Tidak ada ketergantungan dengan perpustakaan eksternal.

Inilah fungsinya

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('');
}

Dan Anda dapat menggunakannya sebagai berikut:

// 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
biarkan myDecipher = decipher ('CartelSystem') - Garam ini juga akan menguraikan string. Anda tidak perlu tahu kata persisnya 'mySecretSalt'
Dror Bar

Juga, apakah saltChars dalam menguraikan tidak digunakan?
Dror Bar

1
Namun pos lain di mana seseorang secara buta menggunakan let. 😒︎
John

1
Bukankah ini a) super rusak dan tidak aman dan b) 'garam' sebenarnya adalah 'kunci rahasia' Anda karena garam tidak diharapkan bersifat pribadi? Saya pikir sangat berbahaya untuk memposting kode seperti ini tanpa komentar bahwa kode-senang ini tidak dimaksudkan untuk penggunaan dunia nyata. Jumlah upvotes mengkhawatirkan. crypto.stackexchange.com/questions/11466/...
lschmierer

1
Yah setidaknya mereka menggunakan crypto suara. Apa yang Anda lakukan pada dasarnya adalah Caesar Chipher (menerapkan kunci yang sama untuk setiap karakter) en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher Mengenai jawaban lain ... Saya berharap jelas bahwa sesuatu yang disebut "rahasia" adalah diharapkan dirahasiakan (oleh pengguna)
lschmierer

19

Jawaban yang ada yang memanfaatkan SJCL, CryptoJS, dan / atau WebCrypto tidak selalu salah tetapi tidak seaman yang awalnya Anda duga. Umumnya Anda ingin menggunakan libsodium . Pertama saya akan menjelaskan mengapa, lalu bagaimana.

Mengapa Tidak SJCL, CryptoJS, WebCrypto, dll?

Jawaban singkat: Agar enkripsi Anda benar-benar aman, perpustakaan ini mengharapkan Anda untuk membuat terlalu banyak pilihan, misalnya mode blok cipher (CBC, CTR, GCM; jika Anda tidak tahu yang mana dari tiga yang saya daftarkan aman untuk gunakan dan di bawah kendala apa, Anda seharusnya tidak terbebani dengan pilihan semacam ini sama sekali ).

Kecuali jika jabatan Anda adalah insinyur kriptografi , kemungkinan besar Anda tidak dapat menerapkannya dengan aman.

Mengapa Harus Menghindari CryptoJS?

CryptoJS menawarkan beberapa blok bangunan dan mengharapkan Anda tahu cara menggunakannya dengan aman. Bahkan default ke mode CBC ( diarsipkan ).

Mengapa mode CBC buruk?

Baca artikel ini tentang kerentanan AES-CBC .

Mengapa Harus Menghindari WebCrypto?

WebCrypto adalah standar potluck, dirancang oleh komite, untuk tujuan yang ortogonal untuk teknik kriptografi. Secara khusus, WebCrypto dimaksudkan untuk menggantikan Flash, bukan memberikan keamanan .

Mengapa Harus Menghindari SJCL?

API dan dokumentasi publik SJCL memohon pengguna untuk mengenkripsi data dengan kata sandi yang diingat manusia. Ini jarang, jika pernah, apa yang ingin Anda lakukan di dunia nyata.

Selain itu: Jumlah putaran standar PBKDF2 kira-kira 86 kali lebih kecil dari yang Anda inginkan . AES-128-CCM mungkin baik-baik saja.

Dari tiga opsi di atas, SJCL adalah yang paling mungkin berakhir dengan menangis. Tetapi ada opsi yang lebih baik tersedia.

Mengapa Libsodium Lebih Baik?

Anda tidak perlu memilih antara menu mode sandi, fungsi hash, dan opsi lain yang tidak perlu. Anda tidak akan pernah berisiko mengacaukan parameter Anda dan menghapus semua keamanan dari protokol Anda .

Sebagai gantinya, libsodium hanya memberi Anda opsi sederhana yang disetel untuk keamanan maksimum dan API minimalis.

  • crypto_box()/ crypto_box_open()tawarkan enkripsi kunci publik terotentikasi.
    • Algoritme yang dimaksud menggabungkan X25519 (ECDH over Curve25519) dan XSalsa20-Poly1305, tetapi Anda tidak perlu tahu (atau bahkan peduli) tentang itu untuk menggunakannya dengan aman
  • crypto_secretbox()/ crypto_secretbox_open()tawarkan enkripsi autentikasi bersama-kunci.
    • Algoritma yang dimaksud adalah XSalsa20-Poly1305, tetapi Anda tidak perlu tahu / peduli

Selain itu, libsodium memiliki ikatan dalam lusinan bahasa pemrograman populer , sehingga sangat mungkin bahwa libsodium hanya akan berfungsi ketika mencoba untuk beroperasi dengan tumpukan pemrograman lain. Juga, libsodium cenderung sangat cepat tanpa mengorbankan keamanan.

Bagaimana Cara Menggunakan Libsodium dalam JavaScript?

Pertama, Anda perlu memutuskan satu hal:

  1. Apakah Anda hanya ingin mengenkripsi / mendekripsi data (dan mungkin masih menggunakan plaintext dalam kueri basis data dengan aman) dan tidak khawatir tentang detailnya? Atau...
  2. Apakah Anda perlu menerapkan protokol tertentu?

Jika Anda memilih opsi pertama , dapatkan CipherSweet.js .

Dokumentasi tersedia online . EncryptedFieldcukup untuk sebagian besar kasus penggunaan, tetapi API EncryptedRowdan EncryptedMultiRowsmungkin lebih mudah jika Anda memiliki banyak bidang berbeda yang ingin Anda enkripsi.

Dengan CipherSweet, Anda bahkan tidak perlu tahu apa itu nonce / IV untuk menggunakannya dengan aman.

Selain itu, ini menangani int/ floatenkripsi tanpa membocorkan fakta tentang konten melalui ukuran ciphertext.

Jika tidak, Anda akan menginginkan sodium-plus , yang merupakan antarmuka yang mudah digunakan untuk berbagai pembungkus libsodium. Sodium-Plus memungkinkan Anda untuk menulis kode lintas platform yang performan, tidak sinkron, dan mudah untuk diaudit.

Untuk menginstal natrium-plus, jalankan saja ...

npm install sodium-plus

Saat ini tidak ada CDN publik untuk dukungan browser. Ini akan segera berubah. Namun, Anda dapat mengambil sodium-plus.min.jsdari rilis Github terbaru jika Anda membutuhkannya.

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());
})();

Dokumentasi untuk natrium-plus tersedia di Github.

Jika Anda ingin tutorial langkah demi langkah, artikel dev.to ini memiliki apa yang Anda cari.



5

Sebelum menerapkan semua ini, silakan lihat jawaban Scott Arciszewski .

Saya ingin Anda sangat berhati - hati dengan apa yang akan saya bagikan karena saya memiliki sedikit atau tidak ada pengetahuan keamanan (Ada kemungkinan besar saya menyalahgunakan API di bawah ini), jadi saya akan sangat menyambut untuk memperbarui jawaban ini dengan bantuan komunitas .

Seperti @richardtallent disebutkan dalam jawabannya , ada dukungan untuk Web Crypto API, jadi contoh ini menggunakan standar. Pada tulisan ini, ada 95,88% dukungan browser global .

Saya akan membagikan contoh menggunakan Web Crypto API

Sebelum kami melanjutkan, harap dicatat ( Mengutip dari MDN ):

API ini menyediakan sejumlah primitif kriptografi tingkat rendah. Sangat mudah untuk menyalahgunakan mereka , dan perangkap yang terlibat bisa sangat halus .

Bahkan dengan asumsi Anda menggunakan fungsi kriptografi dasar dengan benar, manajemen kunci yang aman dan desain sistem keamanan keseluruhan sangat sulit untuk diperbaiki dan pada umumnya merupakan domain para pakar keamanan spesialis.

Kesalahan dalam desain dan implementasi sistem keamanan dapat membuat keamanan sistem benar-benar tidak efektif.

Jika Anda tidak yakin tahu apa yang Anda lakukan, Anda mungkin tidak boleh menggunakan API ini .

Saya sangat menghormati keamanan, dan saya bahkan berani bagian tambahan dari MDN ... Anda telah diperingatkan

Sekarang, untuk contoh aktual ...


JSFiddle:

Ditemukan di sini: https://jsfiddle.net/superjose/rm4e0gqa/5/

catatan:

Perhatikan penggunaan awaitkata kunci. Gunakan di dalam asyncfungsi atau gunakan .then()dan .catch().

Hasilkan kunci:

// 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.

Enkripsi:

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

Dekripsi

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

Konversi ArrayBuffer bolak-balik dari String (Selesai dalam 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;
  }

Anda dapat menemukan lebih banyak contoh di sini (saya bukan pemiliknya): // https://github.com/diafygi/webcrypto-examples


2

CryptoJS tidak lagi didukung. Jika Anda ingin terus menggunakannya, Anda dapat beralih ke url ini:

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


Apa perbedaan antara rollup dan komponen di bawah folder 3.1.2?
Kanagavelu Sugumar

1
Crypto merekomendasikan perpustakaan tempa ketika Anda memasuki situs mereka.
Dror Bar

1

Gunakan SimpleCrypto

Menggunakan enkripsi () dan dekripsi ()

Untuk menggunakan SimpleCrypto, pertama-tama buat instance SimpleCrypto dengan kunci rahasia (kata sandi). Parameter kunci rahasia HARUS ditentukan saat membuat instance SimpleCrypto.

Untuk mengenkripsi dan mendekripsi data, cukup gunakan fungsi encrypt () dan decrypt () dari sebuah instance. Ini akan menggunakan algoritma enkripsi AES-CBC.

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 menggunakan AES-CBC yang tidak diautentikasi dan karenanya rentan terhadap serangan ciphertext yang dipilih.
Scott Arciszewski

-6

Fungsi sederhana,


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
Sementara cuplikan kode ini mungkin solusinya, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda.
Johan

1
Ini bukan algoritma yang aman (perhatikan bahwa Encrypt tidak mengambil parameter kunci) dan dapat dengan mudah dibalik direkayasa. OP meminta sesuatu yang memiliki keamanan di atasnya.
Mike S
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.