Package org.jvnet.glassfish.comms.security.auth.impl

Source Code of org.jvnet.glassfish.comms.security.auth.impl.RFC4474Processor

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.jvnet.glassfish.comms.security.auth.impl;

import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.serverbeans.Config;
import com.sun.enterprise.security.auth.nonce.NonceManager;
import com.sun.enterprise.config.serverbeans.ElementProperty;
import com.sun.enterprise.config.serverbeans.SecurityService;
import com.sun.enterprise.config.serverbeans.ServerBeansFactory;
import com.sun.enterprise.security.auth.nonce.StringNonce;
import com.sun.enterprise.server.ApplicationServer;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.regex.Pattern;
import javax.mail.MessagingException;
import javax.net.ssl.HttpsURLConnection;
import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipServletMessage;
import org.apache.catalina.util.Base64;
import static org.jvnet.glassfish.comms.security.util.Constants.securityLogger;
/**
*
* @author k.venugopal@sun.com
*/
public class RFC4474Processor {
    // LWS i.e. possibly (zero or more whitespaces plus CRLF) followed by
    //(one or more whitespaces)
    //adopted from from BasicMandatoryHeaderValidator.java

    private static final String REGEXP_LWS = "(([ \t]*)(\r\n)){0,1}[ \t]+";
    private Pattern lswPattern = Pattern.compile(REGEXP_LWS);
    private static final String CSEQ = "Cseq";
    private static final byte[] delimeter = "|".getBytes();
    private static final String pattern = "EEE,dd MMM yyyy HH:mm:ss z";
   
    private static final String DEFAULT_SIGNATURE_ALG = "SHA1withRSA";
    public static final String IDENTITY_HEADER = "Identity";
    public static final String IDENTITY_INFO_HEADER = "Identity-Info";
    public static final String DATE_HEADER = "Date";
    public static final String CONTACT_HEADER = "Contact";
    private static HashMap<String, AlgorithmSuite> algMap = new HashMap<String, AlgorithmSuite>();
    private long timestampFreshness = 600000;
    private long maxClockSkew = 0;
    private Properties config = null;
    private CertificateValidator validator = null;
    private static NonceManager nonceManager = null;


    static {
        algMap.put(AlgorithmSuite.DSASHA1.getURI(), AlgorithmSuite.DSASHA1);
        algMap.put(AlgorithmSuite.RSASHA1.getURI(), AlgorithmSuite.RSASHA1);
        algMap.put(AlgorithmSuite.DSASHA256.getURI(), AlgorithmSuite.DSASHA256);
        algMap.put(AlgorithmSuite.RSASHA256.getURI(), AlgorithmSuite.RSASHA256);
        initNonceManager();

    }
    ;

    private static synchronized void initNonceManager() {
        if (nonceManager == null) {
            try {
                Config cfg = (Config) ServerBeansFactory.getConfigBean(ApplicationServer.getServerContext().getConfigContext());
                SecurityService secService = cfg.getSecurityService();
                ElementProperty[] props = secService.getElementProperty();
                String nonceConfigValue = null;
                String nonceId  = "sip-nonce-config";
                for (ElementProperty value : props) {
                    if ("NonceManager".equalsIgnoreCase(value.getName())) {
                        nonceConfigValue = value.getValue();
                        break;
                    }
                }
                if (nonceConfigValue == null || nonceConfigValue.length() == 0) {
                    nonceManager = NonceManager.getInstance("sailfin_identity_Cache", 3600000);
                   
                }else{
                    Map parsedValues = NonceManager.getProperties(nonceConfigValue);
                    String nonceAge = (String) parsedValues.get(nonceId);
                    long maxNonceValue;
                    if(nonceAge == null ||nonceAge.length() ==0){
                        maxNonceValue = 3600000;
                    }else{
                        maxNonceValue = Long.valueOf(nonceAge);
                    }
                    nonceManager  = NonceManager.getInstance(nonceId, maxNonceValue);                   
                }
            } catch (ConfigException ex) {
                securityLogger.log(Level.SEVERE, null, ex);
            }
        }
    }

    public RFC4474Processor() {
       
    }

