🔒
🔑
01
🔒
10
🔑
Diffie-Hellman Key Exchange
Implementation and Analysis in Java
CS285: Discrete Mathematics
Section 963
Instructor: Dr. Jalila Zouhair
College of Computer and Information Sciences (CCIS)
SHOUG FAWAZ ABDULLAH ALOMRAN
223410392
ALJOHARAH WALEED A ALBAWARDI
223410346
YARA MUTLAQ MOHAMMED ALZAMEL
223410834
FAI MOHAMMAD BIN KHANJAR
223410071
1. Introduction
In today's digital world, secure communication is the need of the hour to protect sensitive information being
exchanged over public networks. The Diffie-Hellman key exchange algorithm allows two parties to agree on a
shared secret key without actually exchanging it, and forms a basis for many of today's encryption schemes.
The following Java code for the Diffie–Hellman implementation is provided for CS285, Discrete Mathematics for
Computing. The developed system incorporates fundamental mathematical concepts regarding modular arithmetic and
exponentiation. The system simulates secure key generation and exchange between two entities using the
Diffie–Hellman key exchange method. A simple encryption and decryption mechanism has also been provided to
illustrate how the derived shared key can be used to secure messages.
We divide the program into the following classes: Parameters handles all public values, KeyExchange generates
and computes keys, Encryptor encrypts messages, and Validator, Helpers, and Utils ensure correct input handling
and output formatting. The Main class integrates all components of the system and allows users to test both a
predefined numerical example and a live interactive mode.
The project ties together theoretical mathematics and programming in practice, showing how secure key exchange
and encryption work in real-world systems.
1.1 Purpose of the Diffie–Hellman Key Exchange and Its Applications
The Diffie-Hellman key exchange is a cryptography technique whose main purpose is to allow two parties to
exchange a key that can be used for encrypting messages. This shared key can be used in public channels that
ensures no one unauthorized can read it. The key concept was developed by Ralph Merkle but was published by
Whitfield Diffie and Martin Hellman in 1976, which is why it's named after them.
Real-World Applications
- TLS: DH variants during the handshake generate session keys that protect web traffic.
- SSH: Establishes a secret key early in the connection to secure commands and file
transfers.
- Royal Convoy Security: Vehicles and control centers coordinate securely using a DH-family
exchange.
1.2 Algorithm Explanation
The Diffie–Hellman key exchange algorithm allows two parties to create a common secret over insecure channels
by performing modular exponentiation on agreed public parameters.
How It Works
- Agree on prime q and generator (primitive root) α.
- Choose private keys Xa and Xb with 1 ≤ X < q−1.
- Compute public keys: Ya = αXa mod q, Yb = αXb mod q.
- Exchange Ya and Yb.
- Compute shared secret: k = YbXa mod q = YaXb mod q.
Even though q, α, Ya, and Yb are public, inferring Xa or Xb is computationally infeasible for large,
well-chosen parameters due to the Discrete Logarithm Problem.
1.3 Numerical Example
Two cars and a control center must communicate securely during a royal convoy.
Step-by-Step Calculation
- Public parameters: q = 23, α = 5.
- Car 1 chooses Xa = 6 → Ya = 56 mod 23 = 8.
- Car 2 chooses Xb = 15 → Yb = 515 mod 23 = 19.
- Exchange Ya and Yb.
- Shared secret: Car 1 → 196 mod 23 = 2; Car 2 → 815 mod 23 = 2.
- Both derive the same key: 2.
Numerical example with fixed values (q=23, α=5).
1.4 Advantages and Limitations of Diffie–Hellman
Strengths of Diffie–Hellman
- No pre-shared secret: Two parties can securely derive a shared key over a public
channel.
- Forward secrecy (with ephemeral keys): Each session can use new private keys,
protecting past communications if long-term keys are exposed.
- Mathematical soundness: Security is based on the difficulty of solving the Discrete
Logarithm Problem (DLP) in large prime fields.
- Widely implemented: A proven, well-understood cornerstone of secure protocols for
decades.
Limitations of Diffie–Hellman
- No authentication by default: The original protocol does not confirm identity, leaving
it vulnerable to man-in-the-middle attacks unless combined with certificates or signatures.
- Weak parameter selection: Reusing or sharing small or predictable prime values can
allow precomputation attacks.
- Performance overhead: Large modular exponentiations are computationally heavy compared
to symmetric cryptography.
While Diffie–Hellman provides a strong mathematical basis for key exchange, its security depends on careful
parameter management and the inclusion of authentication to avoid interception or impersonation.
1.5 Comparison Between Diffie–Hellman (DH) and Elliptic Curve Diffie–Hellman (ECDH)
Elliptic Curve Diffie–Hellman (ECDH) follows the same principle as DH but replaces modular arithmetic with
elliptic-curve mathematics, providing stronger security with smaller key sizes. The table below highlights their
main differences and shared properties.
Shared Principles
- Both allow two parties to establish a shared secret without prior key exchange.
- Security relies on computational hardness problems — DLP for DH, ECDLP for ECDH.
- Neither provides authentication by itself; must be combined with digital signatures or certificates.
- Both can achieve forward secrecy using ephemeral (temporary) key pairs.
Key Differences
- Security per bit: ECDH achieves equivalent security with smaller key sizes (e.g., 256-bit
ECDH ≈ 3072-bit DH).
- Performance: ECDH operations are faster and require less computational power, especially
beneficial on mobile or IoT devices.
- Parameter handling: DH relies on prime and generator choices; ECDH uses predefined
elliptic curves standardized by NIST and others.
- Resistance to precomputation: ECDH’s curves make mass precomputation attacks
significantly harder.
- Deployment: DH remains widely used in legacy systems; ECDH dominates in TLS 1.3 and
modern secure communications.
Overlap between DH and ECDH — both share key agreement principles but differ in
efficiency and cryptographic strength.
2. Code Description
2.1 How the Program Works
The program is organized into classes: Main (driver), Parameters (public values q and α),
KeyExchange (private/public keys and shared secret), Encryptor (demo XOR encryption keyed by
SHA-256(shared secret)), Validator (prime/range/message validations), Utils (helpers), and
Helpers (interactive prompts). The driver allows a fixed numerical demo, auto-generated demo, and live
mode with validation.
2.2 Implementation of Each Part
Key Generation
private void generateKeys() {
this.privateKey = new BigInteger(getQ().bitLength(), random)
.mod(getQ().subtract(BigInteger.TWO))
.add(BigInteger.ONE); // 1 <= x <=q-2 this.publicKey=getAlpha().modPow(privateKey, getQ()); // Y=α^x mod q
}
The private key is uniform in [1, q−2]. The public key is computed as Y = α^x mod q.
Encryption
String encrypt(String msg, BigInteger sharedKey) throws Exception {
byte[] msgByte = msg.getBytes(StandardCharsets.UTF_8);
byte[] keyByte = Key(sharedKey); // SHA-256 hash of shared key
byte[] cipherMsgByte = xor(msgByte, keyByte); // demo XOR cipher
return Base64.getEncoder().encodeToString(cipherMsgByte);
}
For demonstration, we hash the shared secret to a fixed-length byte key and XOR the plaintext bytes, then
Base64-encode.
Decryption
String decrypt(String base64cipherMsg, BigInteger sharedKey) throws Exception {
byte[] keyByte = Key(sharedKey);
byte[] cipherMsgByte = Base64.getDecoder().decode(base64cipherMsg);
byte[] msgByte = xor(cipherMsgByte, keyByte);
return new String(msgByte, StandardCharsets.UTF_8);
}
Using the same hash and XOR reverses the demo cipher and recovers the message.
2.3 Challenges Faced and How We Overcame Them
Input Validation
Non-prime q, invalid α ranges, or short messages previously crashed the program; robust prompting loops now
enforce constraints and re-ask on error.
Class Integration
Standardized method names and types across modules to simplify the Main driver and reduce coupling issues.
Modular Arithmetic Accuracy
Adopted Java’s modPow to avoid overflow and correctness issues for public/shared key
derivations.
Byte Handling Consistency
Enforced UTF-8 and Base64 to keep encryption/decryption round-trips stable across platforms.
3. Results and Test Cases
Live Mode – Manual
Manual input of q, α, and message – valid parameters and correct key exchange.
In the manual live mode, users provide all parameters manually: the prime number (q),
primitive root (α), and message text.
The program validates each input, rejecting invalid values until proper ones are provided. Once both users (A
and B) select their private keys,
the program calculates and displays their public keys, followed by the shared key, encrypted message, and
decrypted message.
Successful decryption proves that both participants derived the same shared key.
Observation
This confirms that the program correctly implements the mathematical logic of the Diffie–Hellman key
exchange algorithm.
Live Mode – Alpha Error
Invalid α range — the system rejects and re-prompts until valid.
This test demonstrates input validation and error handling. If the user enters a primitive
root (α)
outside the valid range (1 < α < q), the program rejects it and prompts for re-entry. This prevents
crashes
and ensures correct mathematical operation.
Validation Mechanism
The Validator and Helpers classes work together to ensure that both the prime
number (q)
and the primitive root (α) meet mathematical constraints before proceeding.
Numerical Example (Fixed)
Fixed small integer example – both users derive identical shared key (K).
This example uses small integer values to visually demonstrate how two users derive the same
shared secret key.
Although the numbers are small, the logic mirrors real-world implementations using very large primes.
| Parameter |
Value |
| Prime number (q) |
7 |
| Primitive root (α) |
6 |
| Private key Xₐ |
5 |
| Private key Xᵦ |
3 |
| Shared key (K) |
1 |
Result Verification
Both participants derive the same shared key, confirming successful implementation.
This example serves as an educational demonstration of the core concept behind Diffie–Hellman.
4. Team Contribution
All members collaborated on the Java implementation and the report. Roles below summarize primary ownership
while acknowledging shared reviews and integration work.
Yara Alzamel
Established program scaffolding for public parameters and helper routines, including generation of q and α
and formatted output utilities.
Fai Bin Khanjar
Implemented DH mathematics (private/public/shared keys) and authored the guided numerical example;
validated modular arithmetic correctness.
Aljoharah Albawardi
Built the encryption/decryption demo atop the shared secret and executed test runs; documented security
considerations.
Shoug Alomran
Integrated modules into the driver, implemented validations and resilient input loops, coordinated final
testing, and unified the report tone and structure.
Coordination used GitHub for versioning and group reviews, plus iterative, test-driven integration sessions.
5. Conclusion
The project demonstrates how two parties can establish a shared secret over an insecure network using modular
exponentiation and carefully validated parameters. It also highlights the importance of authentication
wrappers and ephemeral keys to achieve practical security goals (MitM resistance and forward secrecy). Team
workflow, modular design, and rigorous validation enabled clean integration and predictable behavior.
Future Growth Opportunities
Extend with an authenticated key exchange (signatures/certificates), replace the demo XOR with AES-GCM
using a KDF on the shared secret, and add ECDH support with standardized curves for stronger
security-per-cycle.
6. References and Resources
Ambatipudi, S. (2024, June 6). Cryptographic advancements enabled by Diffie–Hellman.
ISACA Journal, Volume 3.
Boneh, D., & Shparlinski, I. E. (2001). On the unpredictability of bits of the
Elliptic Curve Diffie–Hellman scheme. In J. Kilian (Ed.), Advances in Cryptology – CRYPTO 2001 (pp. 201–212).
Springer.
Diffie, W., & Hellman, M. E. (1976). New directions in cryptography. IEEE
Transactions on Information Theory, 22(6), 644–654.
Haakegaard, R., & Lang, J. (2015). The Elliptic Curve Diffie–Hellman (ECDH).
University of California, Santa Barbara.
IEEE. (2012). Automated Analysis of Diffie-Hellman Protocols and Advanced Security
Properties.
Krawczyk, H. (2005). HMQV: A High-Performance Secure Diffie-Hellman Protocol. Springer.
Odlyzko, A. M. (2014). Discrete logarithms in finite fields and their cryptographic
significance.
Roy, A., Datta, A., & Mitchell, J. C. (2007). Formal proofs of cryptographic security
of Diffie–Hellman–based protocols. Stanford University.
Shoug Alomran, Yara, Fai, & Aljoharah. (2025). CS285 – Secure Key Exchange Project
[Source code]. GitHub.
Appendix – Full Source Code
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Scanner;
public class Main {
private static final SecureRandom random = new SecureRandom();
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("--- Royal Convoy - Secure Communication (Diffie-Hellman / ECDH Demo) ---");
System.out.println("This program demonstrates how the control center and vehicles exchange keys
securely.\n");
while (true) {
System.out.println("MAIN MENU:");
System.out.println("1) Numerical Example (Section 1.3 demo - fixed values)");
System.out.println("2) Numerical Example (auto-generated values)");
System.out.println("3) Live Mode (manual / auto parameters and private keys)");
System.out.println("0) Exit");
System.out.print("Choose an option: ");
String choice = input.nextLine().trim();
switch (choice) {
case "1": runFixedExample(); break;
case "2": runRandomExample(input); break;
case "3": runLiveMode(input); break;
case "0":
System.out.println("Goodbye!");
input.close();
return;
default:
System.out.println("Invalid choice. Please enter 1, 2, 3, or 0.\n");
}
}
}
private static void runFixedExample() {
System.out.println("\n--- Numerical Example (Section 1.3 - Fixed) ---");
BigInteger q = BigInteger.valueOf(23);
BigInteger alpha = BigInteger.valueOf(5);
Parameters storage = new Parameters(q, alpha);
BigInteger Xa = BigInteger.valueOf(6);
BigInteger Xb = BigInteger.valueOf(15);
BigInteger Ya = alpha.modPow(Xa, q);
BigInteger Yb = alpha.modPow(Xb, q);
BigInteger kA = Yb.modPow(Xa, q);
BigInteger kB = Ya.modPow(Xb, q);
System.out.println("To give an example, both 2 cars and the control center need a safe way to
communicate.");
System.out.println(storage.toString());
System.out.println("Car 1 chooses Xa = 6 → Ya = 5^6 mod 23 = " + Ya);
System.out.println("Car 2 chooses Xb = 15 → Yb = 5^15 mod 23 = " + Yb);
System.out.println("Each car computes the shared key:");
System.out.println("Car 1: 19^6 mod 23 = " + kA);
System.out.println("Car 2: 8^15 mod 23 = " + kB);
System.out.println("Both cars share the same key: " + kA + "\n");
String message = "Royal convoy message remains secure through shared key exchange.";
System.out.println("Example message: " + message);
try {
Encryptor enc = new Encryptor();
String cipher = enc.encrypt(message, kA);
String plain = enc.decrypt(cipher, kB);
System.out.println("Encrypted Message = " + cipher);
System.out.println("Decrypted Message = " + plain);
System.out.println("Decryption OK = " + plain.equals(message) + "\n");
} catch (Exception e) {
System.out.println("Encryption/Decryption failed: " + e.getMessage());
}
}
private static void runRandomExample(Scanner input) {
System.out.println("\n--- Numerical Example (Auto-generated values) ---");
BigInteger q = BigInteger.probablePrime(8, random);
BigInteger alpha = BigInteger.valueOf(random.nextInt(3, q.intValue() - 1));
Parameters storage = new Parameters(q, alpha);
BigInteger Xa = BigInteger.valueOf(random.nextInt(2, q.intValue() - 2));
BigInteger Xb = BigInteger.valueOf(random.nextInt(2, q.intValue() - 2));
BigInteger Ya = alpha.modPow(Xa, q);
BigInteger Yb = alpha.modPow(Xb, q);
BigInteger kA = Yb.modPow(Xa, q);
BigInteger kB = Ya.modPow(Xb, q);
System.out.println("Automatically generated example parameters and results:");
System.out.println(storage.toString());
System.out.println("Car 1 (Xa): " + Xa + " --> Ya = " + Ya);
System.out.println("Car 2 (Xb): " + Xb + " --> Yb = " + Yb);
System.out.println("Shared key for Car 1: " + kA);
System.out.println("Shared key for Car 2: " + kB);
System.out.println("Keys match: " + kA.equals(kB) + "\n");
String message = Helpers.promptMessage(input);
try {
Encryptor enc = new Encryptor();
String cipher = enc.encrypt(message, kA);
String plain = enc.decrypt(cipher, kB);
System.out.println("--- Encryption Test ---");
System.out.println("Original Message = " + message);
System.out.println("Encrypted Message = " + cipher);
System.out.println("Decrypted Message = " + plain);
System.out.println("Decryption OK = " + plain.equals(message) + "\n");
} catch (Exception e) {
System.out.println("Encryption/Decryption failed: " + e.getMessage());
}
}
private static void runLiveMode(Scanner input) {
System.out.println("\n--- Live Mode ---");
System.out.println("You can either enter your own values for q and alpha, or let the program generate
them.\n");
System.out.println("Choose parameter mode:");
System.out.println("a) Auto-generate q and alpha");
System.out.println("b) Enter manually");
System.out.print("Your choice (a/b): ");
String paramChoice = input.nextLine().trim().toLowerCase();
BigInteger q, alpha;
if ("b".equals(paramChoice)) {
q = Helpers.promptPrime(input);
alpha = Helpers.promptAlpha(input, q);
} else {
q = BigInteger.probablePrime(8, random);
alpha = BigInteger.valueOf(random.nextInt(3, q.intValue() - 1));
System.out.println("Automatically generated parameters:");
System.out.println("q = " + q + ", alpha = " + alpha + "\n");
}
Parameters storage = new Parameters(q, alpha);
System.out.println(storage + "\n");
System.out.println("Choose private-key mode:");
System.out.println("a) Auto-generate private keys");
System.out.println("b) Enter private keys manually");
System.out.print("Your choice (a/b): ");
String keyChoice = input.nextLine().trim().toLowerCase();
BigInteger Xa, Xb;
if ("b".equals(keyChoice)) {
Xa = Helpers.promptPrivateKey(input, q, "Enter private key for Car 1: ");
Xb = Helpers.promptPrivateKey(input, q, "Enter private key for Car 2: ");
} else {
Xa = BigInteger.valueOf(random.nextInt(2, q.intValue() - 2));
Xb = BigInteger.valueOf(random.nextInt(2, q.intValue() - 2));
System.out.println("Auto private keys generated: Xa = " + Xa + ", Xb = " + Xb + "\n");
}
BigInteger Ya = alpha.modPow(Xa, q);
BigInteger Yb = alpha.modPow(Xb, q);
BigInteger kA = Yb.modPow(Xa, q);
BigInteger kB = Ya.modPow(Xb, q);
if (!kA.equals(kB)) {
System.out.println("Shared keys do not match. Please try again.\n");
return;
}
String message = Helpers.promptMessage(input);
try {
Encryptor enc = new Encryptor();
String cipher = enc.encrypt(message, kA);
String plain = enc.decrypt(cipher, kB);
System.out.println("\n--- RESULTS ---");
System.out.println("q = " + q);
System.out.println("alpha = " + alpha);
System.out.println("Xa = " + Xa);
System.out.println("Xb = " + Xb);
System.out.println("Ya = " + Ya);
System.out.println("Yb = " + Yb);
System.out.println("Shared key = " + kA);
System.out.println("Original Message = " + message);
System.out.println("Encrypted Message = " + cipher);
System.out.println("Decrypted Message = " + plain);
System.out.println("Decryption OK = " + plain.equals(message));
System.out.println("Secure session established between convoy vehicles and control center.\n");
} catch (Exception e) {
System.out.println("Encryption/Decryption failed: " + e.getMessage());
}
}
}
import java.math.BigInteger;
public class Parameters {
private BigInteger q;
private BigInteger alpha;
public Parameters(BigInteger q, BigInteger alpha) {
this.q = q;
this.alpha = alpha;
}
public BigInteger getQ() { return q; }
public BigInteger getAlpha() { return alpha; }
@Override
public String toString() {
return "Public Parameters:\n" +
"Prime number (q): " + q + "\n" +
"Primitive root (alpha): " + alpha;
}
}
import java.math.BigInteger;
import java.security.SecureRandom;
public class Utils {
private static final SecureRandom secureRandom = new SecureRandom();
public static BigInteger getRandom(BigInteger upperLimit) {
BigInteger result;
do {
result = new BigInteger(upperLimit.bitLength(), secureRandom);
} while (result.compareTo(BigInteger.ONE) < 0 || result.compareTo(upperLimit)>= 0);
return result;
}
public static void printLine(String label, Object value) {
System.out.println(label + ": " + value);
}
public static byte[] normalizeKeyBytes(BigInteger key) {
byte[] bytes = key.toByteArray();
if (bytes.length > 1 && bytes[0] == 0) {
byte[] normalized = new byte[bytes.length - 1];
System.arraycopy(bytes, 1, normalized, 0, normalized.length);
return normalized;
}
return bytes;
}
}
import java.math.BigInteger;
public class Validator {
public static boolean isPrime(BigInteger q) {
if (q == null) return false;
if (q.compareTo(BigInteger.valueOf(3)) < 0) return false; return q.isProbablePrime(100); } public static
boolean isAlphaInRange(BigInteger alpha, BigInteger q) { return alpha !=null && q !=null &&
alpha.compareTo(BigInteger.ONE)> 0
&& alpha.compareTo(q) < 0; } public static boolean isValidMessage(String message) { return message !=null
&& message.trim().length()> 20;
}
}
import java.math.BigInteger;
import java.security.SecureRandom;
public class KeyExchange extends Parameters {
private SecureRandom random = new SecureRandom();
private BigInteger privateKey;
private BigInteger publicKey;
public KeyExchange(BigInteger q, BigInteger alpha) {
super(q, alpha);
generateKeys();
}
private void generateKeys() {
this.privateKey = new BigInteger(getQ().bitLength(), random)
.mod(getQ().subtract(BigInteger.TWO))
.add(BigInteger.ONE);
this.publicKey = getAlpha().modPow(privateKey, getQ());
}
public BigInteger computeSharedKey(BigInteger otherPublic) {
return otherPublic.modPow(privateKey, getQ());
}
public BigInteger getPrivateKey() { return privateKey; }
public BigInteger getPublicKey() { return publicKey; }
}
import java.math.BigInteger;
import java.util.Base64;
import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;
public class Encryptor {
private byte[] Key(BigInteger key) throws Exception {
MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
byte[] ArrayByte = key.toByteArray();
return msgDigest.digest(ArrayByte);
}
private byte[] xor(byte[] data, byte[] key) {
byte[] xorArray = new byte[data.length];
for (int i = 0; i < data.length; i++) { xorArray[i]=(byte) (data[i] ^ key[i % key.length]); } return
xorArray; } String encrypt(String msg, BigInteger sharedKey) throws Exception { byte[]
msgByte=msg.getBytes(StandardCharsets.UTF_8); byte[] keyByte=Key(sharedKey); byte[]
cipherMsgByte=xor(msgByte, keyByte); return Base64.getEncoder().encodeToString(cipherMsgByte); } String
decrypt(String base64cipherMsg, BigInteger sharedKey) throws Exception { byte[] keyByte=Key(sharedKey);
byte[] cipherMsgByte=Base64.getDecoder().decode(base64cipherMsg); byte[] msgByte=xor(cipherMsgByte,
keyByte); return new String(msgByte, StandardCharsets.UTF_8); } public static void validateMsg(String msg)
{ if (msg==null) throw new IllegalArgumentException("Message cannot be empty"); } public static void
validateCipherMsg(String cipherMsg) { if (cipherMsg==null) throw new IllegalArgumentException("Cipher
message cannot be empty"); } }
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Scanner;
public class Helpers {
public static BigInteger promptPrime(Scanner input) {
while (true) {
try {
System.out.print("Enter a prime q (\u2265 3): ");
BigInteger q = new BigInteger(input.nextLine().trim());
if (Validator.isPrime(q)) return q;
System.out.println("That's not a prime number. Try again.\n");
} catch (Exception e) {
System.out.println("Invalid input. Please type an integer.\n");
}
}
}
public static BigInteger promptAlpha(Scanner in, BigInteger q) {
System.out.print("Enter alpha (1 < alpha < q): ");
while (true) {
try {
BigInteger alpha = new BigInteger(in.nextLine().trim());
if (Validator.isAlphaInRange(alpha, q)) return alpha;
System.out.println(" Alpha must be> 1 and < q. Try again.\n"); } catch (Exception e) {
System.out.println("Invalid input. Please type an integer.\n"); } } } public static BigInteger
promptPrivateKey(Scanner input, BigInteger q, String message) { System.out.println("\nPrivate keys
must be integers between 1 and q-2 (inclusive)."); BigInteger min=BigInteger.ONE; BigInteger
max=q.subtract(BigInteger.TWO); while (true) { try { System.out.print(message); BigInteger
privateKey=new BigInteger(input.nextLine().trim()); if (privateKey.compareTo(min)>= 0 &&
privateKey.compareTo(max) <= 0) return privateKey; System.out.println("Invalid range. Please enter a
value between 1 and q-2.\n"); } catch (Exception e) { System.out.println("Invalid input. Please type
an integer.\n"); } } } public static BigInteger randomPrivateKey(BigInteger q, SecureRandom
randomNo) { BigInteger min=BigInteger.ONE; BigInteger max=q.subtract(BigInteger.TWO); while (true) {
BigInteger candidate=new BigInteger(q.bitLength(), randomNo); if (candidate.compareTo(min)>= 0 &&
candidate.compareTo(max) <= 0) return candidate; } } public static String promptMessage(Scanner
input) { System.out.println("\nStep 3: Enter the message to be securely sent between vehicles.");
while (true) { System.out.print("Enter a message (> 20 characters): ");
String message = input.nextLine();
if (Validator.isValidMessage(message)) return message;
System.out.println("Message too short. Please try again (must be > 20 characters).\n");
}
}
}