From 12127e5873dfbe948f0d537e4d64c0a217f47d96 Mon Sep 17 00:00:00 2001 From: Niklas Date: Wed, 1 Jun 2022 17:52:31 +0200 Subject: [PATCH] initial commit --- .gitignore | 9 ++ README.md | 30 +++++ .../fermat_rsa_attack.jar | Bin 0 -> 4230 bytes src/FermatRSAAttack.java | 109 ++++++++++++++++++ src/META-INF/MANIFEST.MF | 3 + src/Main.java | 26 +++++ test/FermatRSAAttackTest.java | 64 ++++++++++ 7 files changed, 241 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 out/artifacts/fermat_rsa_attack_jar/fermat_rsa_attack.jar create mode 100644 src/FermatRSAAttack.java create mode 100644 src/META-INF/MANIFEST.MF create mode 100644 src/Main.java create mode 100644 test/FermatRSAAttackTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4e62e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +### IntelliJ IDEA ### +out/ +/.idea/ + +!out/artifacts/fermat_rsa_attack_jar +!**/src/main/**/out/ +!**/src/test/**/out/ + +fermat_rsa_attack.iml diff --git a/README.md b/README.md new file mode 100644 index 0000000..c1d024e --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# Fermat Attack on RSA + +This is just a little ugly program to +attack on RSA modul with an approach of Fermat. + +Use: `java -jar fermat_rsa_attack.jar [MODUL] [TRIES]` + +where MODUL is the RSA-modul (generated from both keys you want to evaluate) +and TRIES the amount of iterations to avoid long runtime. +TRIES is optional. If not given the default amount of trie is 100. +If there is no solution found within the tries then give a bigger number up to +approx. 2 billion (integer 32). + +--- +The modul `N` evaluates from product of two prime numbers (except 2): +`N = p1 * p2` + +For the two prime numbers exists `a` and `b` with `p1 = a-b` and `p2 = a+b`. +So `N` can be evaluated as `N = (a-b)(a+b) = a^2 - b^2`. + +The number `a` must lie in between `p1` and `p2` with equal distance `b`. +A good guess for such an `a` is the square root of `N` as a starting point. +In each iteration increase `a` by 1 until a solution is found or the amount of tries is exhausted. + +In the iteration itself calculate `b^2 = a^2 - N` then check if `b` is non-negative and a square number. +If so the solution is found. If not then increase `a` by 1 and try again. +--- +**This method to calculate `p1` and `p2` for RSA-keys is a good method if the two prime numbers +are close together. However, if they are not close it takes long. +So RSA with good key generators are still strong.** \ No newline at end of file diff --git a/out/artifacts/fermat_rsa_attack_jar/fermat_rsa_attack.jar b/out/artifacts/fermat_rsa_attack_jar/fermat_rsa_attack.jar new file mode 100644 index 0000000000000000000000000000000000000000..126788f48b12d246ab53608e2bac156a074b47c2 GIT binary patch literal 4230 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
+ * For the two prime numbers exists {@code a} and {@code b} with {@code p1 = a-b} and {@code p2 = a+b}. + * So {@code N} can be evaluated as {@code N = (a-b)(a+b) = a^2 - b^2}. + *

+ * The number {@code a} must lie in between {@code p1} and {@code p2} with equal distance {@code b}. + * A good guess for such an {@code a} is the square root of {@code N} as a starting point. + * In each iteration increase {@code a} by 1 until a solution is found or the amount of tries is exhausted. + *

+ * In the iteration itself calculate {@code b^2 = a^2 - N} then check if {@code b} is non-negative and a square number. + * If so the solution is found. If not then increase {@code a} by 1 and try again. + *

