Mã hóa và giải mã chuỗi JavaScript?


151

Tôi quan tâm đến việc xây dựng một ứng dụng nhỏ để sử dụng cá nhân sẽ mã hóa và giải mã thông tin về phía khách hàng bằng cách sử dụng JavaScript. Thông tin được mã hóa sẽ được lưu trữ trong cơ sở dữ liệu trên máy chủ, nhưng không bao giờ là phiên bản được giải mã.

Nó không phải là siêu lừa đảo an toàn, nhưng tôi muốn sử dụng một thuật toán hiện chưa bị phá vỡ.

Lý tưởng nhất là tôi có thể làm một cái gì đó như

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

để tạo chuỗi được mã hóa và một cái gì đó như

var sensical = decrypt(gibberish, key);

để giải mã nó sau.

Cho đến nay tôi đã thấy điều này: http://bitwiseshiftleft.github.io/sjcl/

Bất kỳ thư viện khác tôi nên xem xét?




10
Một số thuật ngữ ở đây bị tắt, Đây là phiên bản đơn giản 1. Muối được thêm vào thông tin (thường là mật khẩu) đang được băm. Mục đích của họ là làm cho hàm băm khác với khi không có muối. Điều này rất hữu ích vì nó tạo ra các giá trị băm nếu cơ sở dữ liệu của bạn bị hack và băm mật khẩu người dùng được lấy ra. 2. Băm là một hoạt động một chiều giúp chuyển đầu vào thành đầu ra. Nó không thể dễ dàng đảo ngược hoặc hoàn tác. 3. Mã hóa không phải là mã hóa. base64_encode, urlencode, v.v.
des

Câu trả lời:


159

 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>


8
Encrypted thực sự là một đối tượng, nhưng bạn có thể gọi mã hóa.toString () để lấy chuỗi. Bạn sẽ có thể giải mã chuỗi đó sau: jsbin.com/kofiqokoku/1
Tomas Kirda

9
Nhưng làm thế nào chúng ta có thể bảo mật mật khẩu bí mật?
duykhoa

9
Có vẻ như crypto js là một dự án lưu trữ. Có một bản sao trên github: github.com/sytelus/CryptoJS nhưng nó đã không được cập nhật trong hai năm. Đây vẫn là lựa chọn tốt nhất cho mã hóa js?
syonip

2
Tôi sẽ đi với cái này: github.com/brix/crypto-js nó cũng có sẵn thông qua NPM
Tomas Kirda

1
@stom tùy thuộc vào bạn như thế nào và nơi bạn lưu trữ nó. Tôi không biết có cách nào thực sự an toàn để lưu trữ nó trong trình duyệt hay không. Yêu cầu họ từ máy chủ và lưu trữ trong bộ nhớ.
Tomas Kirda

62

Làm thế nào về CryptoJS ?

Đó là một thư viện tiền điện tử vững chắc, với rất nhiều chức năng. Nó thực hiện các hàm băm, HMAC, PBKDF2 và mật mã. Trong trường hợp này mật mã là những gì bạn cần. Kiểm tra quide bắt đầu nhanh trên trang chủ của dự án.

Bạn có thể làm một cái gì đó như với 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>

Về bảo mật, tại thời điểm viết thuật toán AES của tôi được cho là không bị phá vỡ

Biên tập :

Có vẻ như URL trực tuyến không hoạt động và bạn có thể sử dụng các tệp đã tải xuống để mã hóa từ liên kết đã cho bên dưới và đặt các tệp tương ứng vào thư mục gốc của ứng dụng.

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

hoặc đã sử dụng CDN khác như https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js


Sự khác biệt giữa rollup và các thành phần trong thư mục 3.1.2 là gì?
Kanagavelu Sugumar

Sau khi chơi một chút các thành phần là các phần riêng biệt. Bạn sẽ cần phải biết những thành phần nào cần sử dụng (và theo thứ tự nào) để làm cho nó hoạt động. Các tập tin rollup chứa mọi thứ bạn cần để làm cho nó hoạt động chỉ với một tham chiếu tập lệnh (tốt hơn nhiều vì công việc khó khăn đã được thực hiện).
shahar eldad

