JavaScript字符串加密和解密

JavaScript string encryption and decryption?

本文关键字:解密 加密 字符串 JavaScript      更新时间:2023-09-26

我有兴趣构建一个小型个人应用程序,该应用程序将使用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

或使用其他类似CDNhttps://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:
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"))

此代码基于@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

利用SJCL、CryptoJS和/或WebCrypto的现有答案不一定是错误的,但它们并不像您最初怀疑的那样安全。一般来说,你想使用利钠。首先我会解释为什么,然后解释如何。

为什么不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只是为您提供了一些经过优化的简单选项,以实现最大的安全性和最小的API。

  • crypto_box()/crypto_box_open()提供经过身份验证的公钥加密。
    • 有问题的算法结合了X25519(Curve25519上的ECDH)和XSalsa20-Poly1305,但您不需要知道(甚至不需要关心)这一点就可以安全地使用它
  • crypto_secretbox()/crypto_secretbox_open()提供共享密钥认证加密。
    • 有问题的算法是XSalsa20-Poly1305,但您不需要知道/关心

此外,libsodium在数十种流行的编程语言中都有绑定,因此当试图与另一个编程堆栈进行互操作时,libsodeum很可能只工作。此外,在不牺牲安全性的情况下,libsodium往往速度非常快。

如何在JavaScript中使用Libsodium

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

  1. 你只是想加密/解密数据(也许仍然可以在数据库查询中安全地使用明文)而不担心细节吗?或者
  2. 您需要实施特定的协议吗

如果您选择了第一个选项,则获取CipherSweet.js.

该文档可在线获取。EncryptedField对于大多数用例来说已经足够了,但是如果有很多不同的字段要加密,那么EncryptedRowEncryptedMultiRowsAPI可能会更容易。

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

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

否则,您将需要钠加,这是各种libsodium包装的用户友好前端。Nadium 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文章有你想要的。

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

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

crypt.subtable AES-GCM,自包含,已测试:

async function aesGcmEncrypt(plaintext, password)
async function aesGcmDecrypt(ciphertext, password) 

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

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

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

正如@richardtallent在他的回答中提到的,有对WebCryptoneneneba API的支持,所以这个示例使用该标准。截至本文撰写之时,全球浏览器支持率为95.88%。

我将分享一个使用WebCryptoneneneba 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

你可以使用这些函数,它非常容易第一个用于加密的函数,所以你只需调用该函数并发送你想要加密的文本,然后从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>

2021年12月更新

使用MDN Web文档:提供的加密api

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


老答案

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