簡體   English   中英

JavaScript 字符串加密和解密?

[英]JavaScript string encryption and decryption?

我有興趣構建一個供個人使用的小型應用程序,該應用程序將使用 JavaScript 在客戶端加密和解密信息。 加密信息將存儲在服務器上的數據庫中,但絕不會是解密版本。

它不必是超級安全的,但我想使用當前未破壞的算法。

理想情況下,我可以做類似的事情

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

生成編碼字符串,以及類似的東西

var sensical = decrypt(gibberish, key);

稍后解碼。

到目前為止,我已經看到了這個: http : //bitwiseshiftleft.github.io/sjcl/

我應該看看其他圖書館嗎?

 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>

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

或使用其他 CDN,如https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js

我創建了一個不安全但簡單的文本密碼/解密工具。 不依賴任何外部庫。

這些是功能

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'

利用 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 可能沒問題。

在上述三個選項中,SJCL 最不可能以眼淚收場。 但是有更好的選擇。

為什么 Libsodium 更好?

您無需在密碼模式菜單、散列函數和其他不必要的選項之間進行選擇。 你永遠不會冒着搞砸你的參數和從你的協議中刪除所有安全性的風險

相反, libsodium只是為您提供針對最大安全性和簡約 API 進行調整的簡單選項。

  • crypto_box() / crypto_box_open()提供經過身份驗證的公鑰加密。
    • 有問題的算法結合了 X25519(ECDH over Curve25519)和 XSalsa20-Poly1305,但你不需要知道(甚至關心)它來安全地使用它
  • crypto_secretbox() / crypto_secretbox_open()提供共享密鑰認證加密。
    • 有問題的算法是 XSalsa20-Poly1305,但你不需要知道/關心

此外,libsodium 綁定了數十種流行的編程語言,因此當嘗試與另一個編程堆棧進行互操作時,libsodium 很可能會正常工作 此外,libsodium 在不犧牲安全性的情況下往往非常快。

如何在 JavaScript 中使用 Libsodium?

首先,你需要決定一件事:

  1. 您是否只想加密/解密數據(並且可能仍然以某種方式安全地使用數據庫查詢中的明文)而不擔心細節? 或者...
  2. 您需要實現特定的協議嗎?

如果您選擇了第一個選項,請獲取CipherSweet.js

該文檔可在線獲取 EncryptedField對於大多數用例來說就足夠了,但是如果您有很多不同的字段要加密,那么EncryptedRowEncryptedMultiRows API 可能會更容易。

使用 CipherSweet,您甚至不需要知道 nonce/IV 是什么就可以安全地使用它。

此外,這可以處理int / float加密,而不會通過密文大小泄漏有關內容的事實。

否則,您將需要鈉加它是各種 libsodium 包裝器的用戶友好前端。 Salt-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 上找到關於 salt -plus的文檔。

如果您需要分步教程, 這篇 dev.to 文章可以滿足您的需求。

現代瀏覽器現在支持crypto.subtle API,它使用以下方法之一提供本機加密和解密功能(異步不少!):AES-CBC、AES-CTR、AES-GCM 或 RSA-OAEP。

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

此代碼基於@Jorgeblom 上面的回答。


@Jorgeblom 我的男人,這是一個很棒的小型加密庫:DI 有點觸動它,因為我不喜歡我必須分配鹽並再次調用它,但總的來說,對於我的需求來說絕對是完美的。

const crypt = (salt, text) => {
  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
    .split("")
    .map(textToChars)
    .map(applySaltToChar)
    .map(byteHex)
    .join("");
};

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

你用它

// encrypting
const encrypted_text = crypt("salt", "Hello"); // -> 426f666665

// decrypting
const decrypted_string = decrypt("salt", "426f666665"); // -> Hello

在實施任何這些之前,請參閱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)}`)

從 String 來回轉換 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

你可以使用這些函數很容易第一個加密所以你只需調用函數並發送你想要加密的文本並從 encryptWithAES 函數中獲取結果並將其發送到解密函數,如下所示:

const CryptoJS = require("crypto-js");


   //The Function Below To Encrypt Text
   const encryptWithAES = (text) => {
      const passphrase = "My Secret Passphrase";
      return CryptoJS.AES.encrypt(text, passphrase).toString();
    };
    //The Function Below To Decrypt Text
    const decryptWithAES = (ciphertext) => {
      const passphrase = "My Secret Passphrase";
      const bytes = CryptoJS.AES.decrypt(ciphertext, passphrase);
      const originalText = bytes.toString(CryptoJS.enc.Utf8);
      return originalText;
    };

  let encryptText = encryptWithAES("YAZAN"); 
  //EncryptedText==>  //U2FsdGVkX19GgWeS66m0xxRUVxfpI60uVkWRedyU15I= 

  let decryptText = decryptWithAES(encryptText);
  //decryptText==>  //YAZAN 

不再支持 CryptoJS。 如果你想繼續使用它,你可以切換到這個網址:

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

crypt.subtle AES-GCM,自包含,經過測試:

async function aesGcmEncrypt(plaintext, password)

async function aesGcmDecrypt(ciphertext, password) 

https://gist.github.com/chrisveness/43bcda93af9f646d083fad678071b90a

使用簡單加密

使用 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.");

簡單的功能:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM