在Java中,最佳的加密消息方式是,即使密文是公开的。

huangapple go评论79阅读模式
英文:

Best way to encrypt messages in Java where ciphertext is public

问题

我正在用Java编写一个应用程序,用户必须能够共享加密消息,通信仅通过一个数据库进行,其中所有数据都是公开可见的。

要求大致如下:

  • 每个用户可以向公共数据库发布一些信息(例如公钥),但只能执行一次
  • 其他用户必须能够为目标用户加密消息并将其公开发布在同一个数据库上
  • 预期的接收者必须能够解密消息,但其他用户不能解密该消息
  • 加密必须足够强大,以致于没有合理的暴力计算可以解密消息(现在或将来)
  • 消息长度可变,但通常很小(类似于短电子邮件、推文等)
  • 在常规Java中必须易于实现(如果需要,可以使用诸如Bouncy Castle之类的库)
  • 如果有必要,用户已经拥有用于数字签名的Ed25519密钥对

哪种加密算法或算法组合最适合满足这些要求?我假设对于每个用户,都会使用某种形式的非对称加密算法,配合公钥/私钥对,但如果有替代想法也可以提出。我绝对不希望陷入“自己设计加密算法”的陷阱...

英文:

I'm writing an app in Java where users must be able to share encrypted messages, and communication happens exclusively via a database where all data is publicly visible.

Requirements are roughly:

  • Each user may publish some information (e.g. a public key) to the public database but should only do this once
  • Other users must be able to encrypt a message for a target user and publish it publicly on the same database
  • The intended recipient must be able to decrypt the message, but no other user should be able to do so
  • The encryption must be strong enough that no plausible brute force computation should be able to decrypt the message (now or in the future)
  • Messages are arbitrary length, but usually quite small (think short emails, tweets etc.)
  • Must be easy to implement in regular Java (libraries like Bouncy Castle are fine if needed)
  • Users already have Ed25519 key pairs for digital signatures if necessary

What crypto algorithm or combination of algorithms would be best to meet these requirements? I'm assuming some form of asymmetric encryption algorithm with a public/private key pair for each user but alternative ideas welcome. I definitely want to avoid the "roll your own crypto" trap though....

答案1

得分: 4

你(几乎总是)总是自己设计你自己的协议。这通常是各种黑客攻击的地方。

