简体   繁体   English

JavaScript 字符串加密和解密?

[英]JavaScript string encryption and decryption?

I'm interested in building a small app for personal use that will encrypt and decrypt information on the client side using JavaScript.我有兴趣构建一个供个人使用的小型应用程序,该应用程序将使用 JavaScript 在客户端加密和解密信息。 The encrypted information will be stored in a database on a server, but never the decrypted version.加密信息将存储在服务器上的数据库中,但绝不会是解密版本。

It doesn't have to be super duper secure, but I would like to use a currently unbroken algorithm.它不必是超级安全的,但我想使用当前未破坏的算法。

Ideally I'd be able to do something like理想情况下,我可以做类似的事情

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

to generate the encoded string, and something like生成编码字符串,以及类似的东西

var sensical = decrypt(gibberish, key);

to decode it later.稍后解码。

So far I've seen this: http://bitwiseshiftleft.github.io/sjcl/到目前为止,我已经看到了这个: http : //bitwiseshiftleft.github.io/sjcl/

Any other libraries I should look at?我应该看看其他图书馆吗?

 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>

How about CryptoJS ? CryptoJS怎么

It's a solid crypto library, with a lot of functionality.这是一个可靠的加密库,具有很多功能。 It implements hashers, HMAC, PBKDF2 and ciphers.它实现了哈希器、HMAC、PBKDF2 和密码。 In this case ciphers is what you need.在这种情况下,密码就是您所需要的。 Check out the quick-start quide on the project's homepage.查看项目主页上的快速入门指南。

You could do something like with the AES:你可以用 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>

As for security, at the moment of my writing AES algorithm is thought to be unbroken至于安全性,在我编写 AES 算法的那一刻被认为是完整的

Edit :编辑 :

Seems online URL is down & you can use the downloaded files for encryption from below given link & place the respective files in your root folder of the application.似乎在线 URL 已关闭,您可以使用从下面给定链接下载的文件进行加密,并将相应文件放在应用程序的根文件夹中。

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

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

I created an insecure but simple text cipher/decipher util.我创建了一个不安全但简单的文本密码/解密工具。 No dependencies with any external library.不依赖任何外部库。

These are the functions这些是功能

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('');
}

And you can use them as follows:您可以按如下方式使用它们:

// 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'

The existing answers which leverage SJCL, CryptoJS, and/or WebCrypto aren't necessarily wrong but they're not as safe as you might initially suspect.利用 SJCL、CryptoJS 和/或 WebCrypto 的现有答案不一定是错误的,但它们并不像您最初怀疑的那样安全。 Generally you want to use libsodium .通常你想使用 libsodium First I'll explain why, then how.首先我会解释为什么,然后是如何。

Why Not SJCL, CryptoJS, WebCrypto, etc.?为什么不是 SJCL、CryptoJS、WebCrypto 等?

Short answer: In order for your encryption to actually be secure, these libraries expect you to make too many choices eg the block cipher mode (CBC, CTR, GCM; if you can't tell which of the three I just listed is secure to use and under what constraints, you shouldn't be burdened with this sort of choice at all ).简短回答:为了让您的加密真正安全,这些库希望您做出太多选择,例如分组密码模式(CBC、CTR、GCM;如果您无法分辨我刚刚列出的三个中的哪一个是安全的使用和在什么样的约束,你不应该有这种选择的所有负担)。

Unless your job title is cryptography engineer , the odds are stacked against you implementing it securely.除非您的职位是密码学工程师,否则您安全地实施它的可能性很大。

Why to Avoid CryptoJS?为什么要避免 CryptoJS?

CryptoJS offers a handful of building blocks and expects you to know how to use them securely. CryptoJS 提供了一些构建块,并希望您知道如何安全地使用它们。 It even defaults to CBC mode ( archived ).它甚至默认为 CBC 模式已存档)。

Why is CBC mode bad?为什么 CBC 模式不好?

Read this write-up on AES-CBC vulnerabilities .阅读这篇关于 AES-CBC 漏洞的文章

Why to Avoid WebCrypto?为什么要避免 WebCrypto?

WebCrypto is a potluck standard, designed by committee, for purposes that are orthogonal to cryptography engineering. WebCrypto 是一个由委员会设计的便餐标准,用于与密码工程正交的目的。 Specifically, WebCrypto was meant to replace Flash, not provide security .具体来说, WebCrypto 旨在取代 Flash,而不是提供安全性

Why to Avoid SJCL?为什么要避免 SJCL?

