繁体   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