TL;DR
Do I have to sign every single message, or is there a more efficient way of verifying the origin of messages?
I am developing a simple crypto protocol for fun. Of course I know that in any serious project, I should just use some industry standard, like OpenSSL, but this is for learning and experimenting.
The idea is that the communicating terminals exchange RSA public keys, then using these keys they exchange an AES key securely, so that the AES key can be used to encrypt every message from this point. I have already implemented all of this, and it works fine.
The problem is: a potential attacker cannot read anything thanks to AES, but could still for example cause errors or attempt to hijack the communication or do some other nasty stuff by being man-in-the-middle (for instance she/he could copy an encrypted message and disrupt things by sending it again and again). What I need is digital signature so I can confirm that messages are coming from the valid source and I'm in luck, because I already have a working RSA implementation.
I know how digital signatures work (taking the hash of the message and encrypting it using the private key, etc.), but the only way I can think of making this work is to sign every single message, then check whether the signature is valid or not on the receiving side. However, I'm concerned that this will slow down my protocol. The whole purpose of using AES to secure the communication (or any symmetric key encryption) is that it's a lot faster than RSA (or any public key encryption). Wouldn't doing this defeat the purpose of AES (or any symmetric key encryption)? So the question is: do I have to sign every single message? Or is there a more efficient way of doing this? How does for example OpenSSL handle this?
TL;DR Use authenticated encryption.
In symmetric encryption it is possible to produce a Message Authentication Code (MAC) that enables you to check whether a message that you sent was (maliciously) manipulated. A man-in-the-middle attacker has only a negligible advantage of forging an authentication tag for a message that you haven't tagged.
There are many ways of doing this, but it's generally seen that a MAC should authenticate the ciphertext and not the plaintext (Should we MAC-then-encrypt or encrypt-then-MAC?). Popular MAC algorithms are HMAC (e.g. HMAC-SHA256), CMAC/OMAC1 or GMAC. There are also some distinct authenticated modes such as GCM, EAX, OCB, SIV, CWC, etc. Those combine a mode to achieve confidentiality and a mode for authenticity without the need of different keys for both.
But this is not enough because this only enables the receiver to detect tampering or forgeries of messages. An attacker may still mount other attacks such as replay or delay attacks. Therefore you need to send nonces (such as a message counter) and time stamps along. The receiver would have to keep a record of previously sent messages (by storing the nonces) and not accept any messages that are sent too late judging by its internal clock.
In order to prevent the attacker from changing the nonces and time stamps at will, those also have to be authenticated. Most authenticated modes are actually Authenticated Encryption with Associated Data which can authenticate additional non-secret data such as nonces and time stamps.
The use of authenticated encryption can make a purely symmetric communication relatively tamper-proof under the assumption that the key was exchanged confidentially and was also verified through conventional digital-signatures such as RSA-PSS or Ed25519 (EdDSA).
Related
Are there any protocols which would allow 2 visitors to communicate securely through my website, without the possibility of me reading their messages?
Ask Google for End-to-End encryption like PGP/GPG. For a client-side browser-based implementation you might want to check out GPG encryption in JavaScript.
I just googled it and cannot tell if its really secure (not sending your private key to anyone). I just want to give you a point to start with.
EDIT: Looks like it does send the clients private key to your server to perform a server-based encryption. This is not what you want. But I am sure that a JavaScript implementation of GPG is possible even though I don't know if somebody has done it yet.
Yes; for example, this is what would happen if your server was a link in a communication protected by SSL/TLS.
The participants use a public-key encryption scheme to agree upon a secret, symmetric key; that is then used to encrypt their communication.
It's also possible for the participants to simply encrypt their messages with the public key of the intended receiver. That way, only the intended receiver can decrypt the message. This is not a very advanced scheme and probably vulnerable. (Among others, if an eavesdropper can guess exactly what is sent, he can encrypt that message with the intended recipient's public key and see if the result matches with what is being sent).
There is a lot of literature available on cryptographic protocols; for starters, here's a Wikipedia article on Key Agreement Protocols.
If we're talking about not possible then the second part to S.L. Barth's answer will achieve this with the exception that the key exchange must be done by some other means. This can be the phone or email or even another website but if it's done via your website then it's open to a man-in-the-middle attack. You can tell your users to do this, you just can't actually help them do it.
There is probably a javascript library somewhere that will implement GPG encryption so that all you have to do is make sure that every message is encrypted in the browser before it's sent to your web server. You can store the messages as long as you like, they're encrypted. Only the user with the correct private key will be able to decrypt it.
SSL and TLS as they are used by websites everywhere are vulnerable to man-in-the-middle attacks. The reason we don't hear much about these sorts of attacks is that most of the people in the middle are trustworthy so the attacks simply don't happen. The recent revoking of the CA certificates of DigiNotar and others was precisely because the Iranian Government were caught acting as a man-in-the-middle and decrypting their own citizen's SSL traffic.
If you're happy with preventing casual snooping by curious sysadmins, the key exchange can be done through your website as well.
One more thing: Security is hard.
Even if you do this with well-known encryption techniques, the chances of there being a flaw in the implementation will be very close to 1. This doesn't mean that those curious sysadmins will be able to accidentally read messages but it does mean that a determined and skilled adversary will be able to find a way in. As soon as you can afford it you should hire an expert to redesign or at the very least examine your protocol and implementation.
In general, such a secure link between your users without you being able to read and/or modify their messages is only possible if they have some way of identifying each other (or at least in one direction).
This might be a shared secret (like a passphrase) or a public key known to one (or certified by a CA known to one), where the other one has the corresponding private key.
On this one can build a secure protocol (using a key exchange and then symmetric encryption with MACs in both directions), like TLS does. (Another way, used often for instant messaging, is OTR, the Off-the-Record messaging protocol.)
Without a way to identify the other end point, you end up with a way of allowing man-in-the-middle attacks. SSL/TLS without certificates, or with certificates where the man-in-the-middle knows the corresponding private key, is insecure, as is every other similar encryption scheme.
Another issue is that you said visitors of my website. This looks like you would implement client-side cryptography in JavaScript, delivered from your website. Don't do this ... if the visitors do not trust you not to read their data, they also should not trust you to feed them non-malicious JavaScript, which might implement something else than you are claiming it does, again allowing a MITM, or even directly sending a copy of the data to you.
More details about this are discussed in Javascript Cryptography Considered Harmful (from a slightly different perspective).
Despite all the advices to use SSL/https/etc. I decided to implement my own security layer on top of http for my application... The concept works as follows:
User registers -> a new RSA Keypair is generated
the Private Key gets encrypted with AES using the users login Password
(which the server doesnt know - it has only the sha256 for authentication...)
Server stores the hash of the users password
and the Encrypted Private Key and Public Key
User logs in -> authenticates with nickname+password hash
(normal nick/password -> IP-bound sessionid authentication)
Server replies: sessionid, the Encrypted RSA Private Key
and an Encrypted randomly generated Session Communication Password
Client decrypts the RSA Private Key with the users Password
Client decrypts the Session Communication Password with the RSA Private Key
---> From this point on the whole traffic gets AES-encrypted
using that Session Password
I found no hole in that chain - neither the private key nor the login password get ever sent to the server as plaintext (I make no use of cookies, to exclude the possibility of the HTTP Cookie header to contain sensitive information)... but I am biased, so I ask - does my security implementation provide enough... security?
Why does everyone have to come up with their secure transport layer? What makes you think you've got something better than SSL or TLS? I simply do not understand the motivation to re-invent the wheel, which is a particularly dangerous thing to do when it comes to cryptography. HTTPS is a complex beast and it actually does a lot of work.
Remember, HTTPS also involves authentication (eg: being able to know you are actually talking to who you think you are talking to), which is why there exists a PKI and browsers are shipped with Root CA's. This is simply extremely difficult (if not impossible) to re-invent and prone to security holes. To answer you question, how are you defending against MITM attacks?
TLDR: Don't do it. SSL/TLS work just fine.
/endrant.
I'm not a crypto or security expert by any means, but I do see one serious flaw:
There is no way the client can know that it is running the right crypto code. With SSL/TLS there is an agreed upon standard that both your browser vendor and the server software vendor have implemented. You do not need to tell the browser how SSL works, it comes built in, and you can trust that it works correctly and safely. But, in your case, the browser only learns about the correct protocol by receiving plain-text JavaScript from your server.
This means that you can never trust that the client is actually running the correct crypto code. Any man-in-the-middle could deliver JavaScript that behaves identically to the script you normally serve, except that it sends all the decrypted messages to the attacker's servers. And there's no way for the client to protect against this.
That's the biggest flaw, and I suspect it's a fatal flaw for your solution. I don't see a way around this. As long as your system relies on delivering your crypto code to the client, you'll always be susceptible to man-in-the-middle attacks. Unless, of course, you delivered that code over SSL :)
It looks like you've made more complexity than is needed, as far as "home-grown" is concerned. Specifically, I see no need to involve assymetric keys. If the server already knows the user's hashed password, then just have the client generate a session id rolled into a message digest (symmetrically) encrypted via the client's hashed password.
The best an attacker might do is sniff that initial traffic, and attempt a reply attack...but the attacker would not understand the server's response.
Keep in mind, if you don't use TLS/SSL, then you won't get hardware-accelerated encryption (it will be slower, probably noticeably so).
You should also consider using HMAC, with the twist of simply using the user's password as the crypto key.
SSL/TLS provide transport layer security and what you've done does nothing but do that all over again for only the authorization process. You'd be better served to focus on authorization techniques like client certificates than to add an additional layer of line-level encryption. There's a number of things you could also introduce that you haven't mentioned such as encrypted columns in SQL Server 2008, IPSec, layer 4 & 7 hardware solutions and even setting up trusts between the server and client firewalls. My biggest concern is how you've created such a deep dependency on the username and password, both which can change over time in any system.
I would highly recommend that you reconsider using this approach and look to rely on more standard techniques for ensuring that credentials are never stored unencrypted on the server or passed in the clear from the client.
While I would also advocate the use of SSL/TLS for this sort of thing, there is nothing wrong with going re-inventing the wheel; it leads to innovation, such as the stack exchange series of websites.
I think your security model is quite sufficient and rather intelligent, although what are you using on the client-side? I'm assuming javascript since you tagged this post with 'web-development'? Or are you using this to communicate with a plug-in of sorts? How much overhead does your implementation produce?
Some areas of concern:
-How are you handling initial communication, such as: user login, registration?
-What about man-in-the-middle attacks (assuring the client that it is talking to the authorized server)?
The major problem you have is that your client crypto code is delivered as Javascript over unauthenticated HTTP.
This gives the Man-In-The-Middle plenty of options. He can modify the code so that it still authenticates with your server, but also sends the password / private key / plaintext of the conversation to him.
Javascript encryption can be enough when your adversary is an eavesdropper that can see your traffic but not modify it.
Please note that I am not referring to your specific idea (which I did not take the time to fully understand) but to the general concept of Javascript encryption.
I was arguing about an S3 like aproach using authorization hash with a secret key as the seed and some data on the request as the message signed with hmac sha1 (Amazon S3 way) vs an other developer supporting symetric encryption of the data with a secret key known by the emiter and the server.
What are the advantage of using signed data with hmac sha1 vs symetric key other than the fact that with the former, we do not need to encrypt the username or password.
What would be the hardest to break ? symetric encryption or sha1 hashing at la S3 ?
If all big players are using oauth and similar without symetric key it is sure that there are obvious advantages, what are those ?
An hmac and a symmetric cipher are not mutually exclusive ideas. In fact AES-CMAC which is both an MAC (not hashed) and a symmetric cipher, AES-CMAC is the cryptographic primitive that makes WPA secure. (Although a WPA can still be broken using a rainbow table).
You should not need an exotic authentication system for this. Logging in with a username and password and maintaining session state with a cookie is commonly used because it is easy to implement and it is secure. By storing state, like a cookie its no longer technically RESTful, but there is nothing stopping you from doing it.
However, in terms of authentication I believe that asymmetric cryptography like RSA is the most secure. (Amazon uses asymmetric cryptography for ssh connections by default.) This allows you to only store the public keys, so that if your server where to be compromised no authentication credentials could be used. It also defends against MITM attacks. In many cases this can be implanted quite easily with REST because HTTPS already supports client certificates. You can sign the clients certificates for free and then verify them your self.
If implemented properly, the strength of an hmac vs symmetric cipher it mostly comes down to the strength of the secret. If you are using a secret like a password, then both systems are equally very weak. These secretes must be large, Cryptographically Secure Psudorandom Numbers. Another thing to keep in mind is that symmetric ciphers are very difficult to implement properly. Most programmers do not understand this and end up reusing PRNG when using stream cipher or when using a block cipher they use an incorrect mode and leave the IV null. By contrast HMACS are very easy to implement and less can go wrong. If everything is transmitted over HTTPS, and your are using an hmac then its easy to implement a secure authentication system. If you really want to implement a symmetric cipher you must get a copy of Piratical Cryptography, there are a few chapters devoted to symmetric ciphers alone because so much can go horribly wrong. You also have to take key distribution into consideration, ssl uses a DH-Key Exchange for its symmetric keys.
Make sure to read the OWASP top 10, especially Broken Authentication and Session Management. This requires the use of https for the entire session, most web application programmers don't realize this.
The big differences would be that HMAC would provide integrity but no privacy, while encryption would provide privacy without integrity. Many use cases would require both, but I can't think of any where integrity is unnecessary. HMAC seems like a minimum requirement, with encryption being a likely companion.
I'm not a cryptography expert, I actually only have a little bit of experience using it at all. Anyways, the time has come where one of my applications demands that I have some encryption set up. Please note, the program won't be managing anything super critical that will be able to cause a lot of damage.
Anyways, I was just trying to see if this scheme that I'm using is common and if there are flaws (of which there may be completely stupid & horribly flawed design, that's why I'm asking).
Ok, I have a client -> server communication. The Client I can hard code in the public portion of a 2048-bit RSA key. When the client wants to initiate a secure connection, he sends his username, md5 hash of his password, and a hash of a random UUID, all of which has been encrypted against the server's Public Key. The server receives the information and decrypts using its private key. Checks the database to see if his login + pass work & if they do, create a new entry in the "Sessions" table in the DB. This includes a SessionID, UID (user ID), and the UUID hash. Using the corresponding session ID's UUID as the keyphrase, the server will then send back a message that has the Blowfish encrypted word "Success!" + a random UUID (this message is Digitally Signed so we can determine if it came from the server or not). From that point on, when the client sends info to the server, it will be with a plaintext sess_id & include a Blowfish encrypted message, using the corresponding Session ID's blowfish secret (stored encrypted in the DB) as the key to encrypt / decrypt.
Specifically, I am curious as to whether this system "should work" or if anyone notices that it's glaringly obvious that a vulnerability exists, such as MITM.
Issues I can see off the top of my head (although you have left out most of the details, which is where the devil famously resides):
If you're using a UUID generator rather than a real cryptographic RNG, it likely has insufficient entropy. Don't discount this - in the real world, the favourite way of covertly weakening an encryption system has been to weaken the RNG;
Your initial RSA encryption sounds like it is susceptible to a small-exponent attack, and potentially other creative attacks. There's too much structure there to be comfortable;
It sounds like there's numerous opportunities for replay attacks;
What block cipher mode are you using with Blowfish?
I recommend using TLS/SSL - it's had a lot more friendly eyes looking at it for a lot longer than anything you build yourself ever will.
Just use SSL or DTLS, IKEv2, HIP, EAP or some suitable standard protocol. Don't try to invent your own crypto protocols, nobody has enough expertise to do this on their own. Your protocol doesn't have nearly enough entropy in it, so far as I can see, so your resulting keys will be pretty weak.
From that point on, when the client sends info to the server, it will be with a plaintext sess_id & include a Blowfish encrypted message, using the corresponding Session ID as the key to encrypt / decrypt.
If you're sending the session id in plaintext, and using it as the encryption key, how is that secure?
I see no reason why you can't use standard SSL authentication and let the library implementer worry about the handshaking.
I was given advice that I am suspicious about so I'm looking for support here to go back and challenge the advice.
I was advised to use Diffie-Hellman to get both sides to agree on a secret key, use the secret key to generate an AES key, and then use AES to encrypt/decrypt passwords that are being transmitted. Pretty much like the sample code here
When using this scheme, the length of the encrypted password is the same as the length of the unencrypted password. Should I be worried about this?
Before, I was using RSA, encrypting the passwords with the receiver's public key. This was resulting in an encrypted length of 256 no matter what the password length. Isn't that better?
You can just pad to whatever length with any data. It doesn't have to be random. As long as it's all encrypted. I think though that is the least of your worries.
Note if you use Diffie-Hellman you still need to authenticate the parameters sent, which you probably need to do with RSA.
The alternatives are:
Use RSA to exchange an encrypted secret key that you then use to encrypt your data.
Use Diffie-Hellman to exchange a secret key and then use RSA to sign values sent to authenticate the transaction.
If you do all this, then you have to also worry about whether exchanges have been replayed to make you reuse keys etc.
To be honest if you need to ask this question then you probably are not qualified to write a crypto protocol. They are extremely hard to get right and not for the faint hearted.
Suggest you use SSL/TLS for your exchange if you need to stream a lot of data. PGP/PKCS#7 if you just need to send a single message.
First off: Don't invent your own authentication protocol. Period. If you do, you WILL get it wrong even if you're using strong encryption. There are a number of existing well documented authentication protocols that have been vetted by cryptographers and thus are thought to be secure. Don't be tempted to "simplify" them, they've already been simplified.
Second: IMHO you should never send passwords on the wire for authentication (I'm not aware of any authentication protocol which does, including the hideously insecure NTLMv1 protocol)[1].
If you're dead set on going down the "roll my own authentication scheme" path, here's how I'd make the scheme you described above more secure (Caveat: I'm not a cryptographer - I believe that there are serious weaknesses in what I'm describing here):
Instead of sending the password directly, send a one-way-function (also known as a OWF, often implemented as a cryptographic hash like SHA256 or stronger) of the password.
In other words, have the server send the client a salt value, add the salt to the password, compute the OWF of the password+salt value and send the OWF result to the server. On the server, add the salt to the password and also perform the OWF calculation. If the results are the same, the password is valid, if they're not it's invalid.
And finally have whatever you do reviewed by a real cryptographer. They will find problems in your implementation and you're going to have to fix them. They're likely to suggest that you abandon your effort in favor of an existing published protocol.
[1] AFAIK, the only time you should send the password on the wire is when you're changing the password and even then, you should pad the length to a multiple of the block size (include the length in the cybertext so that when you decrypt it you can distinguish between the password and the padding).
If you can help it, don't send passwords over the wire at all. Instead, use a scheme like SRP, which authenticates both parties with one password.