NSec.Cryptography encrypt and decrypt using ChaCha20Poly1305 and SharedSecret - c#

I'm trying to encrypt (and decrypt) messages send from one device to another by using NSec.Cryptography, but I find the documentation a bit vague. As I understood I need a Key and PublicKey of device A and B, respectively. I can turn these into a SharedSecret:
var sharedSecret = KeyAgreementAlgorithm.X25519.Agree(encryption.Key, deviceKey);
However, this shared secret doesn't seem useful for encryption as the Encrypt(...) method asks for a Key in its parameters:
var cyphertext = AeadAlgorithm.ChaCha20Poly1305.Encrypt(sharedSecret, nonce, new byte[0], message);
^-- will not work
I have multiple questions:
What is the use of SharedSecret if it can not be used to encrypt?
How is the ChaCha20Poly1305.Encrypt method useful if it uses one key which can't be a shared secret?
How do I encrypt a message using the private key of A and public key of B (like box and secret box in libsodium)?
Note: I wanna use X25519 keys.

Read the documentation for SharedSecret:
Represents the output of a key agreement and the input for key derivation
So you first need to generate one or more keys using a specific KDF and NSec seems to implement HKDF (using SHA-256 or-512, I'd prefer the latter because it is more secure and - on 64 bit machines - possibly even faster. The shared secret itself is not fully random to an adversary, so a KDF is required to create the most cryptographically secure keys from it.
This should answer 1 & 2: SharedSecret is used to derive the actual keys, it isn't a key in itself.
How do I encrypt a message using the private key of A and public key of B (like box and secret box in libsodium)?
As for 3: You need an ephemeral key pair on the sender A and the public key of receiver B. Then you perform the key agreement & key derivation (as above) using the sender's ephemeral private key and send the ephemeral public key with the ciphertext. Don't forget to destroy the ephemeral private key of the sender after the key agreement; you don't need it anymore, and leaking it will compromise the ciphertext.
The receiver can now perform the same agreement using their static private key and the received ephemeral public key, and finally decrypt the message.

Related

RSA Crypto: Is it require to store keypair after geneating first time

Once public/private key generated then, is this has to store or persist on storage So, when decryption call then same private key (related to public key) is used ?
Basically, there will be two separate endpoints for RSA encrypt and decryption.
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
var pu = rsa.ToXmlString(false);
var pr = rsa.ToXmlString(true);
}
Yes, in almost all cases you need to store the private key permanently. You could use RSA for one-time key establishment. However, creating a key pair is relatively slow for RSA, so RSA is not commonly used for this.
Note that you may also create a key pair where the private key remains in a key container such as a key store. For instance, most smart cards have been designed in such a way that the private key never leaves the device; it is simply used when required. The key is automatically persisted after creation. So you would not be able to retrieve the values of them and convert them to XML.
Storing keys in the Microsoft proprietary XML format without any protection - as performed in the code within the question - is not a good idea.

Identify the recipient(s) KeyId from PGP encrypted data using the BouncyCastle C# library