2
Nhưng làm thế nào chúng ta có thể bảo mật mật khẩu bí mật?
shaijut

@shaijut Bạn không. Bạn thậm chí không lưu nó ở bất cứ đâu ngoại trừ RAM khi mã hóa / giải mã bản rõ. Cụm mật khẩu chỉ nên được lưu trữ trong não người dùng (hoặc trình quản lý mật khẩu)
slebetman

39

Tôi đã tạo ra một mật mã / giải mã văn bản không an toàn nhưng đơn giản. Không phụ thuộc với bất kỳ thư viện bên ngoài.

Đây là các chức năng

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

Và bạn có thể sử dụng chúng như sau:

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

4
let myDecodes = decoding ('CartelSystem') - Muối này cũng sẽ giải mã chuỗi. Bạn không cần phải biết chính xác từ 'mySecretSalt'
Dror Bar

Ngoài ra, saltChars trong giải mã không được sử dụng?
Thanh Dror

1
Một bài viết khác mà một người nào đó đang sử dụng một cách mù quáng let. 😒︎
John

1
Đây không phải là một) siêu vỡ và không an toàn và b) 'muối' trên thực tế là 'chìa khóa bí mật' của bạn vì muối không được dự kiến ​​là riêng tư? Tôi nghĩ sẽ rất nguy hiểm khi đăng mã như thế này mà không có bất kỳ bình luận nào cho thấy mã vui nhộn này không dành cho bất kỳ mục đích sử dụng nào trong thế giới thực. Số lượng upvote là đáng lo ngại. crypto.stackexchange.com/questions/11466/ Google
lschmierer

1
Ít nhất họ cũng sử dụng tiền điện tử âm thanh. Những gì bạn đang làm về cơ bản là một Caesar Chipher (áp dụng cùng một khóa cho mọi nhân vật) en.wikipedia.org/wiki/Caesar_codes#Breaking_the_codes Về các câu trả lời khác ... Tôi hy vọng rằng điều gì đó được gọi là "bí mật" là dự kiến ​​sẽ được giữ bí mật (bởi người dùng)
lschmierer

18

Các câu trả lời hiện có sử dụng SJCL, CryptoJS và / hoặc WebCrypto không nhất thiết sai nhưng chúng không an toàn như bạn có thể nghi ngờ ban đầu. Nói chung bạn muốn sử dụng libsodium . Đầu tiên tôi sẽ giải thích tại sao, sau đó như thế nào.

Tại sao không phải là SJCL, CryptoJS, WebCrypto, v.v.?

Câu trả lời ngắn: Để mã hóa của bạn thực sự an toàn, các thư viện này hy vọng bạn có quá nhiều lựa chọn, ví dụ như chế độ mật mã khối (CBC, CTR, GCM; nếu bạn không thể biết ai trong số ba tôi vừa liệt kê là an toàn sử dụng và theo những gì trở ngại, bạn không nên gánh nặng với loại này của sự lựa chọn nào cả ).

Trừ khi chức danh công việc của bạn là kỹ sư mật mã , các tỷ lệ cược được xếp chồng lên nhau để bạn thực hiện nó một cách an toàn.

Tại sao nên tránh CryptoJS?

CryptoJS cung cấp một số khối xây dựng và mong bạn biết cách sử dụng chúng một cách an toàn. Nó thậm chí còn mặc định ở chế độ CBC ( được lưu trữ ).

Tại sao chế độ CBC xấu?

Đọc bài viết này về các lỗ hổng AES-CBC .

Tại sao nên tránh WebCrypto?

WebCrypto là một tiêu chuẩn potluck, được thiết kế bởi ủy ban, cho các mục đích trực giao với kỹ thuật mã hóa. Cụ thể, WebCrypto có nghĩa là để thay thế Flash, không cung cấp bảo mật .

Tại sao nên tránh SJCL?

Tài liệu và API công khai của SJCL yêu cầu người dùng mã hóa dữ liệu bằng mật khẩu mà con người nhớ. Điều này hiếm khi, nếu có, những gì bạn muốn làm trong thế giới thực.

Ngoài ra: Số vòng PBKDF2 mặc định của nó nhỏ hơn khoảng 86 lần so với bạn muốn . AES-128-CCM có lẽ tốt.

Trong số ba lựa chọn ở trên, SJCL ít có khả năng kết thúc trong nước mắt. Nhưng có những lựa chọn tốt hơn có sẵn.

Tại sao Libsodium tốt hơn?

Bạn không cần phải chọn giữa một menu các chế độ mật mã, hàm băm và các tùy chọn không cần thiết khác. Bạn sẽ không bao giờ mạo hiểm làm hỏng các tham số của bạn và loại bỏ tất cả bảo mật khỏi giao thức của bạn .

Thay vào đó, libsodium chỉ cung cấp cho bạn các tùy chọn đơn giản được điều chỉnh để bảo mật tối đa và API tối giản.

  • crypto_box()/ crypto_box_open()cung cấp mã hóa khóa công khai xác thực.
    • Thuật toán được đề cập kết hợp X25519 (ECDH trên Curve25519) và XSalsa20-Poly1305, nhưng bạn không cần biết (hoặc thậm chí quan tâm) về điều đó để sử dụng nó một cách an toàn
  • crypto_secretbox()/ crypto_secretbox_open()cung cấp mã hóa xác thực chia sẻ khóa.
    • Thuật toán được đề cập là XSalsa20-Poly1305, nhưng bạn không cần biết / quan tâm

Ngoài ra, libsodium có các ràng buộc trong hàng tá ngôn ngữ lập trình phổ biến , do đó rất có khả năng libsodium sẽ chỉ hoạt động khi cố gắng tương tác với một ngăn xếp lập trình khác. Ngoài ra, libsodium có xu hướng rất nhanh mà không mất an ninh.

Làm cách nào để sử dụng Libsodium trong JavaScript?

Đầu tiên, bạn cần quyết định một điều:

  1. Bạn chỉ muốn mã hóa / giải mã dữ liệu (và có thể vẫn bằng cách nào đó sử dụng bản rõ trong các truy vấn cơ sở dữ liệu một cách an toàn) và không lo lắng về các chi tiết? Hoặc là...
  2. Bạn có cần phải thực hiện một giao thức cụ thể?

Nếu bạn đã chọn tùy chọn đầu tiên , hãy lấy CodesSweet.js .

Các tài liệu có sẵn trực tuyến . EncryptedFieldlà đủ cho hầu hết các trường hợp sử dụng, nhưng API EncryptedRowEncryptedMultiRowscó thể dễ dàng hơn nếu bạn có nhiều trường riêng biệt bạn muốn mã hóa.

Với CodesSweet, bạn thậm chí không cần biết nonce / IV là gì để sử dụng nó một cách an toàn.

Ngoài ra, điều này xử lý int/ floatmã hóa mà không rò rỉ sự thật về nội dung thông qua kích thước bản mã.

Mặt khác , bạn sẽ muốn sử dụng sodium-plus , một giao diện thân thiện với người dùng cho các trình bao bọc libsodium khác nhau. Sodium-Plus cho phép bạn viết mã hiệu suất, không đồng bộ, đa nền tảng, dễ kiểm toán và lý do.

Để cài đặt natri-plus, chỉ cần chạy ...

npm install sodium-plus

Hiện tại không có CDN công khai để hỗ trợ trình duyệt. Điều này sẽ thay đổi sớm. Tuy nhiên, bạn có thể lấy sodium-plus.min.jstừ bản phát hành Github mới nhất nếu bạn cần.

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

Tài liệu về natri-plus có sẵn trên Github.

Nếu bạn muốn hướng dẫn từng bước, bài viết dev.to này có những gì bạn đang tìm kiếm.


16

Các trình duyệt hiện đại hiện hỗ trợ crypto.subtleAPI, cung cấp các chức năng mã hóa và giải mã riêng (không đồng bộ không kém!) Bằng một trong các phương pháp sau: AES-CBC, AES-CTR, AES-GCM hoặc RSA-OAEP.

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