SJCL's public API and documentation begs users to encrypt data with a human-remembered password. SJCL 的公共 API 和文档要求用户使用人类记住的密码加密数据。 This is rarely, if ever, what you want to do in the real world.在现实世界中,这很少(如果有的话)您想要做的事情。

Additionally: Its default PBKDF2 round count is roughly 86 times as small as you want it to be .另外:它的默认 PBKDF2 轮数大约是您希望的 86 倍 AES-128-CCM is probably fine. AES-128-CCM 可能没问题。

Out of the three options above, SJCL is the least likely to end in tears.在上述三个选项中,SJCL 最不可能以眼泪收场。 But there are better options available.但是有更好的选择。

Why is Libsodium Better?为什么 Libsodium 更好?

You don't need to choose between a menu of cipher modes, hash functions, and other needless options.您无需在密码模式菜单、散列函数和其他不必要的选项之间进行选择。 You'll never risk screwing up your parameters and removing all security from your protocol .你永远不会冒着搞砸你的参数和从你的协议中删除所有安全性的风险

Instead, libsodium just gives you simple options tuned for maximum security and minimalistic APIs.相反, libsodium只是为您提供针对最大安全性和简约 API 进行调整的简单选项。

  • crypto_box() / crypto_box_open() offer authenticated public-key encryption. crypto_box() / crypto_box_open()提供经过身份验证的公钥加密。
    • The algorithm in question combines X25519 (ECDH over Curve25519) and XSalsa20-Poly1305, but you don't need to know (or even care) about that to use it securely有问题的算法结合了 X25519(ECDH over Curve25519)和 XSalsa20-Poly1305,但你不需要知道(甚至关心)它来安全地使用它
  • crypto_secretbox() / crypto_secretbox_open() offer shared-key authenticated encryption. crypto_secretbox() / crypto_secretbox_open()提供共享密钥认证加密。
    • The algorithm in question is XSalsa20-Poly1305, but you don't need to know/care有问题的算法是 XSalsa20-Poly1305,但你不需要知道/关心

Additionally, libsodium has bindings in dozens of popular programming languages , so it's very likely that libsodium will just work when trying to interoperate with another programming stack.此外,libsodium 绑定了数十种流行的编程语言,因此当尝试与另一个编程堆栈进行互操作时,libsodium 很可能会正常工作 Also, libsodium tends to be very fast without sacrificing security.此外,libsodium 在不牺牲安全性的情况下往往非常快。

How to Use Libsodium in JavaScript?如何在 JavaScript 中使用 Libsodium?

First, you need to decide one thing:首先,你需要决定一件事:

  1. Do you just want to encrypt/decrypt data (and maybe still somehow use the plaintext in database queries securely) and not worry about the details?您是否只想加密/解密数据(并且可能仍然以某种方式安全地使用数据库查询中的明文)而不担心细节? Or...或者...
  2. Do you need to implement a specific protocol?您需要实现特定的协议吗?

If you selected the first option , get CipherSweet.js .如果您选择了第一个选项,请获取CipherSweet.js

The documentation is available online .该文档可在线获取 EncryptedField is sufficient for most use cases, but the EncryptedRow and EncryptedMultiRows APIs may be easier if you have a lot of distinct fields you want to encrypt. EncryptedField对于大多数用例来说就足够了,但是如果您有很多不同的字段要加密,那么EncryptedRowEncryptedMultiRows API 可能会更容易。

With CipherSweet, you don't need to even know what a nonce/IV is to use it securely.使用 CipherSweet,您甚至不需要知道 nonce/IV 是什么就可以安全地使用它。

Additionally, this handles int / float encryption without leaking facts about the contents through ciphertext size.此外,这可以处理int / float加密,而不会通过密文大小泄漏有关内容的事实。

Otherwise, you'll want sodium-plus , which is a user-friendly frontend to various libsodium wrappers.否则,您将需要钠加它是各种 libsodium 包装器的用户友好前端。 Sodium-Plus allows you to write performant, asynchronous, cross-platform code that's easy to audit and reason about. Salt-Plus 允许您编写易于审计和推理的高性能、异步、跨平台代码。

To install sodium-plus, simply run...要安装钠加,只需运行...

npm install sodium-plus

There is currently no public CDN for browser support.目前没有用于浏览器支持的公共 CDN。 This will change soon.这将很快改变。 However, you can grab sodium-plus.min.js from the latest Github release if you need it.但是,如果需要,您可以从最新的 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()); })();

The documentation for sodium-plus is available on Github.可以在 Github 上找到关于 salt -plus的文档。

