package org.bouncycastle.crypto.agreement.srp;

import java.math.BigInteger;
import java.security.SecureRandom;

import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.params.SRP6GroupParameters;

Implements the server side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe. This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002"
/** * Implements the server side SRP-6a protocol. Note that this class is stateful, and therefore NOT threadsafe. * This implementation of SRP is based on the optimized message sequence put forth by Thomas Wu in the paper * "SRP-6: Improvements and Refinements to the Secure Remote Password Protocol, 2002" */
public class SRP6Server { protected BigInteger N; protected BigInteger g; protected BigInteger v; protected SecureRandom random; protected Digest digest; protected BigInteger A; protected BigInteger b; protected BigInteger B; protected BigInteger u; protected BigInteger S; protected BigInteger M1; protected BigInteger M2; protected BigInteger Key; public SRP6Server() { }
Initialises the server to accept a new client authentication attempt
Params:
  • N – The safe prime associated with the client's verifier
  • g – The group parameter associated with the client's verifier
  • v – The client's verifier
  • digest – The digest algorithm associated with the client's verifier
  • random – For key generation
/** * Initialises the server to accept a new client authentication attempt * @param N The safe prime associated with the client's verifier * @param g The group parameter associated with the client's verifier * @param v The client's verifier * @param digest The digest algorithm associated with the client's verifier * @param random For key generation */
public void init(BigInteger N, BigInteger g, BigInteger v, Digest digest, SecureRandom random) { this.N = N; this.g = g; this.v = v; this.random = random; this.digest = digest; } public void init(SRP6GroupParameters group, BigInteger v, Digest digest, SecureRandom random) { init(group.getN(), group.getG(), v, digest, random); }
Generates the server's credentials that are to be sent to the client.
Returns:The server's public value to the client
/** * Generates the server's credentials that are to be sent to the client. * @return The server's public value to the client */
public BigInteger generateServerCredentials() { BigInteger k = SRP6Util.calculateK(digest, N, g); this.b = selectPrivateValue(); this.B = k.multiply(v).mod(N).add(g.modPow(b, N)).mod(N); return B; }
Processes the client's credentials. If valid the shared secret is generated and returned.
Params:
  • clientA – The client's credentials
Throws:
Returns:A shared secret BigInteger
/** * Processes the client's credentials. If valid the shared secret is generated and returned. * @param clientA The client's credentials * @return A shared secret BigInteger * @throws CryptoException If client's credentials are invalid */
public BigInteger calculateSecret(BigInteger clientA) throws CryptoException { this.A = SRP6Util.validatePublicValue(N, clientA); this.u = SRP6Util.calculateU(digest, N, A, B); this.S = calculateS(); return S; } protected BigInteger selectPrivateValue() { return SRP6Util.generatePrivateValue(digest, N, g, random); } private BigInteger calculateS() { return v.modPow(u, N).multiply(A).mod(N).modPow(b, N); }
Authenticates the received client evidence message M1 and saves it only if correct. To be called after calculating the secret S.
Params:
  • clientM1 – the client side generated evidence message
Throws:
Returns:A boolean indicating if the client message M1 was the expected one.
/** * Authenticates the received client evidence message M1 and saves it only if correct. * To be called after calculating the secret S. * @param clientM1 the client side generated evidence message * @return A boolean indicating if the client message M1 was the expected one. * @throws CryptoException */
public boolean verifyClientEvidenceMessage(BigInteger clientM1) throws CryptoException { // Verify pre-requirements if (this.A == null || this.B == null || this.S == null) { throw new CryptoException("Impossible to compute and verify M1: " + "some data are missing from the previous operations (A,B,S)"); } // Compute the own client evidence message 'M1' BigInteger computedM1 = SRP6Util.calculateM1(digest, N, A, B, S); if (computedM1.equals(clientM1)) { this.M1 = clientM1; return true; } return false; }
Computes the server evidence message M2 using the previously verified values. To be called after successfully verifying the client evidence message M1.
Throws:
Returns:M2: the server side generated evidence message
/** * Computes the server evidence message M2 using the previously verified values. * To be called after successfully verifying the client evidence message M1. * @return M2: the server side generated evidence message * @throws CryptoException */
public BigInteger calculateServerEvidenceMessage() throws CryptoException { // Verify pre-requirements if (this.A == null || this.M1 == null || this.S == null) { throw new CryptoException("Impossible to compute M2: " + "some data are missing from the previous operations (A,M1,S)"); } // Compute the server evidence message 'M2' this.M2 = SRP6Util.calculateM2(digest, N, A, M1, S); return M2; }
Computes the final session key as a result of the SRP successful mutual authentication To be called after calculating the server evidence message M2.
Throws:
Returns:Key: the mutual authenticated symmetric session key
/** * Computes the final session key as a result of the SRP successful mutual authentication * To be called after calculating the server evidence message M2. * @return Key: the mutual authenticated symmetric session key * @throws CryptoException */
public BigInteger calculateSessionKey() throws CryptoException { // Verify pre-requirements if (this.S == null || this.M1 == null || this.M2 == null) { throw new CryptoException("Impossible to compute Key: " + "some data are missing from the previous operations (S,M1,M2)"); } this.Key = SRP6Util.calculateKey(digest, N, S); return Key; } }