forked from TheAlgorithms/Java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathECC.java
More file actions
236 lines (194 loc) · 8.66 KB
/
ECC.java
File metadata and controls
236 lines (194 loc) · 8.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
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;
}
}
}