william-ferguson-au / asymmetric-crypto

Provides a very simple library that uses public key crypto to encrypt any size data using a randomly generated symmetric key
GNU General Public License v3.0
17 stars 8 forks source link

Example does not match code, and public/private keys seem reversed #4

Open cabintech opened 7 months ago

cabintech commented 7 months ago

I know this is an old project, but still seems really useful, however...

The example on the README page is wrong in several ways...

  1. It uses a class named RandomSymmetricCipher that does not exist in the project (should be AsymmetricCipher??). The name RandomSymmetricCipher appears on several comments in the code, but there is no such class.

  2. The encrypt() method shows it taking a PUBLIC key, which seems correct, the LOCAL must encrypt the symmetric key using the partners PUBLIC key. But the code in AsymmetricCiper.encrypt() takes a PRIVATE key and fails if given a public key. This does not make sense, the encryption needs a PUBLIC key, so the receiver can use their PRIVATE key to decrypt. The example seems correct, the code seems wrong.

Also, just a thought, all the methods in CryptoPacketConverter could be static and save all the bother of creating instances of that class.

cabintech commented 7 months ago

PS. I modified the code to take a public key on encrypt() and a private key on decrypt(). It works with this sample:

public class TestClientServerCipher {

private static final String SERVER_PUBLIC_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn5sC9bfmEvqW+cjmor2QrhgL6/wfajfSRnT6Q5wbPinhi3tUtBrCgUjeoWXOHmBxrIDMa10wU56syOnb1S58I1Z8+qeLwJMql/zM96mHdvPCucS0L+WaQK4TAAPwBlBT6RyAUlw6r7CMavwdbCAFJ2CJDuKXLDdaZlOUCVR/jw9v5GSrOk4ZtGjM4A38T99X8iuC5yxx8jINXthe7SZEbK4hOVOlTxPoTRbLclNDrnINYV8FGl4rTUlgHFnyXq2xX49xTJ+UztDAEQMv7fTzMD72RaeDtnzkOPrfr2eh8c+Y7hBuLn7yFBwo46kQh67XIMVo/k81zF5o7dy4VqY09QIDAQAB";
private static final String SERVER_PRIVATE_KEY = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCfmwL1t+YS+pb5yOaivZCuGAvr/B9qN9JGdPpDnBs+KeGLe1S0GsKBSN6hZc4eYHGsgMxrXTBTnqzI6dvVLnwjVnz6p4vAkyqX/Mz3qYd288K5xLQv5ZpArhMAA/AGUFPpHIBSXDqvsIxq/B1sIAUnYIkO4pcsN1pmU5QJVH+PD2/kZKs6Thm0aMzgDfxP31fyK4LnLHHyMg1e2F7tJkRsriE5U6VPE+hNFstyU0Oucg1hXwUaXitNSWAcWfJerbFfj3FMn5TO0MARAy/t9PMwPvZFp4O2fOQ4+t+vZ6Hxz5juEG4ufvIUHCjjqRCHrtcgxWj+TzXMXmjt3LhWpjT1AgMBAAECggEABLodk1N3//XmBM3bbtR4dX+G0t1rvMSceLO8OcZk1Cl/YZcsgg0M17FlzpfqS+IFW9uQsllXNhLMAUNT3HN8/7rvBqlxrSluqIO3OlqOBYrgqInX4FdHg0TkkSqMsFMuuQe8ETRkFJIBBqYi5PHp1Sm5xXQWkBuvlLpC9yfGg2EgsLDnHrANWVBgeqj2xilMbqV+Emjityfl9S5bee+7O4gsxnH/LKlVSgt5CGFbxKfl6qp9JO7ZKsxsI0URN4Z+BHV9jkqdGw+f9EYjuYHGDkYG0AlPEgbEgdbti/zQo5YXe1+ZaLxEWzDPcMI4ANSTLYZD+PF5DExbnFaCAQyz/QKBgQDNMqJmT4K7MekSRKKBh4AutmgQEkH+vUfU2RLPZUuErT98JTqPD8CIQZPTY+R4yevs+FdaPefJoApNEkrcmQ3WMmOTehIVCgghybs9hVt/OPMeRq3CYTV6GPSgLsuHcU2AFi2fqQ+EZ9XHgw6VxHVyBgUPIIts6ilXPaRBSjgOEwKBgQDHHsEbbPKq9Uez4DVY3siYbLRzPYLEcinG9EEwAMVcRHTNnntDh+44sRDfi0ghA/U8KjJLDYbazI8CMqd8kZDhg34qFYfoROKZR6oS5n1JOiduNhcFWDOxRBrnaRXjUZcwyTxB4attN4eVRGx7Jtt81Ow0g34lw9GirSvj6rRx1wKBgQCPDz0v7UEst3hSNy3A9rJEXwGgKsp+CoLY/1pg47NJ1euL5vyznvzRQj8Iwv0XxVdJyD5GeSBvbref0iAFOmPuMQVBXXrb1jmjLNzw4FfRE+hT9FzSRymcbWengdh+Hl007sFyLdDWs/nUdiel/OkQzOecC0mJbpvR6fTpXBBGpwKBgQCXjX/a+PRGTIPzk23nI0G4SdyabbHPBTlo9VV8TMnqbSwdqEcXYildut8l1ePjRRjtXwyCdVwzwkgCmz1BIaYNDsEDFPopPezeT15SHl4zUvDaAI9LARPvEvALO79uEhaqCjAz0Khia0ZlxPtwTGX4QDM70O09nyo6z+U9BtGqZwKBgQCJV9EkXX+USkR4fVU4tdaPAUd+S8NYzKB5nUJzdvn2SjqNVU1ky7qmI8zO3JAYnKFVdXWzfu7a51Ol4IlJQxM9IZuRnm9VsRxdeuysMqlcaYJnzmxalqzbSrusZfLEztVm0JEmbMl8dBH1fKW/QYLwk7rx3rzU6K/MLPtxRGFbNg==";

private static final String TEST_DATA = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Etiam non quam lacus suspendisse faucibus interdum posuere lorem. Elementum pulvinar etiam non quam. Lectus magna fringilla urna porttitor. Suspendisse ultrices gravida dictum fusce ut placerat orci. Amet massa vitae tortor condimentum lacinia quis. Pulvinar etiam non quam lacus suspendisse. Lacus vel facilisis volutpat est. Amet consectetur adipiscing elit ut aliquam. Vivamus at augue eget arcu dictum varius duis. Orci a scelerisque purus semper eget duis. Velit egestas dui id ornare arcu odio ut sem nulla. Nulla aliquet porttitor lacus luctus. Neque viverra justo nec ultrices dui sapien eget mi proin. Diam donec adipiscing tristique risus nec feugiat in. "
        + "Tempor orci eu lobortis elementum nibh tellus molestie nunc non. Fusce id velit ut tortor pretium viverra suspendisse. Erat velit scelerisque in dictum non. Scelerisque eleifend donec pretium vulputate sapien. Nullam ac tortor vitae purus faucibus. Morbi blandit cursus risus at ultrices mi tempus imperdiet nulla. Consectetur lorem donec massa sapien. Iaculis urna id volutpat lacus laoreet. Ipsum suspendisse ultrices gravida dictum fusce ut placerat orci nulla. Amet mauris commodo quis imperdiet massa. Ipsum a arcu cursus vitae congue mauris rhoncus aenean. Dignissim suspendisse in est ante in. Est ultricies integer quis auctor elit. Et magnis dis parturient montes. Porttitor rhoncus dolor purus non enim praesent elementum facilisis. Nulla facilisi etiam dignissim diam quis enim. Penatibus et magnis dis parturient. Nullam ac tortor vitae purus faucibus. Amet tellus cras adipiscing enim.";

public static void main(String[] args) {
    try {

        System.out.println("INPUT : "+TEST_DATA);
        // ------------------ CLIENT

        // Encrypt the data with a random (symmetric key). Then encrypt that key with the server public key.
        // Both together is a CryptoPacket to be sent to the server.

        AsymmetricCipher cipher1 = new AsymmetricCipher();

        CryptoPacket cp = cipher1.encrypt(TEST_DATA.getBytes(StandardCharsets.UTF_8), SERVER_PUBLIC_KEY);
        String packetBase64 = CryptoPacketConverter.convert(cp);

        //... send packetBase64 to the server ...

        // ------------------ SERVER

        // Get the client's random key be decrypting (it was encrypted with our public key). Then use that
        // random key to decrypt the main message. 

        AsymmetricCipher cipher2 = new AsymmetricCipher();
        byte[] decryptedData = cipher2.decrypt(CryptoPacketConverter.convert(packetBase64), SERVER_PRIVATE_KEY);

        System.out.println("OUTPUT: "+new String(decryptedData, StandardCharsets.UTF_8));

        // Show the data transfered from client to server
        System.out.println("Transfer packet:\n"+packetBase64);

    }
    catch (Throwable t) {
        t.printStackTrace(System.out);
    }
}
william-ferguson-au commented 7 months ago

Hi @cabintech

It's been 10 years since I last looked at this.

Yes RandomSymmetricCipher was renamed to AsymmetricCipher 10 years ago https://github.com/william-ferguson-au/asymmetric-crypto/commit/501753193c170fd922845ad60cd9e654bfe4d2c6

I think the example is incorrect and the code is correct. See AssymetrciCipherTest

While you could make the methods on CryptoPacketConverter static as it holds no mutable state, I have found that as soon as you do that you invariably get a requirement that needs the class to have state or be polymorphic, so I always default to nice clean and simple classes.

cabintech commented 7 months ago

I am no crpto expert, but everything I read says with an asymmetric cipher you should encrypt with the public key and decrypt with the private key. Doing the reverse is insecure because the public key is much shorter. Only digital signature applications encrypt with a private key (not sure why).

If I am going to send an encrypted message to someone, I should only need their public key (see sample code above, the client uses the server's public key). Doing it reverse means the private key has to be distributed.

Sorry I know this is old :-). I have reversed the usage for my purposes, and thanks for building this, saved me lots of time to figure out all the little details.

william-ferguson-au commented 7 months ago

You are correct that encrypting with a private key is less secure, but

  1. An attacker doesn't know that you have used a private key rather than a public key so they still need to search the entire space.
  2. You may not have the public key of the person you are sending to, but they may have your public key (because you published it).

But otherwise I agree. In a perfect world everyone would have a published public key and all comms would be encrypted using a private key.

NB the reasons that digital sigs use a private key is that it means that someone can prove that it was you that signed something by using your public key to decrypt.

In truth I can no longer remember the need that drove the creation of this library. I may well have need to digitally sign something.