mirror of
https://github.com/TheAlgorithms/Java.git
synced 2025-07-05 08:17:33 +08:00
237 lines
8.7 KiB
Java
237 lines
8.7 KiB
Java
package com.thealgorithms.ciphers;
|
|
|
|
import java.math.BigInteger;
|
|
import java.security.SecureRandom;
|
|
|
|
/**
|
|
* ECC - Elliptic Curve Cryptography
|
|
* Elliptic Curve Cryptography is a public-key cryptography method that uses the algebraic structure of
|
|
* elliptic curves over finite fields. ECC provides a higher level of security with smaller key sizes compared
|
|
* to other public-key methods like RSA, making it particularly suitable for environments where computational
|
|
* resources are limited, such as mobile devices and embedded systems.
|
|
*
|
|
* This class implements elliptic curve cryptography, providing encryption and decryption
|
|
* functionalities based on public and private key pairs.
|
|
*
|
|
* @author xuyang
|
|
*/
|
|
public class ECC {
|
|
|
|
private BigInteger privateKey; // Private key used for decryption
|
|
private ECPoint publicKey; // Public key used for encryption
|
|
private EllipticCurve curve; // Elliptic curve used in cryptography
|
|
private ECPoint basePoint; // Base point G on the elliptic curve
|
|
|
|
public ECC(int bits) {
|
|
generateKeys(bits); // Generates public-private key pair
|
|
}
|
|
|
|
public EllipticCurve getCurve() {
|
|
return curve; // Returns the elliptic curve
|
|
}
|
|
|
|
public void setCurve(EllipticCurve curve) {
|
|
this.curve = curve;
|
|
}
|
|
|
|
// Getter and Setter for private key
|
|
public BigInteger getPrivateKey() {
|
|
return privateKey;
|
|
}
|
|
|
|
public void setPrivateKey(BigInteger privateKey) {
|
|
this.privateKey = privateKey;
|
|
}
|
|
|
|
/**
|
|
* Encrypts the message using the public key.
|
|
* The message is transformed into an ECPoint and encrypted with elliptic curve operations.
|
|
*
|
|
* @param message The plain message to be encrypted
|
|
* @return The encrypted message as an array of ECPoints (R, S)
|
|
*/
|
|
public ECPoint[] encrypt(String message) {
|
|
BigInteger m = new BigInteger(message.getBytes()); // Convert message to BigInteger
|
|
SecureRandom r = new SecureRandom(); // Generate random value for k
|
|
BigInteger k = new BigInteger(curve.getFieldSize(), r); // Generate random scalar k
|
|
|
|
// Calculate point r = k * G, where G is the base point
|
|
ECPoint rPoint = basePoint.multiply(k, curve.getP(), curve.getA());
|
|
|
|
// Calculate point s = k * publicKey + encodedMessage
|
|
ECPoint sPoint = publicKey.multiply(k, curve.getP(), curve.getA()).add(curve.encodeMessage(m), curve.getP(), curve.getA());
|
|
|
|
return new ECPoint[] {rPoint, sPoint}; // Return encrypted message as two ECPoints
|
|
}
|
|
|
|
/**
|
|
* Decrypts the encrypted message using the private key.
|
|
* The decryption process is the reverse of encryption, recovering the original message.
|
|
*
|
|
* @param encryptedMessage The encrypted message as an array of ECPoints (R, S)
|
|
* @return The decrypted plain message as a String
|
|
*/
|
|
public String decrypt(ECPoint[] encryptedMessage) {
|
|
ECPoint rPoint = encryptedMessage[0]; // First part of ciphertext
|
|
ECPoint sPoint = encryptedMessage[1]; // Second part of ciphertext
|
|
|
|
// Perform decryption: s - r * privateKey
|
|
ECPoint decodedMessage = sPoint.subtract(rPoint.multiply(privateKey, curve.getP(), curve.getA()), curve.getP(), curve.getA());
|
|
|
|
BigInteger m = curve.decodeMessage(decodedMessage); // Decode the message from ECPoint
|
|
|
|
return new String(m.toByteArray()); // Convert BigInteger back to String
|
|
}
|
|
|
|
/**
|
|
* Generates a new public-private key pair for encryption and decryption.
|
|
*
|
|
* @param bits The size (in bits) of the keys to generate
|
|
*/
|
|
public final void generateKeys(int bits) {
|
|
SecureRandom r = new SecureRandom();
|
|
curve = new EllipticCurve(bits); // Initialize a new elliptic curve
|
|
basePoint = curve.getBasePoint(); // Set the base point G
|
|
|
|
// Generate private key as a random BigInteger
|
|
privateKey = new BigInteger(bits, r);
|
|
|
|
// Generate public key as the point publicKey = privateKey * G
|
|
publicKey = basePoint.multiply(privateKey, curve.getP(), curve.getA());
|
|
}
|
|
|
|
/**
|
|
* Class representing an elliptic curve with the form y^2 = x^3 + ax + b.
|
|
*/
|
|
public static class EllipticCurve {
|
|
private final BigInteger a; // Coefficient a in the curve equation
|
|
private final BigInteger b; // Coefficient b in the curve equation
|
|
private final BigInteger p; // Prime number p, defining the finite field
|
|
private final ECPoint basePoint; // Base point G on the curve
|
|
|
|
// Constructor with explicit parameters for a, b, p, and base point
|
|
public EllipticCurve(BigInteger a, BigInteger b, BigInteger p, ECPoint basePoint) {
|
|
this.a = a;
|
|
this.b = b;
|
|
this.p = p;
|
|
this.basePoint = basePoint;
|
|
}
|
|
|
|
// Constructor that randomly generates the curve parameters
|
|
public EllipticCurve(int bits) {
|
|
SecureRandom r = new SecureRandom();
|
|
this.p = BigInteger.probablePrime(bits, r); // Random prime p
|
|
this.a = new BigInteger(bits, r); // Random coefficient a
|
|
this.b = new BigInteger(bits, r); // Random coefficient b
|
|
this.basePoint = new ECPoint(BigInteger.valueOf(4), BigInteger.valueOf(8)); // Fixed base point G
|
|
}
|
|
|
|
public ECPoint getBasePoint() {
|
|
return basePoint;
|
|
}
|
|
|
|
public BigInteger getP() {
|
|
return p;
|
|
}
|
|
|
|
public BigInteger getA() {
|
|
return a;
|
|
}
|
|
|
|
public BigInteger getB() {
|
|
return b;
|
|
}
|
|
|
|
public int getFieldSize() {
|
|
return p.bitLength();
|
|
}
|
|
|
|
public ECPoint encodeMessage(BigInteger message) {
|
|
// Simple encoding of a message as an ECPoint (this is a simplified example)
|
|
return new ECPoint(message, message);
|
|
}
|
|
|
|
public BigInteger decodeMessage(ECPoint point) {
|
|
return point.getX(); // Decode the message from ECPoint (simplified)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class representing a point on the elliptic curve.
|
|
*/
|
|
public static class ECPoint {
|
|
private final BigInteger x; // X-coordinate of the point
|
|
private final BigInteger y; // Y-coordinate of the point
|
|
|
|
public ECPoint(BigInteger x, BigInteger y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
public BigInteger getX() {
|
|
return x;
|
|
}
|
|
|
|
public BigInteger getY() {
|
|
return y;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "ECPoint(x=" + x.toString() + ", y=" + y.toString() + ")";
|
|
}
|
|
|
|
/**
|
|
* Add two points on the elliptic curve.
|
|
*/
|
|
public ECPoint add(ECPoint other, BigInteger p, BigInteger a) {
|
|
if (this.x.equals(BigInteger.ZERO) && this.y.equals(BigInteger.ZERO)) {
|
|
return other; // If this point is the identity, return the other point
|
|
}
|
|
if (other.x.equals(BigInteger.ZERO) && other.y.equals(BigInteger.ZERO)) {
|
|
return this; // If the other point is the identity, return this point
|
|
}
|
|
|
|
BigInteger lambda;
|
|
if (this.equals(other)) {
|
|
// Special case: point doubling
|
|
lambda = this.x.pow(2).multiply(BigInteger.valueOf(3)).add(a).multiply(this.y.multiply(BigInteger.valueOf(2)).modInverse(p)).mod(p);
|
|
} else {
|
|
// General case: adding two different points
|
|
lambda = other.y.subtract(this.y).multiply(other.x.subtract(this.x).modInverse(p)).mod(p);
|
|
}
|
|
|
|
BigInteger xr = lambda.pow(2).subtract(this.x).subtract(other.x).mod(p);
|
|
BigInteger yr = lambda.multiply(this.x.subtract(xr)).subtract(this.y).mod(p);
|
|
|
|
return new ECPoint(xr, yr);
|
|
}
|
|
|
|
/**
|
|
* Subtract two points on the elliptic curve.
|
|
*/
|
|
public ECPoint subtract(ECPoint other, BigInteger p, BigInteger a) {
|
|
ECPoint negOther = new ECPoint(other.x, p.subtract(other.y)); // Negate the Y coordinate
|
|
return this.add(negOther, p, a); // Add the negated point
|
|
}
|
|
|
|
/**
|
|
* Multiply a point by a scalar (repeated addition).
|
|
*/
|
|
public ECPoint multiply(BigInteger k, BigInteger p, BigInteger a) {
|
|
ECPoint result = new ECPoint(BigInteger.ZERO, BigInteger.ZERO); // Identity point
|
|
ECPoint addend = this;
|
|
|
|
while (k.signum() > 0) {
|
|
if (k.testBit(0)) {
|
|
result = result.add(addend, p, a); // Add the current point
|
|
}
|
|
addend = addend.add(addend, p, a); // Double the point
|
|
k = k.shiftRight(1); // Divide k by 2
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|