是一个活着的时间。我们生活在AI上升的时代,每周发生巨大的数据泄露事件,而勒索软件是每日威胁。
每个服务和应用都要求我们提供大量的个人信息,几个月后,这些信息通常可以在Darknet中找到。数据保护是这里的关键。
我想几乎每个人都听说过例如PGP和加密。但是说实话,PGP有点奇怪,而不是普通用户。
因此,我们有RSA,AES(以各种形式)和诸如Chachapoly之类的较新的算法。因此,我们有保护数据的能力。但是为什么使用此应用程序很少?
从一端到另一端
因此,让我们介绍称为端到端加密(E2EE)的奇怪的东西。
如果我们看一下wikipedia,它描述了e2ee:
端到端加密(E2EE)是一个私人通信系统,只有交流用户才能参与。因此,没有人,包括通信系统提供商,电信提供商,互联网提供商或恶意演员,都无法访问Converse
的加密密钥
因此,这是一个广泛的定义。我查看了许多宣传E2EE并相信我的应用程序,公司对E2EE的真实定义有很大的定义。
对于像我这样的某些人来说,如果您对数据进行加密,以便只有对方才能解密并看到它。
对于其他人来说,SSL/TLS也是E2EE。 ðÖ
网络加密API
,但假设你像我一样。并需要真正的加密。那里有很多libs可以为您提供帮助。我们为几乎所有语言都有加密库:PHP,Ruby,Python,C ++等。
如果我们在网络中,我们会面临挑战。如果我们的PHP后端正在加密数据,我们仍然将其发送到服务器。该服务器可能会受到损害(现在或以后)。以及我们如何管理所有钥匙?
要真正安全,我们应该只依靠客户端加密。所有数据都在设备上加密并将加密加密到服务器。没有中间人!
我们甚至为此提供了官方的浏览器API。 Web Crypto API
,不要害怕加密。这并不难!我保证!
对称加密
我们可以使用不同的系统。一个是Symmetric-key Encryption
这很容易理解。您和您的好友知道的共享钥匙。然后您使用该键加密消息。然后,您的好友可以用密钥解密消息并阅读。
AES-GCM
一个非常普遍的标准是AES。它具有不同的模式,并且都有优点和缺点,但是我们不会在这里详细介绍。
在此示例中,我们将重点关注AES-GCM。因为对于我们的用例,它将是最安全的。
我们必须采取的步骤很简单。
- 生成AES键
- 加密我们的数据
- ???
- 安全!!
让我们从第一步开始。关键一代:
(为了理智,我们将写一些辅助功能)
async generateAESKey (): Promise<CryptoKey> {
return await window.crypto.subtle.generateKey(
{
name: 'AES-GCM',
length: 256
},
true,
['encrypt', 'decrypt']
)
}
查看generateKey的文档以获取更多信息。
,但简而言之,我们说的是我们正在使用AES-GCM,我们可以提取键,并且键将用于加密和解密。
然后我们需要加密数据。
export interface EcryptedData {
iv: Uint8Array
encrypted: ArrayBuffer
}
async encrypt (data: string, aesKey: CryptoKey): Promise<EcryptedData> {
const encodedData = new TextEncoder().encode(data)
const iv = window.crypto.getRandomValues(new Uint8Array(128))
// Encrypt with AES key
const encryptedData = await window.crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: iv },
aesKey,
encodedData
)
return {
iv: iv,
encrypted: encryptedData
}
}
好吧,我们这里还有更多的事情。
首先,我们假设您的数据是字符串之类的消息。 NE需要将其转换为适当的ArrayBuffer
,这是加密所需的。我们可以将TextEncoder
使用。
然后,我们需要一个IV(初始化向量)。重要的是要注意,IV应该始终是随机的!
为了帮助我们真正的随机性(在计算机系统中很难),我们具有getRandomValues
函数。
然后,我们将我们的aesKey
和encodedData
传递给函数,而crypto.subtle.encrypt
将返回加密的数据。
请注意,我们需要存储加密的数据和IV!
const aesKey = await generateAESKey()
const wallet = await encrypt('Hello World', aesKey)
// wallet.encrypted => 🔒
// wallet.iv => [150, 142, 234, 218, 156, 112,...]
甜!现在我们有military-grade encryption!
但是,我们还需要解密我们的信息。
但首先,让我们考虑如何存储加密数据。就像我之前说过的,在网络加密API的世界中,我们将主要使用阵列缓冲区。但是,存储阵列缓冲液可能不是最优雅的解决方案。
有多种方法可以做到这一点,但是为简单起见,我们只是将arraybuffer转换为base64。
要解密我们的秘密消息,我们需要数据,AES密钥和IV。
async decrypt (data: string, key: CryptoKey, iv: string): Promise<string> {
const ivBytes = base64ToArrayBuffer(iv)
const dataBytes = base64ToArrayBuffer(data)
const decryptedData = await window.crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: ivBytes },
key,
dataBytes
)
return new TextDecoder().decode(decryptedData)
}
再次,我们将从crypto.subtle.decrypt
中获得一个数组缓冲区
const aesKey = await generateAESKey()
const wallet = await encrypt('Hello World', aesKey)
// wallet.encrypted => 🔒
// wallet.iv => [150, 142, 234, 218, 156, 112,...]
const message = await decrypt(wallet.encrypted, aesKey, wallet.iv)
// message => 'Hello World'
我们完成了!我们现在在应用程序中已加密。
但是,在实际应用中,除了加密过程外,我们还有其他疼痛点。
- 我们如何存储AES密钥?
- 我们希望用户定义密钥?
- 使用密码?
PBKDF2
常见用例是使用用户密码加密某些内容。因此,我们需要一种从字符串(密码)生成坚固的加密密钥的方法。 PBKDF2进行救援!
使用基于密码的键推导,我们可以从低熵输入(密码)生成安全的高熵键。
async getKeyFromPassword (password: string): Promise<CryptoKey> {
const encoder = new TextEncoder()
return await window.crypto.subtle.importKey(
'raw',
encoder.encode(password),
'PBKDF2',
false,
['deriveBits', 'deriveKey']
)
}
因此,我们使用crypto.subtle.importKey
来从密码中获取一个密码。重要的是,我们可以deriveBits
和deriveKey
。
我们不能直接将Cryptokey用于AES。为此,我们需要得出密钥。
async getAESKeyFromPBKDF (
key: CryptoKey,
salt: BufferSource
): Promise<CryptoKey> {
return await window.crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt,
iterations: 100000,
hash: 'SHA-256'
},
key,
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
)
}
现在我们可以使用用户密码生成AES密钥。
const password = 'Please-use-proper-high-entropy-passphrases!'
const PBKDFSecret = await getKeyFromPassword(password)
const aesKey = await getAESKeyFromPBKDF(PBKDFSecret, salt)
const wallet = await encrypt('Hello World', aesKey)
// wallet.encrypted => 🔒
// wallet.iv => [150, 142, 234, 218, 156, 112,...]
const message = await decrypt(wallet.encrypted, aesKey, wallet.iv)
// message => 'Hello World'
我们又完成了。恭喜。您有加密!
在第2部分中,我们将查看与RSA的非对称密钥加密以及有关存储秘密,钥匙以及我在与客户端端到端加密建立whistleblowing software时学到的知识的一些最佳实践。