🔒
🔑
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"); } } }