现在,如果你要自己设计你自己的算法(例如,你避免使用AES-256之类的算法,自己编写一些东西。我们就用重复应用“哦哇,这是最好的安全性”来对每个字节进行异或操作,就像是一次性密码本,无法被破解!——这种思维方式正是整个“不要自己设计加密算法”的梗的核心。不要这样做。

即使你使用现成的“协议”,也很容易搞砸并造成漏洞。协议方面(你如何使用加密算法)的本质无法轻易地抽象成一个单一的、难以滥用的预构建库。

所以,自己设计协议。就是协议这部分。

这在这里似乎几乎是微不足道的,但实际上并不是。基本的工作是使用公钥/私钥加密,按照通常的方式,使用对称加密(例如AES-256)加密消息,生成一个随机的初始化向量(IV)和随机密钥,并将它们都存储在数据库中,但密钥被加密存储——使用公钥/私钥加密。

从本质上讲,这已经足够满足你的要求。但是,如何防止重放攻击?这可能有些过于热衷或不太可能,但如果我可以往你的数据库中写东西呢?我可以重放这条消息:存储完全相同的一组加密字节,但时间戳不同,你可能会认为它是真实的。

这正是协议中的一个问题:如果你在加密前将时间戳、发送者等元数据全部包含在将被加密的数据块内部,可以帮助防止重放攻击变得无害。通常情况下,如果消息(包括元数据)完全相同,它应该是无害的。也许。通常情况下是这样。这取决于用户的期望以及他们将如何使用它。毕竟,并没有绝对安全的加密方法。但是,即使这也是不可接受的,也有解决办法,尽管简单的方法只是确保没有人可以对数据库进行原始SQL级别的写访问。

如果你的系统被“黑客”入侵,几乎总是在第二层的“协议”部分。我能直接致电你的帮助台并冒充用户吗?我能否声称我忘记了密码,然后让系统给我发送一个新密码,然后“只是”黑进用户的电子邮件?我能不能在他们的计算机上安装键盘记录器?也许在停车场里扔一些带有远程访问工具的USB设备;一种专门编写的远程访问工具,用于查找他们的密钥文件,捕获他们输入密钥文件密码的过程,并将所有这些发送给我?没有任何“现成的Java库”能够保护你免受这些攻击。你不能以一种“我不太清楚自己在做什么,但至少我知道我不知道,所以我会确保获得社区和专家推荐的库,并尽量按照它的手册去做,那么肯定没问题!”的方式来进行安全性处理。不过,如果你认真对待的话,情况就不同了。

以下是你可能需要使用的一些算法:

  • BouncyCastle支持ElGamal,一种公钥/私钥加密方案。这个想法是所有用户都有公钥和私钥;公钥为服务器所知(所有用户都可以按需求获得;你的服务器是它们的中转站,必须为真实性提供保证,也就是说,如果你说:“这是用户Foo的公钥”,你在为其提供保证,而且可能还在为用户Foo网站上的个人信息提供保证。这如何实现——取决于你,与加密无关,而是与政策和本地流程有关。你会给他们打电话吗?他们会用护照证明自己吗?怎么办?私钥仅用户自己知道。你需要自己设计一些失效方案。也许用户可以指定其他几个用户——然后他们将获得注销密钥的权利。思路是,如果用户觉得他们的私钥已经泄露,他们可以要求他们的朋友登录并在系统中进行身份验证,然后告诉系统将其公钥标记为不再有效,以供进一步的通信。

  • 请注意,如果你想在上述的保证系统中使用Ed25519密钥对,你需要找到一种方法来使用它。

  • 使用AES-256以及普通的new SecureRandom()来为系统中的每条消息生成一个密钥。要存储一条消息,你需要获取数据,生成一个随机密钥,使用该密钥和AES-256算法加密数据,存储加密数据,然后使用ElGamal和用户的公钥对密钥进行加密,并将其也存储起来。要解密这些数据,用户需要获取(用其公钥加密的)密钥数据(可以公

英文:

You (almost) always roll your own protocols. Which is usually where the hacks are.

Now, if you were to roll your own algorithm (as in, you avoid, say, AES-256, and write something on your own. We'll just XOR every byte with a repeated application of 'OhWowThisIsTheBestSecurityEvar', it's like a one-time pad right, can't be cracked! - that kinda thinking is what the whole 'dont roll your own crypto' meme is all about. Don't do that.

Even if you use off the shelf 'protocols', it's real easy to mess it up and create holes. The protocol side (HOW you use the crypto algorithms) is by its nature not easily abstracted into a single, hard-to-impossible to abuse prebuilt library.

So, roll your own. Protocol that is.

It seems nearly trivial here - but it's not. The basic job is to use Public/Private key crypto, in the usual fashion, encrypting the message with symmetric crypto (say, AES-256), generating a random IV and random key, and storing both in the DB, but the key is stored encrypted - encrypted using Public/Private key crypto.

That is, at its core, enough to do what your requirements say you want. But, what about replay attacks? Possibly overzealous or implausible, but what if I can write stuff into your DB? I could replay the message: Store the exact same ball of encrypted bytes but with different timestamps, and you'd think it was real.

That's exactly one of those protocol thingies: It helps if you include the timestamp, sender, etc all inside the blob to be encrypted - you want a replay attack to be innocuous, and generally, if the message (including the metadata) is the exact, precise same, it should be. Maybe. Usually. It depends on what your users expect and what they'll be using it for. There is no such thing as perfect crypto, after all. But if even that is not acceptable, there are solutions to that too, though the easy route is simply to ensure nobody has raw SQL-level write access to the DB.

If your system gets 'hacked' it'll almost always be in the second-level 'protocol' bits. Can I just call your help desk and inpersonate a user? Can I just say I lost my password and get a new one mailed to me, and 'just' hack the user's email instead? Can I stick a keylogger in their computer? Maybe litter some USB sticks with RAT worms around the parking lot; a RAT worm specifically written to find their key files, catch them in entering the password for this key file, and send it all to me? No 'off the shelf java library' is ever going to protect you against any of this. You can't do security in a 'I dont really know what I am doing, but I at least I know I don't know so I'll make sure to get a community and expert recommended library and try to follow its manual as well as I can and surely I'll be fine!' - kind of fashion. Not if you take it seriously.

Some of the algorithms you should probably use:

  • BouncyCastle supports ElGamal, a Public/Private key encryption scheme. The idea is that all users have a public and private key; the public key is known to your server (and all users on demand; your server is a clearinghouse for them and will have to vouch for the truth, that is, if you go: "here is the public key for user Foo", you're vouching that this is true, and presumably vouching that the personal info of user Foo on your site is correct. How - that's on you, and has nothing to do with crypto, but with politics and local processes. Do you call them? Do they identify themselves with a passport? What? The private key is known only to them. You'll need to handroll some sort of invalidation scheme. Perhaps users can anoint a few other users - they then get the right to invalidate their key. The idea being, if a user feels their private key is compromised, they ask one of their buddies to log in and authenticate themselves to the system, and tell the system to mark your public key as no longer valid for any further communications.

  • Note that you'll have to find a way to use that Ed25519 key pair you have if you want to use that for the vouching system as above.

  • Use AES-256 along with your plane jane basic new SecureRandom() to generate a key for each and every message you want to store in the system. To store a message, you take the data, generate a random key, encrypt the data using that key and the AES-256 algorithm, store the encrypted data, then you encrypt the key using ElGamal and the user's public key, and store that too. To decrypt this data, the user fetches the (with their public key encrypted) key data (which can be public), and the encrypted data (also public), and can undo the job on their end by first using their private key + ElGamal to derive the randomly generated AES-256 key used, and then use that. You don't encrypt the entire message with ElGamal; that's quite slow and not the common way to do it. AES-256 is blazingly fast. But symmetric. You'll need a 'block mode' and a 'padding mode' for your encryption in addition to an algorithm (Which will be AES-256). block mode should probably be GCM; you may read about CBC; that's outdated (worse, and slower). Definitely don't pick ECB, that's straight up insecure. Padding probably doesn't matter, depends, as usual, on so many factors.

  • You mentioned nothing about signatures. If user "Foo" wants to send a message to user "Bar" in a way that nobody but Bar can read it, all they have to do is the above. But if they do, "Bar" has absolutely no idea who sent it. GCM has some built in support for MACs, which is what you need to tag a message in a way that senders can prove they were, in fact, the sender, and also to tag date and time, though this is not easy; basically you as a server would tag any message with 'I, server, decree, and you are going to have to trust me, that this was present in the DB at this point in time and appeared recently; to me anyway. Signed, server'.

That'll give you a few terms (MAC, GCM, AES-256, ElGamal, Bouncy Castle, signatures, and a few more) to search the internet for and read up on.

答案2

得分: 3

听起来你正在开发一个聊天系统。

要满足你的需求,你需要结合两个加密系统。

第一个是Diffie-Hellman密钥交换 - 简而言之:每个参与方生成一个私钥/公钥对。公钥存储在服务器上。如果我想要向Bob发送消息,我会在你的数据库中查找"Bob"并获取他的公钥。然后,我会使用我的私钥和Bob的公钥生成一个"共享密钥" - 这个共享密钥通常是32字节长。

现在进入第二阶段 - 我使用AES算法(最好的模式可能是"GCM"模式)加密我的消息,并将加密后的消息(编码为Base64字符串)保存在你的公共数据库中(当然要附带提示,表示对方是我)。

第三阶段:Bob收到了带有"来自Michael"注释的加密消息。现在Bob在数据库中搜索Michael的公钥,使用他(Bob)的私钥和Michael的公钥构建共享密钥。某种魔法将会发生 - 共享密钥与我用于加密的密钥相同。现在Bob能够解密我的消息了。

只需要注意:要小心风险,因为如果Bob丢失了他的私钥(可能存储在被偷的智能手机上),他将无法再阅读任何发给他的消息,所以需要备份他的私钥。

英文:

It sounds to me that you are developing a chat system.

The way to fulfil your requirements you need to combine two crypto systems.

The first one is a Diffie-Hellman key exchange - in short: each party generates a private/public key pair. The public key is stored on the server. If I try to send a message to Bob I'm using your database for "Bob" and get his public key. Next I'm generating a "shared secret" with my private key and Bob's public key - this shared secret is usually 32 bytes long.

Now the second phase begins - I'm encrypting my message with an AES algorithm (best one could be "GCM"-mode) and save the encrypted message (encoded as Base64-string) in your public database (of course with any hint that the counterpart is me).

Third phase: Bob is getting the encrypted message with the note it's from Michael. Now Bob is searching in the database for Michael's public key, builds the shared secret with his (Bob's) private key and Michael's public. Some kind of magic will happen - the shared secret is the same key I used for encryption. Now Bob is been able to decrypt my message.

Just a note: beware the risks because if Bob will lose his private key (maybe stored on his stolen smartphone) he will no longer read any messages for him, so a backup of his private key is needed.

huangapple
  • 本文由 发表于 2020年9月22日 09:10:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/64001774.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定