From 86856f497377aa0c9b39551a2df013b915be21ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Parm=C3=A9nides=20GV?= Date: Tue, 12 Mar 2013 18:23:11 +0100 Subject: Gonna try jboss srp implementation, because if I implement myself one we will have to audit it. --- src/org/jboss/security/srp/SRPClientSession.java | 289 +++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 src/org/jboss/security/srp/SRPClientSession.java (limited to 'src/org/jboss/security/srp/SRPClientSession.java') diff --git a/src/org/jboss/security/srp/SRPClientSession.java b/src/org/jboss/security/srp/SRPClientSession.java new file mode 100644 index 00000000..bafc4842 --- /dev/null +++ b/src/org/jboss/security/srp/SRPClientSession.java @@ -0,0 +1,289 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2008, Red Hat Middleware LLC, and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.jboss.security.srp; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import org.jboss.crypto.CryptoUtil; +import org.jboss.logging.Logger; + +/** The client side logic to the SRP protocol. The class is intended to be used + * with a SRPServerSession object via the SRPServerInterface. The SRP algorithm + * using these classes consists of: + * + * 1. Get server, SRPServerInterface server = (SRPServerInterface) Naming.lookup(...); + * 2. Get SRP parameters, SRPParameters params = server.getSRPParameters(username); + * 3. Create a client session, SRPClientSession client = new SRPClientSession(username, + * password, params); + * 4. Exchange public keys, byte[] A = client.exponential(); + * byte[] B = server.init(username, A); + * 5. Exchange challenges, byte[] M1 = client.response(B); + * byte[] M2 = server.verify(username, M1); + * 6. Verify the server response, if( client.verify(M2) == false ) + * throw new SecurityException("Failed to validate server reply"); + * 7. Validation complete + * + * Note that these steps are stateful. They must be performed in order and a + * step cannot be repeated to update the session state. + * + * This product uses the 'Secure Remote Password' cryptographic + * authentication system developed by Tom Wu (tjw@CS.Stanford.EDU). + * + * @author Scott.Stark@jboss.org + * @version $Revision: 81038 $ + */ +public class SRPClientSession +{ + private static Logger log = Logger.getLogger(SRPClientSession.class); + private SRPParameters params; + private BigInteger N; + private BigInteger g; + private BigInteger x; + private BigInteger v; + private byte[] s; + private BigInteger a; + private BigInteger A; + private byte[] K; + /** The M1 = H(H(N) xor H(g) | H(U) | s | A | B | K) hash */ + private MessageDigest clientHash; + /** The M2 = H(A | M | K) hash */ + private MessageDigest serverHash; + + private static int A_LEN = 64; + + /** Creates a new SRP server session object from the username, password + verifier, + @param username, the user ID + @param password, the user clear text password + @param params, the SRP parameters for the session + */ + public SRPClientSession(String username, char[] password, SRPParameters params) + { + this(username, password, params, null); + } + + /** Creates a new SRP server session object from the username, password + verifier, + @param username, the user ID + @param password, the user clear text password + @param params, the SRP parameters for the session + @param abytes, the random exponent used in the A public key. This must be + 8 bytes in length. + */ + public SRPClientSession(String username, char[] password, SRPParameters params, + byte[] abytes) + { + try + { + // Initialize the secure random number and message digests + CryptoUtil.init(); + } + catch(NoSuchAlgorithmException e) + { + } + this.params = params; + this.g = new BigInteger(1, params.g); + this.N = new BigInteger(1, params.N); + if( abytes != null ) + { + if( 8*abytes.length != A_LEN ) + throw new IllegalArgumentException("The abytes param must be " + +(A_LEN/8)+" in length, abytes.length="+abytes.length); + this.a = new BigInteger(abytes); + } + + if( log.isTraceEnabled() ) + log.trace("g: "+CryptoUtil.tob64(params.g)); + // Calculate x = H(s | H(U | ':' | password)) + byte[] xb = CryptoUtil.calculatePasswordHash(username, password, params.s); + if( log.isTraceEnabled() ) + log.trace("x: "+CryptoUtil.tob64(xb)); + this.x = new BigInteger(1, xb); + this.v = g.modPow(x, N); // g^x % N + if( log.isTraceEnabled() ) + log.trace("v: "+CryptoUtil.tob64(v.toByteArray())); + + serverHash = CryptoUtil.newDigest(); + clientHash = CryptoUtil.newDigest(); + // H(N) + byte[] hn = CryptoUtil.newDigest().digest(params.N); + if( log.isTraceEnabled() ) + log.trace("H(N): "+CryptoUtil.tob64(hn)); + // H(g) + byte[] hg = CryptoUtil.newDigest().digest(params.g); + if( log.isTraceEnabled() ) + log.trace("H(g): "+CryptoUtil.tob64(hg)); + // clientHash = H(N) xor H(g) + byte[] hxg = CryptoUtil.xor(hn, hg, 20); + if( log.isTraceEnabled() ) + log.trace("H(N) xor H(g): "+CryptoUtil.tob64(hxg)); + clientHash.update(hxg); + if( log.isTraceEnabled() ) + { + MessageDigest tmp = CryptoUtil.copy(clientHash); + log.trace("H[H(N) xor H(g)]: "+CryptoUtil.tob64(tmp.digest())); + } + // clientHash = H(N) xor H(g) | H(U) + clientHash.update(CryptoUtil.newDigest().digest(username.getBytes())); + if( log.isTraceEnabled() ) + { + MessageDigest tmp = CryptoUtil.copy(clientHash); + log.trace("H[H(N) xor H(g) | H(U)]: "+CryptoUtil.tob64(tmp.digest())); + } + // clientHash = H(N) xor H(g) | H(U) | s + clientHash.update(params.s); + if( log.isTraceEnabled() ) + { + MessageDigest tmp = CryptoUtil.copy(clientHash); + log.trace("H[H(N) xor H(g) | H(U) | s]: "+CryptoUtil.tob64(tmp.digest())); + } + K = null; + } + + /** + * @returns The exponential residue (parameter A) to be sent to the server. + */ + public byte[] exponential() + { + byte[] Abytes = null; + if(A == null) + { + /* If the random component of A has not been specified use a random + number */ + if( a == null ) + { + BigInteger one = BigInteger.ONE; + do + { + a = new BigInteger(A_LEN, CryptoUtil.getPRNG()); + } while(a.compareTo(one) <= 0); + } + A = g.modPow(a, N); + Abytes = CryptoUtil.trim(A.toByteArray()); + // clientHash = H(N) xor H(g) | H(U) | A + clientHash.update(Abytes); + if( log.isTraceEnabled() ) + { + MessageDigest tmp = CryptoUtil.copy(clientHash); + log.trace("H[H(N) xor H(g) | H(U) | s | A]: "+CryptoUtil.tob64(tmp.digest())); + } + // serverHash = A + serverHash.update(Abytes); + } + return Abytes; + } + + /** + @returns M1 = H(H(N) xor H(g) | H(U) | s | A | B | K) + @exception NoSuchAlgorithmException thrown if the session key + MessageDigest algorithm cannot be found. + */ + public byte[] response(byte[] Bbytes) throws NoSuchAlgorithmException + { + // clientHash = H(N) xor H(g) | H(U) | s | A | B + clientHash.update(Bbytes); + if( log.isTraceEnabled() ) + { + MessageDigest tmp = CryptoUtil.copy(clientHash); + log.trace("H[H(N) xor H(g) | H(U) | s | A | B]: "+CryptoUtil.tob64(tmp.digest())); + } + // Calculate u as the first 32 bits of H(B) + byte[] hB = CryptoUtil.newDigest().digest(Bbytes); + byte[] ub = + {hB[0], hB[1], hB[2], hB[3]}; + // Calculate S = (B - g^x) ^ (a + u * x) % N + BigInteger B = new BigInteger(1, Bbytes); + if( log.isTraceEnabled() ) + log.trace("B: "+CryptoUtil.tob64(B.toByteArray())); + if( B.compareTo(v) < 0 ) + B = B.add(N); + if( log.isTraceEnabled() ) + log.trace("B': "+CryptoUtil.tob64(B.toByteArray())); + if( log.isTraceEnabled() ) + log.trace("v: "+CryptoUtil.tob64(v.toByteArray())); + BigInteger u = new BigInteger(1, ub); + if( log.isTraceEnabled() ) + log.trace("u: "+CryptoUtil.tob64(u.toByteArray())); + BigInteger B_v = B.subtract(v); + if( log.isTraceEnabled() ) + log.trace("B - v: "+CryptoUtil.tob64(B_v.toByteArray())); + BigInteger a_ux = a.add(u.multiply(x)); + if( log.isTraceEnabled() ) + log.trace("a + u * x: "+CryptoUtil.tob64(a_ux.toByteArray())); + BigInteger S = B_v.modPow(a_ux, N); + if( log.isTraceEnabled() ) + log.trace("S: "+CryptoUtil.tob64(S.toByteArray())); + // K = SessionHash(S) + MessageDigest sessionDigest = MessageDigest.getInstance(params.hashAlgorithm); + K = sessionDigest.digest(S.toByteArray()); + if( log.isTraceEnabled() ) + log.trace("K: "+CryptoUtil.tob64(K)); + // clientHash = H(N) xor H(g) | H(U) | A | B | K + clientHash.update(K); + byte[] M1 = clientHash.digest(); + if( log.isTraceEnabled() ) + log.trace("M1: H[H(N) xor H(g) | H(U) | s | A | B | K]: "+CryptoUtil.tob64(M1)); + serverHash.update(M1); + serverHash.update(K); + if( log.isTraceEnabled() ) + { + MessageDigest tmp = CryptoUtil.copy(serverHash); + log.trace("H[A | M1 | K]: "+CryptoUtil.tob64(tmp.digest())); + } + return M1; + } + /** + * @param M2 The server's response to the client's challenge + * @returns True if and only if the server's response was correct. + */ + public boolean verify(byte[] M2) + { + // M2 = H(A | M1 | K) + byte[] myM2 = serverHash.digest(); + boolean valid = Arrays.equals(M2, myM2); + if( log.isTraceEnabled() ) + { + log.trace("verify serverM2: "+CryptoUtil.tob64(M2)); + log.trace("verify M2: "+CryptoUtil.tob64(myM2)); + } + return valid; + } + + /** Returns the negotiated session K, K = SHA_Interleave(S) + @return the private session K byte[] + @throws SecurityException - if the current thread does not have an + getSessionKey SRPPermission. + */ + public byte[] getSessionKey() throws SecurityException + { + SecurityManager sm = System.getSecurityManager(); + if( sm != null ) + { + SRPPermission p = new SRPPermission("getSessionKey"); + sm.checkPermission(p); + } + return K; + } +} -- cgit v1.2.3