+ * This method to calculate {@code p1} and {@code p2} for RSA-keys is a good method if the two prime numbers + * are close together. However, if they are not close it takes long. + * So RSA with good key generators are still strong. + * + * @param tries the amount of iterations the method should trey before stop to avoid long duration + * @return a {@link KeyPair} with both prime numbers which generated the modul. + */ + public KeyPair attack(final int tries) + { + final var possibleA = this.MODUL.sqrt().add(BigInteger.ONE); + + for (var i = 0; i < tries; i++) + { + final var refinement = BigInteger.valueOf(i); + + final var aSquared = possibleA.add(refinement).pow(2); + final var bSquared = aSquared.subtract(this.MODUL); + + if (!isNegative(bSquared) && isSquareNumber(bSquared)) + { + this.iterationsCount = ++i; + + final var a = aSquared.sqrt(); + final var b = bSquared.sqrt(); + + return new KeyPair(a.subtract(b), a.add(b)); + } + } + + throw new OutOfTriesException(); + } + + public int getIterationsCount() + { + return this.iterationsCount; + } + + public BigInteger getMODUL() + { + return MODUL; + } + + private boolean isSquareNumber(final BigInteger number) + { + return number.sqrt().pow(2).equals(number); + } + + private boolean isNegative(final BigInteger number) + { + return number.compareTo(BigInteger.ZERO) < 0; + } + + public record KeyPair(BigInteger p1, BigInteger p2) + { + @Override + public String toString() + { + return "p1 = %d, p2 = %d".formatted(p1, p2); + } + } + + public class OutOfTriesException extends IndexOutOfBoundsException + { + public OutOfTriesException() + { + super(""" + Your given amount of tries are too few to fully compute the solution. + Iterations given and needed: %d""" + .formatted(getIterationsCount())); + } + } +} diff --git a/src/META-INF/MANIFEST.MF b/src/META-INF/MANIFEST.MF new file mode 100644 index 0000000..37197ef --- /dev/null +++ b/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: Main + diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..c533312 --- /dev/null +++ b/src/Main.java @@ -0,0 +1,26 @@ +import java.math.BigInteger; + +public class Main +{ + public static void main(final String[] args) + { + final var key = BigInteger.valueOf(Long.parseLong(args[0])); + var tries = 100; + + try + { + tries = Integer.parseInt(args[1]); + } + catch (Exception ignored) + { + } + + final var fermatRSAAttack = new FermatRSAAttack(key); + final var primes = fermatRSAAttack.attack(tries); + + System.out.printf(""" + Key-pair to given MODUL %d: %s + Evaluated in %d tries. + """.formatted(fermatRSAAttack.getMODUL(), primes, fermatRSAAttack.getIterationsCount())); + } +} diff --git a/test/FermatRSAAttackTest.java b/test/FermatRSAAttackTest.java new file mode 100644 index 0000000..ef50596 --- /dev/null +++ b/test/FermatRSAAttackTest.java @@ -0,0 +1,64 @@ +import org.junit.jupiter.api.*; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.*; + +public class FermatRSAAttackTest +{ + @Test + public void testExpectExceptionForNegativeNumber() + { + assertThrows(IllegalArgumentException.class, () -> new FermatRSAAttack(BigInteger.valueOf(-1))); + } + + @Test + public void testObjectCorrectInitialized() + { + final var actual = assertDoesNotThrow(() -> new FermatRSAAttack(BigInteger.valueOf(2))); + + final var expectedModul = BigInteger.valueOf(2); + final var expectedIterationsCount = 0; + + assertEquals(expectedModul, actual.getMODUL()); + assertEquals(expectedIterationsCount, actual.getIterationsCount()); + } + + @Test + public void testObjectCorrectInitializedWithLong() + { + final var actual = assertDoesNotThrow(() -> new FermatRSAAttack(2)); + + final var expectedKey = BigInteger.valueOf(2); + final var expectedIterationsCount = 0; + + assertEquals(expectedKey, actual.getMODUL()); + assertEquals(expectedIterationsCount, actual.getIterationsCount()); + } + + @Test + public void testFermatRSAAttack() + { + final var expectedP1 = 3; + final var expectedP2 = 5; + final var expectedIterationsCount = 1; + + final var modul = expectedP1 * expectedP2; + + final var fermatRSAAttack = new FermatRSAAttack(modul); + final var actualPrimes = fermatRSAAttack.attack(1_000_000); + + assertEquals(expectedP1, actualPrimes.p1().longValue()); + assertEquals(expectedP2, actualPrimes.p2().longValue()); + assertEquals(expectedIterationsCount, fermatRSAAttack.getIterationsCount()); + } + + @Test + public void testFermatRSAAttackWithTooFewTries() + { + assertThrows(FermatRSAAttack.OutOfTriesException.class, () -> { + final var fermatRSAAttack = new FermatRSAAttack(10); + fermatRSAAttack.attack(1); + }); + } +} \ No newline at end of file