So, I'm trying to perform a key exchange using the OpenSSL EVP methods for elliptic curve DH to derive a shared secret. This is necessary to provide strong security using AES in GCM mode for a custom protocol.
Problem is, I can only seem to find information and examples about exchange of static keys (ECDH) (see here).
To make my key exchange "ephemeral", do I do the following?
Generate a new "ephemeral" public and private key on the server (pub_s_e,priv_s_e) and on the client (pub_c_e, priv_c_e) using EVP_PKEY_keygen
Sign the new ephemeral public keys with the master private key on both the client and server (sign pub_s_e with priv_s on the server, pub_c_e with priv_c on the client) using EVP_DigestSign functions.
Exchange the signed ephemeral public keys between client and server (can be in cleartext).
Authenticate the server's ephemeral public key against the known master public key for the server, authenticate the client's ephemeral public key against the known master public key for the client using EVP_DigestVerify functions.
Perform ECDH to derive a shared secret, hash it (to remove the weak bits) and using a key derivation function like EVP_BytesToKey to get an encryption key and initialization vector (IV) for AES.
Encrypt as usual using the derived key and IV.
This would seem to provide perfect forward secrecy since a compromise of the master keys would allow signing of future keys but not allow retrieval of past ephemeral keys.
Am I missing anything?
Alright, I ended up solving this. Ephemeral ECDH simply requires the generation of ephemeral keypairs on both the server and client (using EVP_PKEY_keygen. Authenticating them is optional for ECDHE but the "signing" approach using the static key I suggested above is valid.
Sending the static public keys via cleartext should pose no problems so all the client needs to do is authenticate the server's certificate and verify the signature on the server's ephemeral public key.
I did some experiment with the openssl evp library for performing a simple ECDH key agreement in C. The readme has some info on how to compile. I added a few comments for myself to understand what was going on. Hope it helps.
https://github.com/prithuadhikary/OPENSSL_EVP_ECDH_EXAMPLE
Related
My application requires secure messaging between a server and client devices. The devices are not directly internet connected so it is not possible to use standard HTTPS or MQTT connections.
My preferred approach is to use asymmetric authenticated encryption such as libsodium's crypto_box API. The devices would encrypt and authenticate messages using their private keys and the server's public key. The server would do the same using its private key and the devices' public keys.
For this to be safe, I believe the server private key(s) need to be managed by a KMS. I am using Google Cloud Platform for the backend, and I do not see a way to have GCP KMS decrypt and authenticate messages encrypted by libsodium: GCP KMS doesn't seem to support libsodium's key algorithms, and it doesn't seem to support authenticated encryption.
I like libsodium because it is well-supported on my chosen embedded platform, and I like GCP KMS because I'm using GCP for the backend. It seems like the two are primarily designed to handle messages encrypted by themselves.
Is there a way for me to use libsodium on my devices, while maintaining the server private keys in GCP KMS? Or is another approach needed?
Libsodium's crypto box uses X25519 to agree a key using one end's private key and the other's public key (and vice versa). It then uses the derived key and random nonce to encrypt the message to encrypt the message with a symmetrical, authenticated stream cipher (Salsa20). (This key exchange happens to give mutual authentication - both ends need their private key. It can be broken down into parts, so that the more expensive EC parts are performed once and the derived key re-used for several messages.)
As you've discovered ECDH isn't supported (though someone did open an feature request once).
Working backwards from what is supported by Cloud KMS with asymmetric keys it's just decryption (and signing). (Encryption is left to the client which can obtain the public key and it it itself.)
So, if the server private key is to remain safe in the KMS, all you can do is encrypt and decrypt small plain-/cipher-texts.
You can emulate many of the parts of crypto box with what you have available. To encrypt a message: generate a random key and IV, use an authenticated symmetric cipher (e.g. Salsa or AES-GCM), encrypt the random key in the receiver's public key and send the encrypted key, IV and ciphertext. As with crypto box, you could partition this by, say, sending the encrypted key once and reusing it for several messages (with a different IV each time, of course).
At the server end you need RSA encrypt (decrypt is done in KMS) and the symmetric cipher. At the client end you need RSA encrypt, decrypt and the same cipher. Libsodium has AES-GCM, so that seems a good choice.
But you are still missing RSA at the client. Looks for alternative C solutions for that (e.g. libtomcrypto or Oryx Embedded CycloneCRYPTO Open). You might find that whatever you need for RSA also happens to do the AES-GCM too.
I`m really new to this, so I followed some tutorials, and made an example encrypt/decrypt in C++ for packets between client - server using a public and private key.
The results are quite promising, and working quite well, but I am confused, really confused, and I cannot make mistakes ( especially in a commercial environment ).
Please explain to me how I should use the encrypt/decrypt, at the moment I have:
traffic encrypted with public key and decrypted with private key
traffic encrypted with private key and decrypted with public key
I need to understand and know which is the correct method usage, this is what I understood ( please don't laugh ):
**
SERVER (encrypts traffic with private key) -> CLIENT (decrypts
traffic with public key)
CLIENT (encrypts traffic with public key) -> SERVER (decrypts
traffic with private key)
**
So my questions are as following:
Is this correct what I have ?
Does the public key go to the client,and the private key stays on the server ?
Is there are other security issues that can occur?
Many thanks
Cryptography is complicated and you should understand the protocols completely before you use them, especially in a commercial environment. So Ofer Arial is right, further reading is necessary.
But good thing you asked because your example is actually wrong. Here the corrected version:
A encrypts traffic with B's public key -> B decrypts traffic with B's private key
B encrypts traffic with A's public key -> A decrypts traffic with A's private key
I used A and B instead of server and client because the same applies to any kind of connection: server-client, client-client, server-server.
The overview shows that both the server and the client have their own key-pairs. In general, the sender always uses the receiver's public key to encrypt and the receiver uses his own private key to decrypt.
What's wrong with your version? Well, everyone with the public key could read the traffic sent to the client, so the encryption would make no sense.
Additionally to the encryption, the connection also requires an authentification, so each party knows that the (encrypted) message actually came from the correct person. So the key-pair
is also used for authentification:
A signs traffic with A's private key -> B's verifies that the traffic came from A with A's public key
B signs traffic with B's private key -> A's verifies that the traffic came from B with B's public key
Doing both encryption and authentification are done with the same key-pair:
The private key is used for decryption and signing.
The public key is used for encryption and verification.
Now, how do A and B get the public keys and how can they ensure, that they got the correct ones? Either you meet and hand it too each other personally, or you use certificates of trusty parties.
This is just a short answer, but as already said further reading is necessary.
You want to learn about Asymetric Encryption (public-key encryption).
Basically speaking, each side generates a pair of keys - (private key and public key, that are related somehow).
Learning about OpenSSL is related to ssl-certificates, and you have to understand that to work with ssl. Try reading: SSL explained for beginners.
It's not necassarily related to server-client.
For example, you have your pair of keys.
Then, your public key is sent to the your friend, and the friend encrypts the data with the public key you gave him and sends it to you.
Now, you can unlock (decrypt) the encryped data using your private key.
You can learn this important topic in the following resources:
From wiki: Public-key cryptography
Explaining videos: Encryption types explained
Asymetric encryption explained: Video
Public key encryption video: With Diffie-Hellman example
For depper understanding: RSA algorithem explanation+example
Please read the question first as the heading isn't very clear and I am not able to put my question in few words.
My scenario is as such:
I am creating a P2P application. A peer, during installation, generates Public and Private key pair and uploads the public key to my central server. When a peer A wants to communicate with peer B, it downloads the public key of B and normal encryption and data transfer takes place.
I need a little heads up for creating this public/private key pair programmatically. Again I need this public key of a peer to be signed by the central server's private key so as to know its authenticity.
Do I have to create a private CA sort of thing or any other way is possible ? If someone can help me understand this creating, signing, etc from programming perspective, that would be great. I am getting a lot of concept out there in google search but not much in coding. I am new to cryptography so any other idea to implement the same would be helpful.
Note: I cannot use any third party CA. And I am not using the certificates for authentication, but for encryption.
Thanks.
The reason you get many hits on the concept but not the programming is that it is hard to come up with a good protocol, and not that hard to actually program it. It is the right order to go with this too, this kind of protocol you cannot just program and document later.
It doesn't really matter much if you want to use the certificates/private key for signing or for encryption. The point is that the public key needs to be trusted. If you cannot trust the public key then you cannot guarantee that the encryption was performed for the right entity. This means that e.g. peer A was encrypting using a public key of M instead of B. This is almost always a problem, unless you are only interested in eavesdropping attacks.
As you seem to have a central server it seems logical to use a hierarchical trust model. For such a system PKI using X509 certificates and a central CA makes the most sense. You can use an OpenSSL based CA system or any other free CA solution such as EJBCA or some Windows server based solution.
When you have the CA running you are not there yet. You need a method for the peers to trust the root certificate of the CA. After that you need a method for the CA to trust the certificate requests send by the peers. Without more information it is hard to say what method would fit you best.
You should only worry about the programming when you've figured all this out (at the minimum).
classic PKI scenario ... you want a CA ...
all your peers need to know the public CA cert ahead of time ...
once a peer registers with the server, it should encrypt its own key so the server can be sure that the received key wasn't tampered with...
for data structure you should probably use X509
from the programmers point of view:
peer A wants to register with a new keypair ...
-> generate keypair
-> fill the identification details of a certificate, and attach the public key
(what you have now is usually called "certificate request")
-> encrypt the request symetrically (AES-CBC-256 with a random IV looks like a good choice)
-> encrypt the symertic key, and send that together with the encrypted request and the cleartext IV to the server (optionally, include a server provided nonce or addidional session data in the encrypted part)
on the server side:
decrypt, check the data, especially the identification information of the request
if that's ok, take the request (ID part + public key) and sign that with the CA key
report back to peer A and handover the signed certificate (since it does not include any secrets, that may be clear text or encrypted with the provided key for peer A)
once you need to make contact between the peers, you only need some contact information:
if peer X wants to contact peer A, all you have to hand out is the address how A can be contacted ... X then contacts A and asks for ID ("Hello? is this A? please give me your cert and here is my cert.") ... after certificates have been exchanged the signatures are verified ... if CA sigs are ok, both parties generate random numbers ("nonces"; number-once-used) and encrypt them with the public keys from the received and verified cert and hand them over to the other peer ... upon receiving the encrypted value is decrypted, and reencrypted with the other parties key, and send back ... upon receiving that decrypt the value with your own private key, and verify that it's the same number you sent ... authenticated connection is established and you can now proceed to handover symetric keys and start transfering encrypted data
if you think you can live without authenticating the other peer, you can directly start transfering encrypted data after you checked the CA sig on their cert ... but consider that in this case, an attacker can receive data that's not for him (he can't decrypt it, but he can pretend to be the other peer ...)
After many headaches and people advising to stop, I finally managed to get my Server/Client App to work with this API and create the required keys, i.e. Session and Exchange.
When I send the public key to the client, it successfully imports the key and will also encrypt a message using that key, but when I pass it back to the server; it decrypts the message using the session key but the message is returned as garbage (hmm.. private key is needed!). Now this could be due to way I am passing the encrypted message back via rpc, but something tells me it is something else. Ideally what I need is a clear and plain explanation of what it is I should be doing with all these keys, because the information I am currently getting is quite confused.
Do I pass the exchange public key to the client so it can encrypt a message and return for decryption.
Or:
Should I actually be encrypting the clients session key with the servers public key and then return that? (This doesn't sound right to me, but I am all ears!!!)
Please leave out comments to move to another API, or copy pasties from MSDN (I have already read all that). I am working with the Crypto API and just need a clear explanation of what keys the server should pass to the client, and then what the client should do and pass back so I can finally move on...
Sounds like you are on the right track if you really are determined to stick with that API :)
There are two distinct families of encryption algorithms in cryptography. 1) Ones that use symmetric keys and 2) those that use asymmetric keys. Symmetric key algorithms (e.g. AES, DES...) are very fast and should be used as long as there's a safe way to make sure both client and server have the same key (i.e. session key) and no one else can gain access to that key. On the other hand, asymmetric key algorithms (e.g. RSA...), which are also known private/public key algorithms, are much more computationally expensive. They have one key which can only be used to encrypt data and a second key which can only be used to decrypt data. These algorithms, as you found out, are perfect for the initial handshake and session key exchange. The server creates public/private key pair and sends the client the public key. Anyone can intercept it, but when the client encodes the session key and sends that back, pbulic key is useless if an eavesdropper wants to find out the session key. Only the server can decode the message as it is the only entity that is holding the private key. So your initial problem was that when the message came back, instead of using the private key from the pair, you were using synchronous session key and thus were getting garbage.
Essentially you've just implemented the basic handshake that SSL does (and you could easily do with very few lines of code if using OpenSSL library).
Once the handshake is performed you now have a secure channel between the client and the server. The only problem you might have is, what if someone piggy backs on your server's IP address and starts pretending like they are the real server? Your client will think he is talking to the real server, it'll do the key exchange and will start sending secure information, but that information might all end up in malicious hands if an attacker's PC happens to be on the other end.
This is where SSL's use of certificates comes in. Certificates are another example of where public/private keys are used. A trusted authority uses private key to sign certificates hash code and anyone can verify that certificate is valid by using it's attach public key against certificates identity data. This way even if attacker takes over your server's IP address, it won't be able to spoof your server's certificate.
I am producing a web service which will allow any third party "device" to communicate with it. Each device has a reasonably unusual string to identify itself and uses the web service to store data against this id. However, this allows someone who wishes to game the service to scan through and guess device ids and store malicious data against them.
The device itself using this web service is relatively "dumb" and doesn't offer a suitable interface for data entry, so a password or any form of entry on the client is not available.
As this web service is open for anyone who wishes to produce a device of this nature to use, I can't increase security with the use a private key as this would be publicly defined in a specification. Also due to the simplistic nature of the device and it's IP/HTTP stack, HTTPS is unsuitable for this implementation.
To the best of my knowledge I can't see a way of using a privately shared key in this operation. To this extent, I believe it be impossible to secure a system of this nature, but I am wondering if some other methods which I've yet to find may help me secure this system somewhat?
Is there a reason you can't use a public/private key pair?
You could publish the server's public key with the specification, and require each device to generate a random public/private key pair for itself. The device could encrypt its public key with the server's public key and send it to the server. The server could use its private key to decrypt the device's public key, and then assume that nobody else could decrypt any message that the server encrypts using that public key. The server therefore registers that public key as the device's ID.
If you set up some kind of session, the server can retain the device's public key associated with that session. If not, some defined portion of any message sent from the device to the server must include the device's public key encrypted this way, so that the server can know which device sent the message.
Any message sent to the server will be encrypted with both the client's private key (so the server knows this device sent it) and the server's public key (so only the server can read it). Messages sent to the device will be encrypted with the server's private key (so the client knows the server sent it) and the client's public key (so only the client can read it). Only you know your private key, and only they know their private key, so everything's secure as long as you (and they) use good seeding and encryption algorithms.
Does that make sense?
You could try some simple challenge-response system, where the server sends a random string of bytes to the device. The device uses some known ID (ideally not the public one) and some hash like algorithm to produce a response.
This will only protect you from people without access to your devices, though.
If "guessing" device IDs is your only concern, make the device IDs unguessable? For instance, use the result of HMAC-SHA256 with a vendor-private key and serial number as the payload. Not even you would need a copy of the vendor's key.
That wouldn't be terribly useful though, as the device's network connection is probably sniffable thus its ID could be trivially captured over the wire. A secret key element must be present on the device, therefore. But this is where I get slightly confused - your concern over using a shared private key seems to be logistical, rather than whether the device can actually support it (implying that it does have some crypto) - yet you can't use HTTPS? What kind of limitations does the device have (memory/CPU/storage)?
Two things come to mind.
First, use a non-guessable device id. Something as simple as a base64 encoded guid would work.
Second you should require that the data being passed to your web service is encrypted using a public key. Your private key would then be required to decrypt the data.
This would ensure that even if they have a listener watching the data come across, they wouldn't be able to grab a different device id. Also, with a non-guessable ID they wouldn't be able to affect other device accounts.