Package com.ericsson.ssa.container

Source Code of com.ericsson.ssa.container.FlowToken

package com.ericsson.ssa.container;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.StringTokenizer;

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;

import org.apache.catalina.util.Base64;
import org.jvnet.glassfish.comms.util.LogUtil;

import com.ericsson.ssa.sip.dns.SipTransports;
import com.ericsson.ssa.sip.dns.TargetTuple;

/**
* FlowToken class used for connection reuse.
* FlowToken is representing a flowtoken used to identify the flow to the UA.
* The flow to UA is used for sending responses and subsequent request.
*
* The flowtoken is generated so it can be correlated to specific connection information where the request was recieved on.
* The flowtoken is obfuscated for security reasons before it leaves the container.
*
* @author epkadsz, Adrian Szwej
* @version 1.0, 2008-09-10
*
*/
public class FlowToken {
   
    /** Reused flowtoken bytearray. */
    private static final ByteArrayOutputStream flowTokenOutput = new ByteArrayOutputStream();
   
    /** Size of the digest included in the flowtoken before it gets base64 encoded. */
    private static final int DIGEST_LENGTH = 20;
   
    /** Prepared HMAC generator with a secret key. */
    private static Mac HMAC;
   
    private static final char SEP = ':';
   
    static {
        try {
            // Generate a key for the HMAC-SHA1-80 keyed-hashing algorithm; see RFC 2104
            KeyGenerator keyGen = KeyGenerator.getInstance("HmacSHA1");
            SecretKey key = keyGen.generateKey();
           
            // Create a MAC object using HMAC-SHA1-80 and initialize with key
            HMAC = Mac.getInstance(key.getAlgorithm());
            HMAC.init(key);
        } catch (NoSuchAlgorithmException e1) {
            LogUtil.SIP_LOGGER.getLogger().severe("Missing HmacSHA1 algorithm");
        } catch (InvalidKeyException e) {
            LogUtil.SIP_LOGGER.getLogger().severe("Cannot generate secret key. Path header during outbound proxy will not be obfuscated.");
        }
    }
   
    /** Base64 encoded flow id. */
    private String flowId;
   
    /**
     * Constructor
     * @param remotePoint the UA endpoint
     * @param localPoint the container endpoint
     */
    public FlowToken(TargetTuple remotePoint, InetSocketAddress localPoint) {
        this.flowId = encodeToken(composeConnectionInfo(remotePoint, localPoint));
    }
   
    /**
     * Constructor
     * @param flow token extracted directly from the Path header.
     */
    public FlowToken(String flowToken) {
        this.flowId = flowToken;
    }
   
    public String toString() {
        return flowId;
    }
   
    /**
     * Gets the flow identifier.
     * @return Base64 encoded flowIf containing the connection information with its HMAC.
     */
    public String getEncodedFlowId() {
        return flowId;
    }
   
    /**
     * Gets the connection information forming this flow.
     * The remote is the UA address connecting the connection to the container.
     * @return the remote tuple, null if there is a problem to decode the information
     */
    public TargetTuple getRemote() {
        StringTokenizer stringToken = new StringTokenizer(decodeToken(getEncodedFlowId()), String.valueOf(SEP));

        String transport = stringToken.nextToken();
        String remoteIP = stringToken.nextToken();
        String remotePort = stringToken.nextToken();
       
        try {
            return new TargetTuple(SipTransports.getTransport(transport), remoteIP, Integer.parseInt(remotePort));
        } catch (Exception e) {
            e.printStackTrace();
        }
       
        return null;
    }
   
    /**
     * Validates the token by extracting the HMAC prefix and comparing against the HMAC calculated from the connection information.
     *
     * 1) base64 decode the flowId.
     * 2) The first 20 octets are HMAC calculated value over the remaining part of the decoded string
     *
     * @throws Exception when flowtoken is tampered
     */
    public void validate() throws Exception {
        if (HMAC != null) {
            byte[] decodedDigestB64 = Base64.decode(flowId.getBytes("UTF8"));
            if (decodedDigestB64.length <= DIGEST_LENGTH)
                throw new Exception("Flowtoken has been tampered. Flowtoken is too short.");
           
            byte[] messageCandidate = new byte[decodedDigestB64.length - DIGEST_LENGTH];
            System.arraycopy(decodedDigestB64, DIGEST_LENGTH, messageCandidate, 0, decodedDigestB64.length - DIGEST_LENGTH);
            byte[] calculatedDigest = calculateDigest(messageCandidate);
            for (int i = 0; i < DIGEST_LENGTH; i++)
                if (calculatedDigest[i] != decodedDigestB64[i])
                    throw new Exception("Flowtoken has been tampered. Flowtoken missmatch");
       
    }
   
    /**
     * Generates string token with encoded information.
     * 1) the HMAC is computed using the input together with the secret key.
     * 2) the HMAC and the input is concatenated on format: HMAC + input (where HMAC corresponds to first 20 octets)
     * 3) The result is base64 encoded and returned
     *
     * @param input used for token generation
     * @return base64 encoded string
     **/
    private static String encodeToken(String input) {
        // Encode the string into bytes using utf-8 and digest it
        if (HMAC != null) {
            byte[] flowToken;
            byte[] utf8 = null;
            byte[] digest = null;
            try {
                utf8 = input.getBytes("UTF8");
                digest = calculateDigest(utf8);
            } catch (UnsupportedEncodingException e1) {
            }
           
            synchronized (flowTokenOutput) {
                flowTokenOutput.reset();
                try {
                    flowTokenOutput.write(digest);
                    flowTokenOutput.write(utf8);
                } catch (IOException e) {
                }
               
                flowToken = flowTokenOutput.toByteArray();
            }

            return new String(Base64.encode(flowToken));
        }
        else
            return input;
    }
   
    private static byte[] calculateDigest(byte[] utf8) {
        synchronized (HMAC) {
            return HMAC.doFinal(utf8);
        }
    }
   
    /**
     * Encodes connection information on format:
     * protocol + remote IP address + remote port + local IP address + local port +
     * @return
     */
    private static String composeConnectionInfo(TargetTuple remotePoint, InetSocketAddress localPoint) {
        return remotePoint.getProtocol().name() + SEP + remotePoint.getIP() + SEP + remotePoint.getPort() + SEP + localPoint.getHostName() + SEP + localPoint.getPort();
    }
   
    /**
     * Decodes the encoded token. This method does 64 bit decode, and strips of the HMAC information.
     * @param encodedToken 64 base encoded token with HMAC
     * @return 64 decoded string without the HMAC prefix
     */
    private static String decodeToken(String encodedToken) {
        //remove the first digits
        if (HMAC != null) {
            try {
                byte[] decodedDigestB64 = Base64.decode(encodedToken.getBytes("UTF8"));
                byte[] messageCandidate = new byte[decodedDigestB64.length - DIGEST_LENGTH];
                System.arraycopy(decodedDigestB64, DIGEST_LENGTH, messageCandidate, 0, decodedDigestB64.length - DIGEST_LENGTH);
               
                return new String(messageCandidate, "UTF8");
            }
            catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
       
        return null;
    }
   
}
TOP

Related Classes of com.ericsson.ssa.container.FlowToken

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.