3
Trong số các tùy chọn trên, chỉ có AES-GCM và RSA-OAEP là hợp lý. :(
Scott Arciszewski

4

Trước khi thực hiện bất kỳ điều này, xin vui lòng xem câu trả lời của Scott Arciszewski .

Tôi muốn bạn rất cẩn thận với những gì tôi sắp chia sẻ vì tôi không có chút kiến ​​thức về bảo mật (Có khả năng cao là tôi đang lạm dụng API bên dưới), vì vậy tôi rất sẵn lòng cập nhật câu trả lời này với sự giúp đỡ của cộng đồng .

Như @richardtallent đã đề cập trong câu trả lời của mình , có hỗ trợ cho API Crypto Web, vì vậy ví dụ này sử dụng tiêu chuẩn. Theo văn bản này, có 95,88% hỗ trợ trình duyệt toàn cầu .

Tôi sẽ chia sẻ một ví dụ bằng API Crypto Web

Trước khi chúng tôi tiến hành, xin lưu ý ( Trích dẫn từ MDN ):

API này cung cấp một số nguyên thủy mã hóa cấp thấp. Nó rất dễ dàng để lạm dụng chúng , và những cạm bẫy có liên quan có thể rất tinh tế .

Ngay cả khi giả sử bạn sử dụng các chức năng mã hóa cơ bản một cách chính xác, quản lý khóa an toàn và thiết kế hệ thống bảo mật tổng thể là cực kỳ khó để có được quyền và nói chung là lĩnh vực của các chuyên gia bảo mật chuyên gia.

Lỗi trong thiết kế và triển khai hệ thống bảo mật có thể làm cho bảo mật của hệ thống hoàn toàn không hiệu quả.

Nếu bạn không chắc chắn bạn biết bạn đang làm gì, có lẽ bạn không nên sử dụng API này .

Tôi tôn trọng bảo mật rất nhiều và tôi thậm chí còn in đậm các phần bổ sung từ MDN ... Bạn đã được cảnh báo

Bây giờ, với ví dụ thực tế ...


Mã thông báo:

Tìm thấy ở đây: https://jsfiddle.net/superjose/rm4e0gqa/5/

Ghi chú:

Lưu ý việc sử dụng awaittừ khóa. Sử dụng nó bên trong một asyncchức năng hoặc sử dụng .then().catch().

Tạo khóa:

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

Mã hóa:

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

Giải mã

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

Chuyển đổi ArrayBuffer qua lại từ Chuỗi (Xong trong 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;
  }

Bạn có thể tìm thấy nhiều ví dụ ở đây (Tôi không phải là chủ sở hữu): // https://github.com/diafygi/webcrypto-examples


2

CryptoJS không còn được hỗ trợ. Nếu bạn muốn tiếp tục sử dụng nó, bạn có thể chuyển sang url này:

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


Sự khác biệt giữa rollup và các thành phần trong thư mục 3.1.2 là gì?
Kanagavelu Sugumar

1
Crypto đang đề xuất thư viện giả mạo khi bạn vào trang web của họ.
Thanh Dror

1

Sử dụng SimpleCrypto

Sử dụng mã hóa () và giải mã ()

Để sử dụng SimpleCrypto, trước tiên hãy tạo một cá thể SimpleCrypto bằng một khóa bí mật (mật khẩu). Tham số khóa bí mật PHẢI được xác định khi tạo một cá thể SimpleCrypto.

Để mã hóa và giải mã dữ liệu, chỉ cần sử dụng hàm mã hóa () và giải mã () từ một thể hiện. Điều này sẽ sử dụng thuật toán mã hóa 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.");

3
SimpleCrypto sử dụng AES-CBC không được xác thực và do đó dễ bị tấn công từ các bản mã được chọn.
Scott Arciszewski

-6

Chức năng đơn giản,


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

4
Mặc dù đoạn mã này có thể là giải pháp, bao gồm một lời giải thích thực sự giúp cải thiện chất lượng bài đăng của bạn. Hãy nhớ rằng bạn đang trả lời câu hỏi cho độc giả trong tương lai và những người đó có thể không biết lý do cho đề xuất mã của bạn.
Johan

1
Đây không phải là một thuật toán an toàn (lưu ý rằng Encrypt không lấy tham số chính) và có thể dễ dàng đảo ngược được thiết kế. OP đã yêu cầu một cái gì đó có bảo mật trên đó.
Mike S
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.