JavaScript 문자열 암호화 및 해독?


152

JavaScript를 사용하여 클라이언트 측의 정보를 암호화하고 해독하는 개인용 소형 앱을 작성하고 싶습니다. 암호화 된 정보는 서버의 데이터베이스에 저장되지만 해독 된 버전은 아닙니다.

보안이 철저해야 할 필요는 없지만 현재 깨지지 않은 알고리즘을 사용하고 싶습니다.

이상적으로는 다음과 같은 것을 할 수 있습니다.

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

인코딩 된 문자열을 생성하고

var sensical = decrypt(gibberish, key);

나중에 해독합니다.

지금까지 나는 이것을 보았다 : http://bitwiseshiftleft.github.io/sjcl/

내가 봐야 할 다른 라이브러리가 있습니까?




10
여기 일부 용어가 꺼져 있습니다. 여기에 간단한 버전 1이 있습니다. 소금은 해시되는 정보 (일반적으로 암호)에 추가됩니다. 그들의 목적은 해시가 소금이없는 것과 다른 것을 만드는 것입니다. 데이터베이스가 해킹 당하고 해시 된 사용자 비밀번호가 나올 경우 해시를 미리 생성하므로 유용합니다. 2. 해싱은 입력을 출력으로 변환하는 단방향 작업입니다. 쉽게 되돌 리거나 취소 할 수 없습니다. 3. 인코딩은 암호화가 아닙니다. base64_encode, urlencode 등
des

답변:


160

 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는 실제로 객체이지만 encrypted.toString ()을 호출하여 문자열을 가져올 수 있습니다. jsbin.com/kofiqokoku/1
Tomas Kirda가

9
그러나 비밀 암호를 어떻게 확보 할 수 있습니까?
duykhoa

9
crypto js는 보관 된 프로젝트 인 것 같습니다. github에는 github.github.com/sytelus/CryptoJS 복제본이 있지만 2 년 안에 업데이트되지 않았습니다. 이것이 여전히 js 암호화에 가장 적합한 옵션입니까?
syonip

2
나는 이것을 가지고 갈 것이다 : github.com/brix/crypto-js 그것은 NPM을 통해서도 이용 가능하다
Tomas Kirda

1
@stom 저장 방법 및 위치는 사용자에게 달려 있습니다. 브라우저에 안전하게 저장할 수있는 방법이 있는지 모르겠습니다. 서버에서 요청하고 메모리에 저장하십시오.
Tomas Kirda 2014

62

CryptoJS어떻 습니까?

많은 기능을 갖춘 견고한 암호화 라이브러리입니다. 해시, HMAC, PBKDF2 및 암호를 구현합니다. 이 경우 암호가 필요합니다. 프로젝트 홈페이지에서 빠른 시작 quide를 확인하십시오.

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

https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/aes-min.js 와 같은 다른 CDN을 사용했습니다.


폴더 3.1.2에서 롤업과 구성 요소의 차이점은 무엇입니까?
Kanagavelu Sugumar

약간 연주 한 후 구성 요소는 분리 된 부분입니다. 어떤 구성 요소를 어떤 순서로 사용해야 작동하는지 알아야합니다. 롤업 파일에는 단 하나의 스크립트 참조로 작동하는 데 필요한 모든 것이 포함되어 있습니다 (열심히 작업 한 경우보다 훨씬 우수).
shahar eldad

2
하지만 비밀 암호를 어떻게 확보 할 수 있습니까?
shaijut

@shaijut 당신은하지 않습니다. 평문을 암호화 / 복호화 할 때 RAM을 제외하고는 저장하지 않습니다. 암호는 사용자의 뇌 (또는 암호 관리자)에만 저장해야합니다
slebetman

39

안전하지 않지만 간단한 텍스트 암호 / 암호화 유틸리티를 만들었습니다. 외부 라이브러리와의 종속성이 없습니다.

이들은 기능입니다

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'

4
let myDecipher = decipher ( 'CartelSystem')-이 소금은 문자열을 해독합니다. 정확한 단어 'mySecretSalt'를 몰라도
인 Dror 바

또한 해독의 saltChars가 사용되지 않습니까?
Dror Bar

1
누군가가 맹목적으로 사용하는 또 다른 게시물 let . 😒︎
John

1
이것이 a) 완전 깨지거나 안전하지 않은가? b) 소금이 사적인 것으로 예상되지 않기 때문에 '소금'이 실제로 '비밀 키'가 아닌가? 나는이 재미있는 코드가 실제 용도로 사용되지 않는다는 의견없이 이와 같은 코드를 게시하는 것이 매우 위험하다고 생각합니다. 공짜의 양이 걱정됩니다. crypto.stackexchange.com/questions/11466/…
lschmierer

1
적어도 그들은 소리 암호화를 사용합니다. 당신이하고있는 것은 기본적으로 (모든 문자에 동일한 키를 적용) 시저 Chipher입니다 en.wikipedia.org/wiki/Caesar_cipher#Breaking_the_cipher ... I는 뭔가가 "비밀"이라고 분명있을 것으로 예상 다른 답변에 대해서 비밀 유지 (사용자에 의해)
lschmierer

18

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대부분의 사용 사례에는 충분하지만 암호화하려는 고유 필드가 많은 경우 EncryptedRowEncryptedMultiRowsAPI가 더 쉬울 수 있습니다.

CipherSweet 를 사용하면 nonce / IV가 안전하게 사용하는 것이 무엇인지 알 필요가 없습니다 .

또한 암호 텍스트 크기를 통해 내용에 대한 사실을 유출하지 않고 처리 int/ float암호화합니다.

그렇지 않으면, 당신은 할 것 나트륨 플러스 , 다양한 libsodium 래퍼에 대한 사용자 친화적 인 프론트 엔드입니다. Sodium-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 기사 에 원하는 내용 이 있습니다.



5

이 중 하나를 구현하기 전에 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


2

CryptoJS는 더 이상 지원되지 않습니다. 계속 사용하려면 다음 URL로 전환하십시오.

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


폴더 3.1.2에서 롤업과 구성 요소의 차이점은 무엇입니까?
Kanagavelu Sugumar

1
Crypto는 사이트에 입장 할 때 단조 라이브러리를 권장합니다 .
Dror Bar

1

SimpleCrypto 사용

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

3
SimpleCrypto는 인증되지 않은 AES-CBC를 사용 하므로 선택된 암호문 공격에 취약합니다.
Scott Arciszewski

-6

간단한 기능,


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
이 코드 스 니펫이 해결책이 될 수 있지만 설명을 포함하면 게시물의 품질을 향상시키는 데 실제로 도움이됩니다. 앞으로 독자들에게 질문에 대한 답변을 제공하므로 해당 사람들이 코드 제안의 이유를 모를 수도 있습니다.
Johan

1
이것은 안전한 알고리즘이 아니며 (Encrypt는 주요 매개 변수를 사용하지 않습니다) 쉽게 리버스 엔지니어링 할 수 있습니다. OP는 보안이 필요한 것을 요구했습니다.
Mike S
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.