initial commit

This commit is contained in:
Niklas Birk 2022-06-01 17:52:31 +02:00
commit 12127e5873
7 changed files with 241 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
### IntelliJ IDEA ###
out/
/.idea/
!out/artifacts/fermat_rsa_attack_jar
!**/src/main/**/out/
!**/src/test/**/out/
fermat_rsa_attack.iml

30
README.md Normal file
View File

@ -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.**

109
src/FermatRSAAttack.java Normal file
View File

@ -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}
* <br /><br />
* 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}.
* <br /><br />
* 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.
* <br /><br />
* 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.
* <br /><br />
* 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()));
}
}
}

3
src/META-INF/MANIFEST.MF Normal file
View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: Main

26
src/Main.java Normal file
View File

@ -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()));
}
}

View File

@ -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);
});
}
}