If you'd like a step-by-step tutorial, this dev.to article has what you're looking for.如果您需要分步教程, 这篇 dev.to 文章可以满足您的需求。

Modern browsers now support the crypto.subtle API, which provides native encryption and decryption functions (async no less!) using one of these method: AES-CBC, AES-CTR, AES-GCM, or RSA-OAEP.现代浏览器现在支持crypto.subtle API,它使用以下方法之一提供本机加密和解密功能(异步不少!):AES-CBC、AES-CTR、AES-GCM 或 RSA-OAEP。

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

This code is based on @Jorgeblom's answer above.此代码基于@Jorgeblom 上面的回答。


@Jorgeblom my man, that's fantastic small crypto lib :DI touch it a bit because I didn't like that I have to assign the salt and to call it again but in general, for my needs is absolutely perfect. @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("");
};

And you use it你用它

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

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

Before implementying any of this, please see Scott Arciszewski's answer .在实施任何这些之前,请参阅Scott Arciszewski 的回答

I want you to be very careful with what I'm about to share as I have little to no security knowledge (There's a high chance that I'm misusing the API below), so I'd be more than welcome to update this answer with the help of the community .我希望你对我将要分享的内容非常小心,因为我几乎没有安全知识(我很可能滥用下面的 API),所以我非常欢迎更新这个答案在社区的帮助下

As @richardtallent mentioned in his answer , there's support for the Web Crypto API, so this example uses the standard.正如@richardtallent 在他的回答中提到的,支持 Web Crypto API,因此本示例使用标准。 As of this writing, there's a 95.88% of global browser support .在撰写本文时,全球浏览器支持 95.88%

I'm going to be sharing an example using the Web Crypto API我将分享一个使用 Web Crypto API 的示例

Before we proceed, please note ( Quoting from MDN ):在我们继续之前,请注意( 引自 MDN ):

This API provides a number of low-level cryptographic primitives.此 API 提供了许多低级加密原语。 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 .如果您不确定自己知道自己在做什么,那么您可能不应该使用这个 API

I respect security a lot, and I even bolded additional parts from MDN... You've been warned我非常尊重安全性,我什至在 MDN 中加粗了其他部分......你已经被警告过

Now, to the actual example...现在,以实际示例为例...


JSFiddle: JSFiddle:

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

Note:笔记:

Note the use of await keywords.注意await关键字的使用。 Use it inside an async function or use .then() and .catch() .使用它的内部async功能或使用.then().catch()

Generate the key:生成密钥:

// 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.

Encrypt:加密:

    const encryptedUri = await crypto.subtle.encrypt({
      name: 'RSA-OAEP'
    }, key.publicKey, stringToArrayBuffer(stringToEncrypt))
    
    console.log('The encrypted string is', encryptedUri);


Decrypt解密

   const msg = await  crypto.subtle.decrypt({
      name: 'RSA-OAEP',
    }, key.privateKey, encryptedUri);
    console.log(`Derypted Uri is ${arrayBufferToString(msg)}`)

Converting ArrayBuffer back and forth from String (Done in TypeScript):从 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;
  }

You can find more examples here (I'm not the owner): // https://github.com/diafygi/webcrypto-examples您可以在此处找到更多示例(我不是所有者):// https://github.com/diafygi/webcrypto-examples

you can use those function it's so easy the First one for encryption so you just call the function and send the text you wanna encrypt it and take the result from encryptWithAES function and send it to decrypt Function like this:你可以使用这些函数很容易第一个加密所以你只需调用函数并发送你想要加密的文本并从 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 is no longer supported.不再支持 CryptoJS。 If you want to continue using it, you may switch to this url:如果你想继续使用它,你可以切换到这个网址:

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

crypt.subtle AES-GCM, self-contained, tested: crypt.subtle AES-GCM,自包含,经过测试:

async function aesGcmEncrypt(plaintext, password)

async function aesGcmDecrypt(ciphertext, password) 

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

Use SimpleCrypto使用简单加密

Using encrypt() and decrypt()使用 encrypt() 和decrypt()

To use SimpleCrypto, first create a SimpleCrypto instance with a secret key (password).要使用 SimpleCrypto,首先创建一个带有密钥(密码)的 SimpleCrypto 实例。 Secret key parameter MUST be defined when creating a SimpleCrypto instance.创建 SimpleCrypto 实例时必须定义密钥参数。

To encrypt and decrypt data, simply use encrypt() and decrypt() function from an instance.要加密和解密数据,只需使用实例中的 encrypt() 和decrypt() 函数即可。 This will use AES-CBC encryption algorithm.这将使用 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.");

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM