initial commit
This commit is contained in:
commit
12127e5873
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal 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
30
README.md
Normal 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.**
|
BIN
out/artifacts/fermat_rsa_attack_jar/fermat_rsa_attack.jar
Normal file
BIN
out/artifacts/fermat_rsa_attack_jar/fermat_rsa_attack.jar
Normal file
Binary file not shown.
109
src/FermatRSAAttack.java
Normal file
109
src/FermatRSAAttack.java
Normal 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
3
src/META-INF/MANIFEST.MF
Normal file
@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: Main
|
||||
|
26
src/Main.java
Normal file
26
src/Main.java
Normal 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()));
|
||||
}
|
||||
}
|
64
test/FermatRSAAttackTest.java
Normal file
64
test/FermatRSAAttackTest.java
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user