I have developed a library to perform PGP signing/encryption and decryption/validation of files against one or more recipients. This part works great and works with large files using streams nicely and efficiently.
Part of the PGP Message Exchange Formats specification (RFC 1991) states the following:
...
6.7 User ID Packet
Purpose. A user ID packet identifies a user and is associated with a
public or private key.
Definition. A user ID packet is the concatenation of the following
fields:
(a) packet structure field (2 bytes);
(b) User ID string.
The User ID string may be any string of printable ASCII characters.
However, since the purpose of this packet is to uniquely identify an
individual, the usual practice is for the User ID string to consist of
the user's name followed by an e-mail address for that user, the
latter enclosed in angle brackets.
...
The application I am creating will need to attempt to identify the appropriate key for decrypting the files automatically so that I have as little user intervention as possible. If the key can not be identified (for example, if the recipient(s) are hidden) the application will prompt for the selection of the correct key. I am trying to make it as streamlined as possible.
The RFC suggests the packet is not part of the encrypted data which makes sense. PGP makes it easy to try and identify who encrypted the data. This is evident when you try and decrypt a file using Kleopatra when it has the relevant keys added to its key database. In this instance, it will prompt for the password protecting the secret key.
My specific question is:
How do I use the C# BouncyCastle library to read which recipients the encrypted data was intended to? In otherwords, which private key to use for decryption?
I have tried to find examples using the Bouncy Castle GitHub repository and couldn't see any that demonstrated this particular problem. I also looked at as many google search results for this question to no avail.
I found the answer to my question. I assumed that if it was part of the PGP specification then it must be possible without too much bother. I therefore decided to scrutinise the decryption process and all of the objects used throughout it.
Using the debugger I enumerated the items within the PgpEncryptedDataList and found the key ID for the public key that encrypted it inside the individual PgpPublicKeyEncryptedData object.
The object contains a property of type long called KeyId. This was the value I was looking for to match against the keys stored in the application.
The following snippet is just an example of what I used to reach the KeyId property:
using (var inputFile = File.OpenRead(#"E:\Staging\6114d23c-2595abef\testfile.txt.gpg"))
using (var decoderStream = PgpUtilities.GetDecoderStream(inputFile))
{
var objectFactory = new PgpObjectFactory(decoderStream);
var encryptedList = (PgpEncryptedDataList)objectFactory.NextPgpObject();
foreach (var encryptedData in encryptedList.GetEncryptedDataObjects().Cast<PgpPublicKeyEncryptedData>())
{
var keyId = encryptedData.KeyId.ToString("X");
Console.WriteLine($"Encryption Key ID: {keyId}");
}
}
Setting a breakpoint after the first enumeration you can examine the encryptedData variable and observe something similar to:
So, after all the struggle, it was actually very simple. Accessing the KeyId during the decryption process is then straightforward and you can automagically go and grab the correct private key to do the decryption.
For completeness, it is common for PGP that files are encrypted for more than just one recipient. In this case, you will see more than one encrypted data object. It doesn't mean the data is encrypted twice. Only the session key. The session key is encrypted N number of times where N is the number of recipients. This allows each recipient to be able to decrypt one of the sessions keys and then to go ahead and decrypt the data.
Refer to the image below showing two objects, and as you would expect, two KeyId properties :)
This snippet is from the PgpDecrypt.cs which already looks through the encrypted objects and checks the key identifier against the PgpSecretKeyRingBundle passed in as a parameter:
foreach (PgpPublicKeyEncryptedData pked in encryptedDataList.GetEncryptedDataObjects())
{
privateKey = PgpKeyHelper.FindSecretKey(secretKeyRing, pked.KeyId, passPhrase.ToCharArray());
if (privateKey == null)
{
continue;
}
encryptedData = pked;
break;
}
For anyone wishing to have a head start with PGP, BouncyCastle and C#, please refer to my library which contains a compilation of many PGP functions. The PgpDecrypt class can be changed to automatically incorporate the key discovery as discussed in this question.

Private key encryption in .NET 4.0 of a very large number

In my scenario, I would like to encrypt a very big number (10^27) using a private key and later be able to decrypt it using a public key. The problem I have is that I want to keep the size of the encrypted text as small as possible.
I know that .NET has support for public key encryption (RSACryptoServiceProvider), but the encrypted text gets so huge.
Would it work to instead treat the private key as a public key?
Would Elliptic curve cryptography produce a smaller output?
First of all, if you want to achieve confidentiality you should always encrypt with the public key, not the private key. RSA encryption is not defined for encryption with the private key, and the results may vary (especially the kind of padding that is applied).
For direct RSA encryption, the size of the encrypted message is identical to the modulus. Now the modulus should be at least 2048 bits by now, and your message is only about (27/3)*10=90 bits. So RSA would have a large overhead, independent on the key used. Using ECIES is therefore likely to give significant benefits.

Is this enough for signing data?

Searching for how signing is done I've come across some fairly elaborate code samples. But the following code seems to be enough. Is there something missing here like a salt for example, or are salts unnecessary when just signing? I'm not encrypting, just signing.
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
byte[] data = Encoding.ASCII.GetBytes("hello");
byte[] signature = rsa.SignData(data, "SHA1");
byte[] dataTest = Encoding.ASCII.GetBytes("hello");
bool verified = rsa.VerifyData(dataTest, "SHA1", signature);
if (verified) Text = "True"; else Text = "Untrue";
Are salts unnecessary when just signing?
Salting is necessary if your task is to prevent precomputation of hashes of known messages where the hash is being used as a shared secret. If that's not your application then there is no need to salt.
If you do not understand why you need a salt, see my series of articles on that topic:
http://blogs.msdn.com/b/ericlippert/archive/tags/salt/
Is there something missing here?
Yes, the most important step is missing. How are you going to communicate the public key? The security of the whole system relies upon that step, which you have not even mentioned.
Suppose Alice wishes to send a message to Bob and Bob wishes to verify that it came from Alice. They do the following:
Alice creates a key pair and securely stores the private key.
Alice publishes the public key.
Bob obtains Alice's public key.
Alice publishes a message.
Alice hashes the message and encrypts the hash with her private key.
Bob reads the message.
Bob reads the encrypted hash.
Bob decrypts the encrypted hash with Alice's public key.
Bob hashes the message.
Bob compares the decrypted hash to the message hash. If they match, then Bob knows that the message was vouched for by Alice.
Is this correct?
No. The conclusion is incorrect. The conclusion should be:
Bob compares the decrypted hash to the message hash. If they match, then Bob knows that the message was vouched for by someone who possessed the private key that matches the public key that Bob believes is Alice's public key.
The original conclusion is only correct if Bob has additional evidence that he has Alice's public key. Because Bob could be in this situation:
Alice creates a key pair and securely stores the private key.
Mallory creates a key pair and security stores the private key.
Alice publishes the public key.
Mallory intercepts Alice's publication and replaces Alice's public key with Mallory's public key.
Bob obtains Mallory's public key but believes it is Alice's.
And now the whole thing has gone to hell. Mallory can now publish messages that Bob believes come from Alice, and Alice cannot!
You have to say how you are going to securely publish the public key. The entire system relies on two things: that the private keys stay private, and that there is some mechanism by which public keys can be correctly associated with their owners.

Sending messages between two clients, how to verify the identity of the sender?

So assume you have two clients, C1 and C2, each client has a GUID associated with it.
How do you, when you receive a message on C2 that supposedly comes from C1 (by checking the GUID and seeing that it matches the GUID of C1), but since the message is not guaranteed to have come from C1 (C3 might just have sent the message, sending the GUID of C1 in the message header) there has to be some verification that the message actually came from C1.
I've been looking into using asymmetric encryption (RSA) to have C1 send a message that consists of [C1.GUID; RSAEncrypt(C2.PUBLIC_KEY, C1.GUID); MESSAGE], and then let C2 basically do a check like this (python pseudo code):
message.GUID == RSADecrypt(C2.PRIVATE_KEY, message.ENCRYPTED_GUID)
Is this a viable approach? Or is there some other clever/more obvious way to verify the sender of a message?
Assymmetric Algorithms have been invented for such purposes, that's the way digital signatures work.
However, your approach has some problems. Anyone with the public key of the recipient could fake the signature. Also, the signature does not change at all! Anyone intercepting the messages can fake being a valid sender. The purpose of assymetric encryption is to defeat these problems with key exchanges, there's the concept of the digital signature, which is basically an assymetrically encrypted hash of the message you are tossing around.
For RSA, you need to do a bit more in order to create a digital signature from the basic algorithm, see wikipedia for more details: http://en.wikipedia.org/wiki/RSA#Signing_messages
I'd just use a digital signature algorithm from a library. First google search turns up with this for Python:
http://www.example-code.com/python/pythonrsa.asp
http://www.chilkatsoft.com/dsa-python.asp
The problem with this method is that any machine could then capture the guid and rsa-encrypted-guid and pass them just the same. You haven't really created any unique challenge/response criteria that can only be divined by the receiving client. What you would need would be something that is completely unique and can't be gotten simply by looking at passed parameters. Maybe something like:
[ClientName; RSA-ENCRYPTED(GUID+Timestamp); MESSAGE]
In this method, the RSA encryption would be done using Client2's public key so that only Client2's private key could unlock it. Using the ClientName, Client2 could retrieve the expected GUID from a datasource, and then match the returned GUID against the one in the encryption. I incorporated the usage of a timestamp as a salt so that the encrypted string comes out differently each time. It's considered very weak to use a timestamp as a randomization for a salt, but it gets the point across. Other more secure/random algorithms could be implemented.
Anyone spying messages between a client and the server will be able to forge new messages, has client's GUID never change, nor RSA-ENCRYPTED-GUID.
Consider switching to this message model : [GUID; ENCRYPTED_CONTENT_CHECKSUM; CONTENT].
Checksum(message.CONTENT) ==
RSADescrypt(C1.PUBLIC_KEY, message.ENCRYPTED_CONTENT_CHECKSUM)
Still, anyone spying messages can resend previously sent messages.
Public and private keys are the way to go. I will assume you don't care about encrypting the data, but you do care that the data is "authorized".
Lets say you have 3 computers
Comp1
Comp2
Comp3
Lets also say you want Comp1 to send a message to Comp3. you don't care if the message was intercepted, but you do care that it was not forged.
Comp1 will digitally sign the message with its private key
Comp2 will intercept the message from Comp1 to Comp3, but can't change the message without invalidating the signature
Comp2 will forward the message onto Comp3
Comp3 will use Comp1's public key to decrypt the signature and use the hash in the signature to validate the contents.
Now if you want to encrypt the data, you need to add an extra step
Comp1 will digitally sign the message with its private key
Comp1 will generate an random encryption key(typically AES) and encrypt the message.
Comp1 will take that encryption key and encrypt it with Comp3's public key
Comp2 will intercept the message, but cannot read it without Comp3's private key
Comp2 will forward the message onto Comp3
Comp3 will use it's private key to decrypt the AES key
Comp3 will decrypt the entire message using the AES key
Comp3 will validate the message by decrypting the signature with Comp1's public key.
Signature contains a hash of the message, if the hash and the message's hash match, then the data is intact.
You can include the GUIDs in the payload to use as a lookup to decide which public keys to use.
P.S. You will want to use the built in methods to sign a message. Let the Framework do the hashing/etc

Categories