commit 12127e5873dfbe948f0d537e4d64c0a217f47d96 Author: Niklas Date: Wed Jun 1 17:52:31 2022 +0200 initial commit 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 0000000..126788f Binary files /dev/null and b/out/artifacts/fermat_rsa_attack_jar/fermat_rsa_attack.jar differ diff --git a/src/FermatRSAAttack.java b/src/FermatRSAAttack.java new file mode 100644 index 0000000..ce02c34 --- /dev/null +++ b/src/FermatRSAAttack.java @@ -0,0 +1,109 @@ +import java.math.BigInteger; + +public class FermatRSAAttack +{ + private final BigInteger MODUL; + + private int iterationsCount; + + public FermatRSAAttack(final long rsaModul) + { + this(BigInteger.valueOf(rsaModul)); + } + + public FermatRSAAttack(final BigInteger rsaModul) + { + if (isNegative(rsaModul)) + { + throw new IllegalArgumentException("The provided RSA-Modul must be non-negative!"); + } + + this.MODUL = rsaModul; + } + + /** + * The modul {@code N} evaluates from product of two prime numbers (except 2): + * {@code N = p1 * p2} + *

+ * 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