    public RFC4474Processor(Properties props) {
        this.config = props;
        init();
    }

    public void init() {
        if (config != null && !config.isEmpty()) {
            String mcs = config.getProperty("maxClockSkew");
            String tlimit = config.getProperty("timestampFreshnessLimit");
            if (mcs != null && mcs.length() > 0) {
                maxClockSkew = Long.parseLong(mcs);
            }
            if (tlimit != null && tlimit.length() > 0) {
                long limit = Long.parseLong(tlimit);
                if (limit > 0) {
                    timestampFreshness = limit;
                }
            }
        }
        try {
            CertValidator.init(config);
            validator = CertValidator.getInstance();
        } catch (Exception ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex);
        }

    }

    public void sign(SipServletMessage message, PrivateKey privateKey, AlgorithmSuite alg, String url) throws SignatureException {
        try {
            MessageDigest md = MessageDigest.getInstance(alg.getDigestAlgorithm());
            byte[] data = createDigestData(message, md, true);

            byte[] edd = Base64.encode(data);
            if (securityLogger.isLoggable(Level.FINE)) {
                securityLogger.log(Level.FINE, "Base64 of message digest " + new String(edd));
            }

            Signature signature = Signature.getInstance(alg.getSignatureAlgorithm());
            signature.initSign(privateKey);
            signature.update(data);
            byte[] signedData = signature.sign();
            byte[] ed = Base64.encode(signedData);
            message.setHeader(IDENTITY_HEADER, "\"" + new String(ed) + "\"");
            String identityInfo = "<" + url + ">;" + "alg=" + alg.getURI();
            message.setHeader(IDENTITY_INFO_HEADER, identityInfo);

            if (securityLogger.isLoggable(Level.FINE)) {
                securityLogger.log(Level.FINE, "Base64 of message signature: " + new String(ed));
            }
        } catch (ServletParseException ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw new SignatureException(ex);
        } catch (MessagingException ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw new SignatureException(ex);
        } catch (IOException ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw new SignatureException(ex);
        } catch (InvalidKeyException ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw new SignatureException(ex);
        } catch (SignatureException ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw ex;
        } catch (NoSuchAlgorithmException ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw new SignatureException(ex);
        }

    }

    public boolean validateAndCacheNonce(StringNonce nonce){
        if(!nonceManager.hasNonce(nonce)){
            nonceManager.validateNonce(nonce);
            return true;
        }
        return false;
    }
   
    private byte[] createDigestData(SipServletMessage message, MessageDigest md, boolean toCreate) throws MessagingException, IOException, ServletParseException {

        Address from = message.getFrom();
        Address to = message.getTo();
        String callId = message.getCallId();
        String cseq = getCSeq(message);
        String date = message.getHeader(DATE_HEADER);
        Address ahContact = message.getAddressHeader(CONTACT_HEADER);
        String contact = null;
        if (ahContact != null) {
            contact = ahContact.getURI().toString();
        }
        if (date == null || date.length() == 0) {
            if (toCreate) {
                date = addDate(message);
            } else {
                throw new InvalidParameterException("Date header must be present" + "in the SIP message");
            }
        }


        md.update(from.getURI().toString().getBytes());
        md.update(delimeter);
        md.update(to.getURI().toString().getBytes());
        md.update(delimeter);
        md.update(callId.getBytes());
        md.update(delimeter);
        md.update(cseq.getBytes());
        md.update(delimeter);
        md.update(date.getBytes());
        md.update(delimeter);
        if (contact != null) {
            md.update(contact.getBytes());
        }
        md.update(delimeter);
        byte[] content = message.getRawContent();
        if (content != null) {
            md.update(content);
        }
        if (securityLogger.isLoggable(Level.FINE)) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            bos.write(from.getURI().toString().getBytes());
            bos.write(delimeter);
            bos.write(to.getURI().toString().getBytes());
            bos.write(delimeter);
            bos.write(callId.getBytes());
            bos.write(delimeter);
            bos.write(cseq.getBytes());
            bos.write(delimeter);
            bos.write(date.getBytes());
            bos.write(delimeter);
            if (contact != null) {
                bos.write(contact.getBytes());
            }
            bos.write(delimeter);
            if (content != null) {
                bos.write(content);
            }
            securityLogger.log(Level.FINE, "Canonicalized Message for Identity header is : " + new String(bos.toByteArray()));
        }
        return md.digest();
    }

    private AlgorithmSuite getAlgorithmSuite(String info) {
        int index = info.indexOf("alg=");
        if (index == -1) {
            return AlgorithmSuite.RSASHA1;
        }
        String uri = info.substring(index + 4, info.length());
        return algMap.get(uri.trim());
    }

    private X509Certificate getCertificate(String header) throws CertificateException, MalformedURLException, IOException {
        int index = header.indexOf(";");
        String url = header.substring(1, index - 1);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        URL link = new URL(url);
        URLConnection connection = link.openConnection();
        InputStream is = null;
        if (connection instanceof HttpsURLConnection) {
            HttpsURLConnection hu = (HttpsURLConnection) link.openConnection();
            is = hu.getInputStream();
        } else {
            is = link.openStream();
        }
        X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(is);
        return x509Cert;
    }

    public boolean verify(SipServletMessage message) throws SignatureException, CertificateException, NoSuchAlgorithmException, ServletParseException, InvalidKeyException {
        boolean result = false;
        try {
            String signedData = null;
            validateDate(message);
            String info = message.getHeader("Identity-Info");
            AlgorithmSuite algorithmSuite = getAlgorithmSuite(info);
            X509Certificate x509Cert = getCertificate(info);
            validate(x509Cert);
            MessageDigest md = MessageDigest.getInstance(algorithmSuite.getDigestAlgorithm());
            byte[] data = createDigestData(message, md, false);
            signedData = message.getHeader(IDENTITY_HEADER);
            Signature signature = Signature.getInstance(algorithmSuite.getSignatureAlgorithm());
            signature.initVerify(x509Cert.getPublicKey());
            signature.update(data);
            signedData = signedData.substring(1, signedData.length() - 1);
            byte[] dd = Base64.decode(signedData.getBytes());
            result = signature.verify(dd);           
            if (!result) {
                if (securityLogger.isLoggable(Level.FINE)) {
                    securityLogger.log(Level.FINE, "Signature verification of Identity header failed, message is " + message);
                }
            } else {
                if (securityLogger.isLoggable(Level.FINE)) {
                    securityLogger.log(Level.FINE, "Signature verification of Identity header passed, message is " + message);
                }
            }
        } catch (MessagingException ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw new SecurityException(ex);
        } catch (IOException ex) {
            securityLogger.log(Level.SEVERE, null, ex);
            throw new SecurityException(ex);
        } catch (ParseException pe) {
            throw new SecurityException(pe);
        } catch (TimeoutException te) {
            throw new SecurityException(te);
        }
        return result;
    }

    private void validate(X509Certificate certificate) throws CertificateException {

        if(validator == null){
            try {
                CertValidator.getInstance().validate(certificate);
                return;
            } catch (Exception ex) {
                securityLogger.log(Level.SEVERE, null, ex);
                throw new CertificateException(ex);
            }
        }

        validator.validate(certificate);

    }

    private String addDate(SipServletMessage message) {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        Date date = new Date();
        String pd = sdf.format(date);
        message.addHeader(DATE_HEADER, pd);
        return pd;
    }

    private String getCSeq(SipServletMessage message) {
        String value = message.getHeader(CSEQ);
        // CSeq is "1*DIGIT LWS METHOD"
        String[] parts = lswPattern.split(value);
        if (parts.length != 2) {
            throw new InvalidParameterException("Invalid CSeq  :" + value);
        }
        return parts[0] + " " + parts[1];
    }

    private void validateDate(SipServletMessage message) throws ParseException, TimeoutException {
        String dateValue = message.getHeader(DATE_HEADER);
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        Date date = sdf.parse(dateValue);
        new TimestampValidatorImpl().validate(date, maxClockSkew, timestampFreshness);
    }
}
TOP

Related Classes of org.jvnet.glassfish.comms.security.auth.impl.RFC4474Processor

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.