Шифрование и дешифрование строк JavaScript?

153

Я заинтересован в создании небольшого приложения для личного пользования, которое будет шифровать и дешифровать информацию на стороне клиента с помощью JavaScript. Зашифрованная информация будет храниться в базе данных на сервере, но никогда не будет расшифрована.

Это не должно быть супер-надёжно, но я бы хотел использовать в настоящее время непрерывный алгоритм.

В идеале я бы мог сделать что-то вроде

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

генерировать закодированную строку и что-то вроде

var sensical = decrypt(gibberish, key);

расшифровать его позже.

До сих пор я видел это: http://bitwiseshiftleft.github.io/sjcl/

Какие-нибудь другие библиотеки мне нужно посмотреть?

Иеремии
источник
2
Взгляните на шифрование Javascript AES
kevinji
2
jsfiddle.net/kein1945/M9K2c
ХИРА ТАКУР
10
Некоторая терминология здесь отключена. Вот простая версия 1. Соли добавляются к хэшируемой информации (обычно паролям). Их цель - сделать хеш отличным от того, что было бы без соли. Это полезно, потому что это делает предварительно сгенерированные хэши, если ваша база данных взломана и хешированные пароли пользователей получаются. 2. Хеширование - это односторонняя операция, которая переводит ввод в вывод. Это не может быть легко полностью изменено или отменено. 3. Кодирование не является шифрованием. base64_encode, UrlEncode и т.д.
дез

Ответы:

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
Зашифрованный на самом деле является объектом, но вы можете вызвать encrypted.toString (), чтобы получить строку. Вы сможете расшифровать эту строку позже: jsbin.com/kofiqokoku/1
Томас Кирда
9
Но как мы можем получить секретную фразу-пароль?
дуйхоа
9
Кажется, Crypto JS является архивным проектом. На github есть клон: github.com/sytelus/CryptoJS, но он не обновлялся в течение двух лет. Это все еще лучший вариант для шифрования JS?
syonip
2
Я хотел бы пойти с этим: github.com/brix/crypto-js, он также доступен через NPM
Томас Кирда
1
@stom это зависит от вас, как и где вы храните его. Я не знаю, есть ли действительно безопасный способ сохранить его в браузере. Запросите их с сервера и сохраните в памяти.
Томас
62

Как насчет CryptoJS ?

Это надежная крипто библиотека, с большим количеством функций. Он реализует хэши, HMAC, PBKDF2 и шифры. В этом случае шифры - это то, что вам нужно. Проверьте быстрый старт на домашней странице проекта.

Вы могли бы сделать что-то вроде с 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>

Что касается безопасности, на момент написания моей статьи алгоритм AES считается не нарушенным

Редактировать :

Кажется, что онлайн-адрес не работает, и вы можете использовать загруженные файлы для шифрования по указанной ниже ссылке и поместить соответствующие файлы в корневую папку приложения.

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

или использовал другой CDN, например https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js

ovidb
источник
В чем разница между накопительными пакетами и компонентами в папке 3.1.2?
Канагавелу Сугамар
После игры немного компонентов являются отдельными частями. Вам нужно будет знать, какие компоненты взять (и в каком порядке), чтобы он работал. В накопительных файлах содержится все, что вам нужно для того, чтобы он работал только с одной ссылкой на скрипт (гораздо лучше, поскольку тяжелая работа уже выполнена).
Шахар Эльдад
2
Но как мы можем получить секретную фразу-пароль?
Shaijut
@ Shajut Вы не делаете. Вы даже не сохраняете его нигде, кроме как в ОЗУ при шифровании / дешифровании открытого текста. Пароль должен храниться только в мозгу пользователя (или в менеджере паролей)
slebetman
39

Я создал небезопасную, но простую утилиту для шифрования и дешифрования текста. Нет зависимостей с какой-либо внешней библиотекой.

Это функции

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

И вы можете использовать их следующим образом:

// 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'
Jorgeblom
источник
4
let myDecipher = decipher ('CartelSystem') - Эта соль также расшифрует строку. Вам не нужно знать точное слово «mySecretSalt»
Dror Bar
Кроме того, сольчары в расшифровке не используются?
Dror Bar
1
Еще один пост, где кто-то слепо пользуется let. John
Джон
1
Разве это не а) сверхразрушенный и небезопасный и б) «соль» на самом деле ваш «секретный ключ», поскольку соли не должны быть частными? Я думаю, очень опасно размещать подобный код без каких-либо комментариев, что этот забавный код не предназначен для использования в реальном мире. Количество голосов вызывает беспокойство. crypto.stackexchange.com/questions/11466/...
lschmierer
1
Ну, по крайней мере, они используют звуковую криптографию. То , что вы делаете в основном Цезарь Chipher (применяя тот же ключ к каждому символу) en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher Что касается других ответов ... Я ожидаю , что это будет очевидно , что что - то называется «секрет» является ожидается, что будет храниться в секрете (пользователь)
lschmierer
19

Существующие ответы, в которых используются SJCL, CryptoJS и / или WebCrypto, не обязательно ошибочны, но они не так безопасны, как вы могли изначально подозревать. Как правило, вы хотите использовать libsodium . Сначала я объясню почему, а затем как.

Почему не SJCL, CryptoJS, WebCrypto и т. Д.?

Краткий ответ: для того, чтобы ваше шифрование было действительно безопасным, эти библиотеки ожидают, что вы сделаете слишком много вариантов, например, режим блочного шифра (CBC, CTR, GCM; если вы не можете сказать, какой из трех перечисленных мною безопасен для использовать и при каких ограничениях вы не должны быть обременены таким выбором вообще ).

Если ваша должность не является инженером-криптографистом , вероятность того, что она будет надежно реализована, зависит от вас.

Зачем избегать CryptoJS?

CryptoJS предлагает несколько строительных блоков и ожидает, что вы знаете, как их безопасно использовать. Это даже по умолчанию в режиме CBCархиве ).

Почему режим CBC плох?

Прочтите эту статью об уязвимостях AES-CBC .

Зачем избегать WebCrypto?

WebCrypto - это стандарт безопасности, разработанный комитетом для целей, которые ортогональны разработке криптографии. В частности, WebCrypto должен был заменить Flash, а не обеспечивать безопасность .

Зачем избегать SJCL?

Публичный API и документация SJCL просят пользователей зашифровать данные с помощью запоминаемого человеком пароля. Это редко, если вообще, то, что вы хотите делать в реальном мире.

Дополнительно: стандартное количество раундов PBKDF2 по умолчанию примерно в 86 раз меньше, чем вы хотите . AES-128-CCM, вероятно, в порядке.

Из трех приведенных выше вариантов SJCL наименее вероятно закончится слезами. Но есть и лучшие варианты.

Почему Libsodium лучше?

Вам не нужно выбирать между меню режимов шифрования, хэш-функциями и другими ненужными опциями. Вы никогда не рискуете испортить ваши параметры и удалить всю безопасность из вашего протокола .

Вместо этого libsodium предлагает простые настройки, настроенные на максимальную безопасность и минималистичные API.

  • crypto_box()/ crypto_box_open()предложить аутентифицированное шифрование с открытым ключом.
    • Рассматриваемый алгоритм объединяет X25519 (ECDH по Curve25519) и XSalsa20-Poly1305, но вам не нужно знать (или даже заботиться) об этом, чтобы использовать его безопасно
  • crypto_secretbox()/ crypto_secretbox_open()предложить шифрование с аутентификацией с общим ключом.
    • Рассматриваемый алгоритм XSalsa20-Poly1305, но вам не нужно знать / заботиться

Кроме того, у libsodium есть привязки на десятках популярных языков программирования , поэтому очень вероятно, что libsodium просто сработает при попытке взаимодействия с другим стеком программирования. Кроме того, libsodium имеет тенденцию быть очень быстрым, не жертвуя безопасностью.

Как использовать Libsodium в JavaScript?

Во-первых, вам нужно решить одну вещь:

  1. Вы просто хотите зашифровать / расшифровать данные (и, возможно, все-таки как-то безопасно использовать открытый текст в запросах к базе данных) и не беспокоиться о деталях? Или...
  2. Вам нужно реализовать определенный протокол?

Если вы выбрали первый вариант , получите CipherSweet.js .

Документация доступна онлайн . EncryptedFieldэтого достаточно для большинства случаев использования, но API EncryptedRowи EncryptedMultiRowsAPI могут быть проще, если у вас есть много различных полей, которые вы хотите зашифровать.

С CipherSweet вам даже не нужно знать, что такое nonce / IV, чтобы безопасно его использовать.

Кроме того, это обрабатывает int/ floatшифрует без утечки фактов о содержимом через размер зашифрованного текста.

В противном случае вы захотите натрия-плюс , который является удобным интерфейсом для различных оболочек libsodium. Sodium-Plus позволяет писать производительный, асинхронный, кроссплатформенный код, который легко проверять и анализировать.

Чтобы установить натрий-плюс, просто запустите ...

npm install sodium-plus

В настоящее время нет общедоступной CDN для поддержки браузера. Это скоро изменится. Тем не менее, вы можете получить sodium-plus.min.jsиз последней версии Github , если вам это нужно.

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

Документация для натрия-плюс доступна на Github.

Если вы хотите пошаговое руководство, эта статья содержит то, что вы ищете.

Скотт Аркишевский
источник
16

Современные браузеры теперь поддерживают crypto.subtleAPI, который предоставляет собственные функции шифрования и дешифрования (не менее асинхронно!) С использованием одного из следующих методов: AES-CBC, AES-CTR, AES-GCM или RSA-OAEP.

https://www.w3.org/TR/WebCryptoAPI/#dfn-Crypto

richardtallent
источник
3
Из вышеперечисленных вариантов приемлемы только AES-GCM и RSA-OAEP. :(
Скотт
5

Прежде чем реализовать что-либо из этого, ознакомьтесь с ответом Скотта Аркишевского .

Я хочу, чтобы вы были очень осторожны с тем, чем я собираюсь поделиться, поскольку у меня мало или совсем нет знаний по безопасности (есть большая вероятность, что я неправильно использую API ниже), поэтому я буду более чем рад обновить этот ответ с помощью сообщества .

Как упомянул в своем ответе @richardtallent , есть поддержка Web Crypto API, поэтому в этом примере используется стандарт. На момент написания этой статьи 95,88% глобальной поддержки браузеров .

Я собираюсь поделиться примером с использованием Web Crypto API

Прежде чем мы продолжим, пожалуйста, обратите внимание ( цитата из MDN ):

Этот API предоставляет ряд низкоуровневых криптографических примитивов. Это очень легко злоупотребить их , и подводные камни , связанные могут быть очень тонкими .

Даже при условии правильного использования основных криптографических функций управление безопасными ключами и общий дизайн системы безопасности крайне сложно понять, и, как правило, это область специалиста по безопасности.

Ошибки в разработке и реализации системы безопасности могут сделать безопасность системы совершенно неэффективной.

Если вы не уверены, что знаете, что делаете, вам, вероятно, не стоит использовать этот API .

Я очень уважаю безопасность, и я даже выделил дополнительные детали из MDN ... Вы были предупреждены

сейчас, к фактическому примеру ...


JSFiddle:

Найдено здесь: https://jsfiddle.net/superjose/rm4e0gqa/5/

Примечание:

Обратите внимание на использование awaitключевых слов. Используйте его внутри asyncфункции или используйте .then()и .catch().

Сгенерируйте ключ:

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

Шифрование:

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

Расшифровать

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

Преобразование ArrayBuffer туда и обратно из строки (сделано в 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;
  }

Вы можете найти больше примеров здесь (я не владелец): // https://github.com/diafygi/webcrypto-examples

Хосе А
источник
2

CryptoJS больше не поддерживается. Если вы хотите продолжить использовать его, вы можете переключиться на этот URL:

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

Дмитрий Лопушанский
источник
В чем разница между накопительными пакетами и компонентами в папке 3.1.2?
Канагавелу Сугамар
1
Crypto рекомендует библиотеку Forge при входе на их сайт.
Бар Dror
1

Используйте SimpleCrypto

Использование encrypt () и decrypt ()

Чтобы использовать SimpleCrypto, сначала создайте экземпляр SimpleCrypto с секретным ключом (паролем). Параметр секретного ключа ДОЛЖЕН быть определен при создании экземпляра SimpleCrypto.

Чтобы зашифровать и расшифровать данные, просто используйте функции encrypt () и decrypt () из экземпляра. Это будет использовать алгоритм шифрования 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 использует AES-CBC без проверки подлинности и поэтому уязвим для атак с использованием зашифрованного текста.
Скотт
-6

Простые функции,


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
Хотя этот фрагмент кода может быть решением, включение пояснения действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код.
Йохан,
1
Это небезопасный алгоритм (обратите внимание, что Encrypt не принимает ключевой параметр) и может быть легко изменен. ОП попросил что-то, что было защищено.
Майк С