230

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

它不必是超级安全的,但我想使用当前完整的算法。

理想情况下,我可以做类似的事情

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

生成编码的字符串,以及类似的东西

var sensical = decrypt(gibberish, key);

稍后解码。

到目前为止,我已经看到了这个: http ://bitwiseshiftleft.github.io/sjcl/

我应该看看其他任何图书馆吗?

4

12 回答 12

214

 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>

于 2014-10-11T17:46:45.050 回答
90

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

于 2014-05-14T12:48:55.633 回答
79

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

这些是功能:

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

于 2019-01-03T16:46:31.320 回答
31

此代码基于上面@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
于 2021-04-04T07:28:02.667 回答
30

利用 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对于大多数用例来说已经足够了,但是如果您有很多不同的字段要加密,则EncryptedRow和API 可能会更容易。EncryptedMultiRows

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

此外,这处理int/float加密不会通过密文大小泄露有关内容的事实。

否则,您将需要sodium-plus它是各种 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 上找到。

如果你想要一个分步教程,这篇 dev.to 文章有你正在寻找的东西。

于 2019-10-13T03:59:41.220 回答
27

现代浏览器现在支持crypto.subtleAPI,它使用以下方法之一提供本机加密和解密功能(异步不少!):AES-CBC、AES-CTR、AES-GCM 或 RSA-OAEP。

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

于 2018-09-10T21:12:20.233 回答
8

在实施任何这些之前,请参阅Scott Arciszewski 的回答

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

正如@richardtallent 在他的回答中提到的那样,支持 Web Crypto API,所以这个例子使用了标准。在撰写本文时,全球浏览器支持率为 95.88%

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

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

此 API 提供了许多低级加密原语。很容易误用它们,所涉及的陷阱可能非常微妙

即使假设您正确使用了基本的加密功能,安全密钥管理和整体安全系统设计也很难做到正确,并且通常是专业安全专家的领域。

安全系统设计和实施中的错误会使系统的安全性完全失效。

如果你不确定你知道你在做什么,你可能不应该使用这个 API

我非常尊重安全性,我什至加粗了 MDN 的其他部分......你已经被警告

现在,到实际的例子......


JSF中:

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

笔记:

注意await关键字的使用。async在函数中使用它或使用.then()and .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

于 2019-12-05T12:01:17.207 回答
7

您可以使用这些函数,第一个用于加密非常简单,因此您只需调用该函数并发送要加密的文本,然后从 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 
于 2020-12-28T22:15:52.287 回答
6

crypt.subtle AES-GCM,独立的,经过测试:

async function aesGcmEncrypt(plaintext, password)

async function aesGcmDecrypt(ciphertext, password) 

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

于 2021-01-03T09:08:52.497 回答
4

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

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

于 2018-05-04T19:26:02.157 回答
2

2021 年 12 月更新

使用MDN Web Docs提供的 crypto api :

https://developer.mozilla.org/en-US/docs/Web/API/Crypto


旧答案

使用SimpleCrypto

使用加密()和解密()

要使用 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.");
于 2019-05-13T11:16:02.603 回答
-6

Simple functions:

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;
} 
于 2019-06-05T10:29:30.927 回答