JavaScript文字列の暗号化と復号化?


152

JavaScriptを使用してクライアント側で情報を暗号化および復号化する個人用の小さなアプリを作成することに興味があります。暗号化された情報はサーバー上のデータベースに保存されますが、復号化されたバージョンは決して保存されません。

非常に安全である必要はありませんが、現在壊れていないアルゴリズムを使用したいと思います。

理想的には私は次のようなことができるだろう

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

エンコードされた文字列を生成する、など

var sensical = decrypt(gibberish, key);

後でデコードします。

これまでのところ、これを見てきました:http : //bitwiseshiftleft.github.io/sjcl/

私が見るべき他のライブラリはありますか?


2
見てみましょうJavascriptのAES暗号化を
kevinji


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は実際にはオブジェクトですが、encrypted.toString()を呼び出して文字列を取得できます。後でその文字列を復号化できるようになります:jsbin.com/kofiqokoku/1
Tomas

9
しかし、どうすれば秘密のパスフレーズを保護できますか?
duykhoa 2015年

9
crypto jsはアーカイブされたプロジェクトのようです。github:github.com/sytelus/CryptoJSにクローンがありますが、2年間更新されていません。これはまだjs暗号化に最適なオプションですか?
syonip 2015

2
私はこれを使います:github.com/brix/crypto-jsこれはNPMからも利用できます
Tomas Kirda

1
@stomそれをどこにどのように保存するかはあなた次第です。それをブラウザに保存する本当に安全な方法があるかどうかわかりません。それらをサーバーに要求し、メモリに保存します。
Tomas Kirda

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アルゴリズムは壊れていないと考えられています

編集:

オンラインURLがダウンしているようです。ダウンロードしたファイルを暗号化に使用して、以下のリンクからアクセスし、それぞれのファイルをアプリケーションのルートフォルダーに配置できます。

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

またはhttps://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.jsのような他のCDNを使用


フォルダー3.1.2のロールアップとコンポーネントの違いは何ですか?
カナガベルスグマール2018

少し遊んだ後、コンポーネントは分離された部分になります。機能させるためには、どのコンポーネントをどのような順序で使用するかを知る必要があります。ロールアップファイルには、1つのスクリプト参照で機能させるために必要なすべてのものが含まれています(ハードジョブが既に完了しているため、はるかに優れています)。
shahar eldad

2
しかし、どうすれば秘密のパスフレーズを保護できますか?
シャイユット

@shaijutあなたはしません。平文を暗号化/復号化するときは、RAM以外の場所には保存しません。パスフレーズはユーザーの頭脳(またはパスワードマネージャー)にのみ保存する必要があります
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'

4
let myDecipher = decipher( 'CartelSystem')-このソルトは文字列も解読します。「mySecretSalt」という正確な単語を知っている必要はありません
Dror Bar、

また、解読されたsaltCharsは使用されていませんか?
Dror Bar、

1
誰かが盲目的に使用しているさらに別の投稿let。😒︎
ジョン

1
これは、a)非常に壊れていて安全ではない、b)「塩」は実際には「秘密鍵」ではないのですか?この楽しいコードが実際の使用を意図していないというコメントなしでこのようなコードを投稿することは非常に危険だと思います。賛成票の量が気になります。crypto.stackexchange.com/questions/11466/...
lschmierer

1
まあ、少なくとも彼らは健全な暗号を使用しています。あなたがしていることは、基本的にはシーザーチファーです(すべてのキャラクターに同じキーを適用します)en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher他の回答について...「秘密」と呼ばれるものは(ユーザーによって)秘密にされることが期待される
lschmierer

18

SJCL、CryptoJS、および/またはWebCryptoを利用する既存の回答は必ずしも間違っているわけではありませんが、最初に疑うほど安全ではありません。通常は、libsodium使用します。最初に理由を説明し、次にその方法を説明します。

なぜSJCL、CryptoJS、WebCryptoなどではないのですか?

短い答え:暗号化を実際に安全にするために、これらのライブラリでは、ブロック暗号モード(CBC、CTR、GCMなど)の選択が多すぎることが予想されます。使用し、どのような制約の下でも、この種の選択にまったく負担をかけるべきではありません)。

役職が暗号技術者でない限り、それを安全に実装する可能性は低くなります。

CryptoJSを回避する理由

CryptoJSはいくつかのビルディングブロックを提供し、それらを安全に使用する方法を知っていることを期待しています。デフォルトではCBCモードアーカイブ済み)です。

CBCモードが悪いのはなぜですか?

AES-CBCの脆弱性に関するこの記事を読んでください。

WebCryptoを回避する理由

WebCryptoは、暗号工学に直交する目的のために委員会によって設計されたポットラック標準です。特に、WebCryptoはFlashに代わるものであり、セキュリティを提供するものではありませんでした

SJCLを回避する理由

SJCLのパブリックAPIとドキュメントは、ユーザーに記憶されたパスワードでデータを暗号化するようにユーザーに要求します。これが現実の世界でしたいことは、たとえあったとしてもめったにありません。

さらに、そのデフォルトのPBKDF2ラウンドカウントは、希望する値の約86倍です。AES-128-CCMはおそらく問題ありません。

上記の3つのオプションのうち、SJCLが涙で終わる可能性が最も低いです。しかし、より良いオプションがあります。

なぜLibsodiumの方が優れているのですか?

暗号モード、ハッシュ関数、およびその他の不要なオプションのメニューから選択する必要はありません。パラメータをめちゃくちゃにして、プロトコルからすべてのセキュリティを削除するリスクは決してありません。

代わりに、libsodiumは、最大のセキュリティと最小限のAPIのために調整されたシンプルなオプションを提供します。

  • crypto_box()/ crypto_box_open()認証された公開鍵暗号化を提供します。
    • 問題のアルゴリズムはX25519(Curve25519上のECDH)とXSalsa20-Poly1305を組み合わせていますが、安全に使用するためにそれを知る必要はありません(または気にする必要もありません)。
  • crypto_secretbox()/ crypto_secretbox_open()共有キー認証暗号化を提供します。
    • 問題のアルゴリズムはXSalsa20-Poly1305ですが、知る必要はありません。

さらに、libsodiumは持っている一般的なプログラミング言語の数十でバインディングをそれがlibsodiumは可能性が非常に高いですので、ちょうど仕事他のプログラミングスタックとの相互運用しようとしたとき。また、libsodiumは、セキュリティを犠牲にすることなく非常に高速になる傾向があります。

JavaScriptでLibsodiumを使用する方法

最初に、1つのことを決定する必要があります。

  1. データを暗号化/復号化したいだけで(データベースクエリでプレーンテキストを安全に使用している可能性があります)、詳細について心配しませんか?または...
  2. 特定のプロトコルを実装する必要がありますか?

最初のオプションを選択した場合はCipherSweet.jsを取得します

ドキュメントはオンライン入手できますEncryptedFieldほとんどのユースケースでは十分ですが、暗号化する個別のフィールドが多数ある場合、EncryptedRowおよびEncryptedMultiRowsAPIの方が簡単な場合があります。

CipherSweet を使用すると、安全に使用するためにnonce / IVが何であるかを知る必要さえありません。

さらに、これは暗号文のサイズを通じてコン​​テンツに関する事実を漏らすことなくint/ float暗号化を処理します。

それ以外の場合は、さまざまなlibsodiumラッパーのユーザーフレンドリーなフロントエンドあるナトリウムプラスが必要になります。Sodium-Plusを使用すると、監査と推論が容易な、パフォーマンスの高い非同期のクロスプラットフォームコードを記述できます。

ナトリウムプラスをインストールするには、単に実行します...

npm install sodium-plus

現在、ブラウザーをサポートするためのパブリックCDNはありません。これはすぐに変更されます。ただし、必要に応じて最新のGithubリリースsodium-plus.min.jsから取得できます。

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から入手できます。

ステップバイステップのチュートリアルが必要な場合は、このdev.to記事に探しているものが含まれています。



4

これを実装する前に、Scott Arciszewskiの回答を参照してください。

私はセキュリティの知識をほとんどまたはまったく持っていないので、これから共有する内容に細心の注意払ってください(以下の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のロールアップとコンポーネントの違いは何ですか?
カナガベルスグマール2018

1
Cryptoは、サイトに入るときにフォージライブラリを推奨しています。
Dror Bar、

1

SimpleCryptoを使用する

encrypt()およびdecode()の使用

SimpleCryptoを使用するには、まず秘密鍵(パスワード)を使用してSimpleCryptoインスタンスを作成します。SimpleCryptoインスタンスを作成するときに、秘密鍵パラメーターを定義する必要があります。

データを暗号化および復号化するには、インスタンスから暗号化()および復号化()関数を使用するだけです。これは、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を使用するため、選択された暗号文攻撃に対して脆弱です。
Scott Arciszewski、

-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
このコードスニペットが解決策となる可能性がありますが、説明を含めると、投稿の品質向上に役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。
Johan

1
これは安全なアルゴリズムではなく(Encryptがキーパラメーターをとらないことに注意)、簡単にリバースエンジニアリングすることができます。OPは、セキュリティが確保されているものを要求しました。
Mike S
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.