我对构建一个个人使用的小型应用程序感兴趣,该应用程序将使用JavaScript在客户端加密和解密信息。加密的信息将存储在服务器上的数据库中,但绝不会存储解密的版本。

它不必是超级duper安全,但我想使用目前未被破坏的算法。

理想情况下,我可以做一些

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

来生成编码的字符串,诸如此类

var sensical = decrypt(gibberish, key);

稍后再解码。

到目前为止,我看到的是: http://bitwiseshiftleft.github.io/sjcl/

我还应该看看其他的图书馆吗?


当前回答

不再支持CryptoJS。如果你想继续使用它,你可以切换到这个url:

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

其他回答

不再支持CryptoJS。如果你想继续使用它,你可以切换到这个url:

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

现代浏览器现在支持加密。微妙的API,它提供本地加密和解密功能(异步!)使用这些方法之一:AES-CBC, AES-CTR, AES-GCM,或RSA-OAEP。

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

这段代码是基于上面@Jorgeblom的回答。


@Jorgeblom我的朋友,这是一个很棒的小加密库:D 我有点碰它,因为我不喜欢我必须分配盐,然后再调用它但总的来说,对于我的需求是绝对完美的。

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

我创建了一个不安全但简单的文本密码/解密实用程序。不依赖任何外部库。

这些是函数:

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: console.log(myCipher('the secret string')) //To decipher, you need to create a decipher and use it: const myDecipher = decipher('mySecretSalt') console.log(myDecipher("7c606d287b6d6b7a6d7c287b7c7a61666f"))

在实现这些之前,请参阅Scott Arciszewski的回答。

我希望你对我将要分享的内容非常小心,因为我几乎没有安全知识(我很有可能误用了下面的API),所以我非常欢迎在社区的帮助下更新这个答案。

正如@richardtallent在他的回答中提到的,有对Web Crypto API的支持,因此本例使用了该标准。在撰写本文时,全球浏览器的支持率为95.88%。

我将分享一个使用Web Crypto API的示例

在我们继续之前,请注意(引用自MDN):

This API provides a number of low-level cryptographic primitives. It's very easy to misuse them, and the pitfalls involved can be very subtle. Even assuming you use the basic cryptographic functions correctly, secure key management and overall security system design are extremely hard to get right and are generally the domain of specialist security experts. Errors in security system design and implementation can make the security of the system completely ineffective. If you're not sure you know what you are doing, you probably shouldn't be using this API.

我非常尊重安全性,我甚至加粗了MDN的附加部分……我已经警告过你了 现在来看看实际的例子……


JSFiddle:

在这里找到:https://jsfiddle.net/superjose/rm4e0gqa/5/

注意:

注意await关键字的使用。在异步函数中使用它或使用.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