🔒 🔑 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

  1. Agree on prime q and generator (primitive root) α.
  2. Choose private keys Xa and Xb with 1 ≤ X < q−1.
  3. Compute public keys: Ya = αXa mod q, Yb = αXb mod q.
  4. Exchange Ya and Yb.
  5. 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

  1. Public parameters: q = 23, α = 5.
  2. Car 1 chooses Xa = 6Ya = 56 mod 23 = 8.
  3. Car 2 chooses Xb = 15Yb = 515 mod 23 = 19.
  4. Exchange Ya and Yb.
  5. Shared secret: Car 1 → 196 mod 23 = 2; Car 2 → 815 mod 23 = 2.
  6. Both derive the same key: 2.
Numerical Example Screenshot

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.
DH vs ECDH Venn Diagram

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

Key Generation Implementation
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

Encryption Implementation
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

Decryption Implementation
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

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

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)

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

Main Class
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()); } } }
Parameters Class
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; } }
Utils Class
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; } }
Validator Class
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; } }
KeyExchange Class
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; } }
Encryptor Class
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"); } }
Helpers Class
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"); } } }