1

Here I read about the basic idea of how to use asymmetric Elliptic Curve encryption to create a product key, here is a quote:

enter image description here

My question is how is it possible to create a product key by a 128-bit Elliptic Curve, since if you encrypt X-length text you get encryption that is much longer than X?

Below is the test code I wrote, an 8-character message encrypted to an array of 28 bytes, what am I wrong with, or is it not at all possible to use the Elliptic Curve to create a 25-character product key?

using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using System.Text;
using System.Linq;
using Org.BouncyCastle.Crypto.Engines;
using System.Diagnostics;
using Org.BouncyCastle.Crypto.Agreement;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;

class Program
{
    public static void Main(string[] args)
    {
        var keyPairGenerator = new ECKeyPairGenerator("ECDH");
        var x9 = ECNamedCurveTable.GetByName("secp128r1");
        keyPairGenerator.Init(new ECKeyGenerationParameters(new ECDomainParameters(x9), new SecureRandom()));
        var keyPair = keyPairGenerator.GenerateKeyPair();

        byte[] d = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
        byte[] e = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 };
        var p = new IesWithCipherParameters(d, e, 64, 128);

        var cipher = new IesEngine(
                       new ECDHBasicAgreement(),
                       new Kdf2BytesGenerator(new Sha1Digest()),
                       new HMac(new Sha1Digest()));

        var message = Encoding.UTF8.GetBytes("12345678");

        cipher.Init(true, keyPair.Private, keyPair.Public, p);
        var encryption = cipher.ProcessBlock(message, 0, message.Length); // encryption length is 28 <==

        cipher.Init(false, keyPair.Private, keyPair.Public, p);
        var decryption = cipher.ProcessBlock(encryption, 0, encryption.Length);

        Debug.Assert(decryption.SequenceEqual(message));
    }
}
codeDom
  • 1,623
  • 18
  • 54
  • 4
    Given that the example uses MD5 and specifies direct EC encryption (ElGamal?, if not, how?) I would say that is a pretty shit protocol. Also 128 bit EC is not secure. – Maarten Bodewes Jan 02 '22 at 21:34
  • @MaartenBodewes forgot to say to use ECIES instead of ECC encryption. [That has an encoding issue](https://crypto.stackexchange.com/q/76340/18298) – kelalaka Jan 02 '22 at 22:38
  • Yeah, but ECIES will require additional data, whereas the example indicates that the user has to enter 25 characters, not more. – Maarten Bodewes Jan 02 '22 at 22:46
  • 1
    It's not completely clear but it appears that the linked answer is actually proposing signatures rather than encryption, but the answer author doesn't realize it. The author talks about "encrypting" with the private key on the server and "decrypting" with the public key on the client. – President James K. Polk Jan 02 '22 at 23:13
  • You did not define a cipher to encrypt. Therefore it uses [streaming mode](https://github.com/bcgit/bc-java/blob/bc3b92f1f0e78b82e2584c5fb4b226a13e7f8b3b/core/src/main/java/org/bouncycastle/crypto/engines/IESEngine.java#L178) and there is MAC to consider the size. – kelalaka Jan 09 '22 at 20:13
  • @kelalaka Can you please write an answer with a code that works and get a bounty? – codeDom Jan 11 '22 at 10:29
  • @googledev I don't have C# environment as of nowadays, I'm here only to help. It is interesting that C#' coders did not get this bounty up to now :) You don't need ECC encryption, a ECDSA will be enough. Sign the hash then user side can verify that the signature is valid. – kelalaka Jan 11 '22 at 10:40

2 Answers2

1

It is not true that "if you encrypt X-length text you get encryption that is much longer than X", but the encryption proceeds in blocks.

The basic operation of 128-bit EC encryption is to encrypt a 128-bit plaintext block to produce a 128-bit ciphertext block -- exactly the same size.

If you want to encrypt something smaller, it needs to be rounded up to a whole number of blocks, which is why your very short message got a lot longer.

There is also a bunch of extra stuff that is often added to the message:

  • Encryption modes like CBC require a block of random data at the start.
  • A message authentication code (HMAC) is often added for tamper prevention.
  • Padding is added to expand the message out to a whole number of blocks, and this must include extra information to indicate the original message size. If your message is exactly one block long, this padding with extra information will consume a whole additional block.

For your application, you don't need any of this extra stuff. You get 128 bits from MD5, and you should encrypt exactly and only that block of 128 plaintext bits to produce a block of 128 ciphertext bits.

I'm not sure how you construct one of your bouncycastle IesEngine instances the right way. But in terms of the usual parameters for block ciphers:

  • Usually you have to specify a mode. ECB is the correct choice. It stands for "electronic code book", because the language is ancient, and means "just encrypt the blocks".
  • Usually you have to specify the type of padding to use. You should specify NO PADDING.
  • Usually you have to configure what kind of authentication code to use. You seem to have specified HMAC. The correct value is NO AUTHENTICATION CODE.

If you set it up right, then you can encrypt a 16-byte (128-bit) message to produce a 16-byte output.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
-1

Rather than saying "encryption", the right way to do this is by using a signature.

Use a hash function on some input, which both the client and server can calculate without needing to transmit anything. Then the server can sign that hash, and the client can verify the signature.

128-bit ECC signatures are useful here, because they are only 128-bits long. So it's plausible to encode the entire value as a text string that the user can type. Plus without knowledge of the secret key, it should be "impossible" (or at least without boiling the oceans...) for an attacker to generate fake keys.

However in the age of always online internet connections, do you want to annoy your potential customers like this? They have to type in exactly the same input into your online order page as the product activation screen. Which can be rather frustrating to use and support.

Plus there's no mechanism to prevent multiple installations using the same key. So any software pirate will either crack the software to remove your activation page, or buy one product key and distribute it.

IMHO any simple obfuscation should be sufficient as a product key. Since any attacker with the skills to break it will probably remove it anyway. For more security you should be performing some online check. Or just trust your legitimate users to do the right thing.

Jeremy Lakeman
  • 9,515
  • 25
  • 29
  • The question was about how to perform asymmetric encryption, Symmetric algorithms are limited, in that any novice cracker with a disassembler can find your key (or the algorithm used to generate one) and make a "keygen". – codeDom Jan 10 '22 at 12:33
  • Yes, and signing is an asymmetric system. You don't need the client to "decrypt" anything, you only need them to verify that a value is valid. I don't see how you thought I was advocating for a symmetric system. Though I am saying that once you assume an attacker can modify your code, the complexity of your product key is irrelevant. And even if they can't, just replicating a valid key is sufficient if there is no online test being performed. – Jeremy Lakeman Jan 11 '22 at 05:20
  • Can you please add code that works to your answer and get bounty? – codeDom Jan 11 '22 at 10:30