Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
BEGIN LICENSE BLOCK ***** Version: CPL 1.0/GPL 2.0/LGPL 2.1 The contents of this file are subject to the Common Public License Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.eclipse.org/legal/cpl-v10.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. Copyright (C) 2006 Ola Bini <ola@ologix.com> Alternatively, the contents of this file may be used under the terms of either of the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), in which case the provisions of the GPL or the LGPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of either the GPL or the LGPL, and not to allow others to use your version of this file under the terms of the CPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the CPL, the GPL or the LGPL. END LICENSE BLOCK ***
 
 package org.jruby.ext.openssl;
 
 import java.util.List;
 import java.util.Map;
 import org.jruby.Ruby;

Author(s):
Ola Bini
 
 @SuppressWarnings("deprecation")
 public class SSLContext extends RubyObject {
     private static final long serialVersionUID = -6203496135962974777L;
 
     private final static String[] ctx_attrs = {
         "cert""key""client_ca""ca_file""ca_path",
         "timeout""verify_mode""verify_depth",
         "verify_callback""options""cert_store""extra_chain_cert",
         "client_cert_cb""tmp_dh_callback""session_id_context"};
 
     // Mapping table for OpenSSL's SSL_METHOD -> JSSE's SSLContext algorithm.
     private final static Map<StringStringSSL_VERSION_OSSL2JSSE;
     // Mapping table for JSEE's enabled protocols for the algorithm.
     private final static Map<StringString[]> ENABLED_PROTOCOLS;
     
     static {
          = new HashMap<StringString>();
          = new HashMap<StringString[]>();
 
         .put("TLSv1""TLSv1");
         .put("TLSv1_server""TLSv1");
         .put("TLSv1_client""TLSv1");
         .put("TLSv1"new String[] { "TLSv1" });
 
         .put("SSLv2""SSLv2");
         .put("SSLv2_server""SSLv2");
         .put("SSLv2_client""SSLv2");
         .put("SSLv2"new String[] { "SSLv2" });
 
        .put("SSLv3""SSLv3");
        .put("SSLv3_server""SSLv3");
        .put("SSLv3_client""SSLv3");
        .put("SSLv3"new String[] { "SSLv3" });
        .put("SSLv23""SSL");
        .put("SSLv23_server""SSL");
        .put("SSLv23_client""SSL");
        .put("SSL"new String[] { "SSLv2""SSLv3""TLSv1" });
        // Followings(TLS, TLSv1.1) are JSSE only methods at present. Let's allow user to use it.
        
        .put("TLS""TLS");
        .put("TLS"new String[] { "TLSv1""TLSv1.1" });
        .put("TLSv1.1""TLSv1.1");
        .put("TLSv1.1"new String[] { "TLSv1.1" });
    }
    private static ObjectAllocator SSLCONTEXT_ALLOCATOR = new ObjectAllocator() {
        public IRubyObject allocate(Ruby runtimeRubyClass klass) {
            return new SSLContext(runtimeklass);
        }
    };
    
    public static void createSSLContext(Ruby runtimeRubyModule mSSL) {
        RubyClass cSSLContext = mSSL.defineClassUnder("SSLContext",runtime.getObject(),);
        for(int i=0;i<.;i++) {
            cSSLContext.addReadWriteAttribute(runtime.getCurrentContext(), [i]);
        }
        cSSLContext.defineAnnotatedMethods(SSLContext.class);
    }
    public SSLContext(Ruby runtimeRubyClass type) {
        super(runtime,type);
    }
    public static RaiseException newSSLError(Ruby runtimeString message) {
        return Utils.newError(runtime"OpenSSL::SSL::SSLError"messagefalse);
    }
    private String protocol = "SSL"// SSLv23 in OpenSSL by default
    private boolean protocolForServer = true;
    private boolean protocolForClient = true;
    private PKey t_key = null;
    private X509Cert t_cert = null;
    /* TODO: should move to SSLSession after implemented */
    private int verifyResult = 1; /* avoid 0 (= X509_V_OK) just in case */
    private InternalContext internalCtx = null;
    @JRubyMethod(rest=true)
    public IRubyObject initialize(IRubyObject[] args) {
        return this;
    }
    public IRubyObject setup() {
        if (isFrozen()) {
            return getRuntime().getNil();
        }
        this.freeze(getRuntime().getCurrentContext());
        
         = new InternalContext();
        . = ;
        // TODO: handle tmp_dh_callback
        X509Store certStore = getCertStore();
        if (certStore != null) {
            . = certStore.getStore();
        } else {
            . = new Store();
        }
        IRubyObject value = getInstanceVariable("@extra_chain_cert");
        if (value != null && !value.isNil()) {
            . = new ArrayList<X509AuxCertificate>();
            for (X509Cert ele : convertToX509Certs(value)) {
                ..add(ele.getAuxCert());
            }
        }
        value = getInstanceVariable("@key");
        PKey key = null;
        if (value != null && !value.isNil()) {
            Utils.checkKind(getRuntime(), value"OpenSSL::PKey::PKey");
            key = (PKeyvalue;
        } else {
            key = getCallbackKey();
        }
        value = getInstanceVariable("@cert");
        X509Cert cert = null;
        if (value != null && !value.isNil()) {
            Utils.checkKind(getRuntime(), value"OpenSSL::X509::Certificate");
            cert = (X509Certvalue;
        } else {
            cert = getCallbackCert();
        }
        if (key != null && cert != null) {
            . = key.getAlgorithm();
            . = key.getPrivateKey();
            . = cert.getAuxCert();
        }
        value = getInstanceVariable("@client_ca");
        if (value != null && !value.isNil()) {
            if (value.respondsTo("each")) {
                for (X509Cert ele : convertToX509Certs(value)) {
                    ..add(ele.getAuxCert());
                }
            } else {
                Utils.checkKind(getRuntime(), value"OpenSSL::X509::Certificate");
                ..add(((X509Certvalue).getAuxCert());
            }
        }
        String caFile = getCaFile();
        String caPath = getCaPath();
        if (caFile != null || caPath != null) {
            try {
                if (..loadLocations(caFilecaPath) == 0) {
                    getRuntime().getWarnings().warn(."can't set verify locations");
                }
            } catch (Exception e) {
                throw newSSLError(getRuntime(), e.getMessage());
            }
        }
        value = getInstanceVariable("@verify_mode");
        if (value != null && !value.isNil()) {
            . = RubyNumeric.fix2int(value);
        } else {
            . = .;
        }
        value = getInstanceVariable("@verify_callback");
        if (value != null && !value.isNil()) {
            ..setExtraData(1, value);
        } else {
            ..setExtraData(1, null);
        }
        value = getInstanceVariable("@timeout");
        if (value != null && !value.isNil()) {
            . = RubyNumeric.fix2int(value);
        }
        
        value = getInstanceVariable("@verify_depth");
        if (value != null && !value.isNil()) {
            ..setDepth(RubyNumeric.fix2int(value));
        } else {
            ..setDepth(-1);
        }
        /* TODO: should be implemented for SSLSession
    val = ossl_sslctx_get_sess_id_ctx(self);
    if (!NIL_P(val)){
        StringValue(val);
        if (!SSL_CTX_set_session_id_context(ctx, (unsigned char *)RSTRING_PTR(val),
                                            RSTRING_LEN(val))){
            ossl_raise(eSSLError, "SSL_CTX_set_session_id_context:");
        }
    }
    if (RTEST(rb_iv_get(self, "@session_get_cb"))) {
        SSL_CTX_sess_set_get_cb(ctx, ossl_sslctx_session_get_cb);
        OSSL_Debug("SSL SESSION get callback added");
    }
    if (RTEST(rb_iv_get(self, "@session_new_cb"))) {
        SSL_CTX_sess_set_new_cb(ctx, ossl_sslctx_session_new_cb);
        OSSL_Debug("SSL SESSION new callback added");
    }
    if (RTEST(rb_iv_get(self, "@session_remove_cb"))) {
        SSL_CTX_sess_set_remove_cb(ctx, ossl_sslctx_session_remove_cb);
        OSSL_Debug("SSL SESSION remove callback added");
    }
    val = rb_iv_get(self, "@servername_cb");
    if (!NIL_P(val)) {
        SSL_CTX_set_tlsext_servername_callback(ctx, ssl_servername_cb);
        OSSL_Debug("SSL TLSEXT servername callback added");
    }
         */
        try {
            .init();
        } catch(GeneralSecurityException gse) {
            throw newSSLError(getRuntime(), gse.getMessage());
        }
        return getRuntime().getTrue();
    }
    public IRubyObject ciphers() {
        List<IRubyObjectlist = new ArrayList<IRubyObject>();
        Ruby rt = getRuntime();
        try {
            String[] supported = getCipherSuites(createDummySSLEngine());
            List<CipherStrings.Defciphs = CipherStrings.getMatchingCiphers(supported);
            for (CipherStrings.Def def : ciphs) {
                RubyArray ele = getRuntime().newArray(4);
                ele.set(0, rt.newString(def.name));
                ele.set(1, rt.newString(sslVersionString(def.algorithms)));
                ele.set(2, rt.newFixnum(def.strength_bits));
                ele.set(3, rt.newFixnum(def.alg_bits));
                list.add(ele);
            }
        } catch (GeneralSecurityException gse) {
            throw newSSLError(getRuntime(), gse.getMessage());
        }
        return rt.newArray(list);
    }
    @JRubyMethod(name = "ciphers=")
    public IRubyObject set_ciphers(IRubyObject val) {
        if (val.isNil()) {
             = .;
        } else if (val instanceof RubyArray) {
            StringBuilder builder = new StringBuilder();
            String sep = "";
            for (IRubyObject obj : ((RubyArrayval).toJavaArray()) {
                builder.append(sep).append(obj.toString());
                sep = ":";
            }
             = builder.toString();
        } else {
             = val.convertToString().toString();
            if (.equals("DEFAULT")) {
                 = .;
            }
        }
        RubyArray ary = (RubyArray)ciphers();
        if (ary.size() == 0) {
            throw newSSLError(getRuntime(), "no cipher match");
        }
        return val;
    }
    @JRubyMethod(name = "ssl_version=")
    public IRubyObject set_ssl_version(IRubyObject val) {
        String given;
        if (val instanceof RubyString) {
            RubyString str = val.convertToString();
            given = str.toString();
        } else {
            given = val.toString();
        }
        String mapped = .get(given);
        if (mapped == null) {
            throw newSSLError(getRuntime(), String.format("unknown SSL method `%s'."given));
        }
         = mapped;
         =  = true;
        if (given.endsWith("_client")) {
             = false;
        }
        if (given.endsWith("_server")) {
             = false;
        }
        return val;
    }
    boolean isProtocolForServer() {
        return ;
    }
    boolean isProtocolForClient() {
        return ;
    }
    int getLastVerifyResult() {
        return ;
    }
    void setLastVerifyResult(int verifyResult) {
        this. = verifyResult;
    }
        javax.net.ssl.SSLContext ctx = javax.net.ssl.SSLContext.getInstance();
        ctx.init(nullnullnull);
        return ctx.createSSLEngine();
    }
    // should keep SSLContext as a member for introducin SSLSession. later...
    SSLEngine createSSLEngine(String peerHostint peerPortthrows NoSuchAlgorithmExceptionKeyManagementException {
        SSLEngine engine;
        // an empty peerHost implies no SNI (RFC 3546) support requested
        if (peerHost == null || peerHost.length() == 0) {
            engine = .getSSLContext().createSSLEngine();
        }
        // SNI is attempted for valid peerHost hostname on Java >= 7
        // if peerHost is set to an IP address Java does not use SNI
        else {
            engine = .getSSLContext().createSSLEngine(peerHostpeerPort);
        }
        engine.setEnabledCipherSuites(getCipherSuites(engine));
        engine.setEnabledProtocols(getEnabledProtocols(engine));
        return engine;
    }
    private String[] getCipherSuites(SSLEngine engine) {
        List<CipherStrings.Defciphs = CipherStrings.getMatchingCiphers(engine.getSupportedCipherSuites());
        String[] result = new String[ciphs.size()];
        for (int i = 0; i < result.lengthi++) {
            result[i] = ciphs.get(i).;
        }
        return result;
    }
    private String[] getEnabledProtocols(SSLEngine engine) {
        List<Stringcandidates = new ArrayList<String>();
        long options = getOptions();
        if (.get() != null) {
            for (String enabled : .get()) {
                if (((options & .) != 0) && enabled.equals("SSLv2")) {
                    continue;
                }
                if (((options & .) != 0) && enabled.equals("SSLv3")) {
                    continue;
                }
                if (((options & .) != 0) && enabled.equals("TLSv1")) {
                    continue;
                }
                for (String allowed : engine.getEnabledProtocols()) {
                    if (allowed.equals(enabled)) {
                        candidates.add(allowed);
                    }
                }
            }
        }
        return candidates.toArray(new String[candidates.size()]);
    }
    private String sslVersionString(long bits) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        if ((bits & .) != 0) {
            if (!first) {
                sb.append("/");
            }
            first = false;
            sb.append("TLSv1/SSLv3");
        }
        if ((bits & .) != 0) {
            if (!first) {
                sb.append("/");
            }
            first = false;
            sb.append("SSLv2");
        }
        return sb.toString();
    }
    
    private PKey getCallbackKey() {
        if ( != null) {
            return ;
        }
        initFromCallback();
        return ;
    }
    private X509Cert getCallbackCert() {
        if ( != null) {
            return ;
        }
        initFromCallback();
        return ;
    }
    private void initFromCallback() {
        IRubyObject value = getInstanceVariable("@client_cert_cb");
        if (value != null && !value.isNil()) {
            IRubyObject out = value.callMethod(getRuntime().getCurrentContext(), "call"this);
            Utils.checkKind(getRuntime(), out"Array");
            IRubyObject cert = (IRubyObject) ((RubyArrayout).getList().get(0);
            IRubyObject key = (IRubyObject) ((RubyArrayout).getList().get(1);
            Utils.checkKind(getRuntime(), cert"OpenSSL::X509::Certificate");
            Utils.checkKind(getRuntime(), key"OpenSSL::PKey::PKey");
             = (X509Certcert;
             = (PKeykey;
        }
    }
    private X509Store getCertStore() {
        IRubyObject value = getInstanceVariable("@cert_store");
        if (value != null && !value.isNil() && (value instanceof X509Store)) {
            Utils.checkKind(getRuntime(), value"OpenSSL::X509::Store");
            return (X509Storevalue;
        } else {
            return null;
        }
    }
    private String getCaFile() {
        IRubyObject value = getInstanceVariable("@ca_file");
        if (value != null && !value.isNil()) {
            return value.convertToString().toString();
        } else {
            return null;
        }
    }
    private String getCaPath() {
        IRubyObject value = getInstanceVariable("@ca_path");
        if (value != null && !value.isNil()) {
            return value.convertToString().toString();
        } else {
            return null;
        }
    }
    private long getOptions() {
        IRubyObject value = getInstanceVariable("@options");
        if (value != null && !value.isNil()) {
            return RubyNumeric.fix2long(value);
        } else {
            return 0;
        }
    }
    
    private X509Cert[] convertToX509Certs(IRubyObject value) {
        final ArrayList<X509Certresult = new ArrayList<X509Cert>();
        ThreadContext ctx = getRuntime().getCurrentContext();
        RubyClass klass = Utils.getClassFromPath(ctx.runtime"OpenSSL::SSL::SSLContext");
        RuntimeHelpers.invoke(ctxvalue"each", CallBlock.newCallClosure(valueklass.new BlockCallback() {
            public IRubyObject call(ThreadContext contextIRubyObject[] argsBlock block) {
                Utils.checkKind(getRuntime(), args[0], "OpenSSL::X509::Certificate");
                result.add((X509Certargs[0]);
                return context.runtime.getNil();
            }
        }, ctx));
        return result.toArray(new X509Cert[0]);
    }

    
c: SSL_CTX
    private class InternalContext {
        Store store = null;
        int verifyMode = .;
        X509AuxCertificate cert = null;
        String keyAlgorithm = null;
        PrivateKey privateKey = null;
        List<X509AuxCertificateextraChainCert = null;
        int timeout = 0;
        String protocol = null;
        boolean protocolForServer = true;
        boolean protocolForClient = true;
        private javax.net.ssl.SSLContext sslCtx = null;
        void setLastVerifyResultInternal(int lastVerifyResult) {
            setLastVerifyResult(lastVerifyResult);
        }
        javax.net.ssl.SSLContext getSSLContext() {
            return ;
        }
        void init() throws GeneralSecurityException {
            KM km = new KM(this);
            TM tm = new TM(this);
             = javax.net.ssl.SSLContext.getInstance();
            if () {
                .getClientSessionContext().setSessionTimeout();
            }
            if () {
                .getServerSessionContext().setSessionTimeout();
            }
            .init(new javax.net.ssl.KeyManager[]{km}, new javax.net.ssl.TrustManager[]{tm}, null);
        }
        // part of ssl_verify_cert_chain
        StoreContext createStoreContext(String purpose) {
            if ( == null) {
                return null;
            }
            StoreContext ctx = new StoreContext();
            if (ctx.init(nullnull) == 0) {
                return null;
            }
            // for verify_cb
            ctx.setExtraData(1, .getExtraData(1));
            if (purpose != null) {
                ctx.setDefault(purpose);
            }
            ctx.param.inherit(.);
            return ctx;
        }
    }
    private static class KM extends javax.net.ssl.X509ExtendedKeyManager {
        private final InternalContext ctx;
        
        public KM(InternalContext ctx) {
            super();
            this. = ctx;
        }
        @Override
        public String chooseEngineClientAlias(String[] keyTypejava.security.Principal[] issuersjavax.net.ssl.SSLEngine engine) {
            if ( == null) {
                return null;
            }
            if (. == null) {
                return null;
            }
            for (int i = 0; i < keyType.lengthi++) {
                if (keyType[i].equalsIgnoreCase(.)) {
                    return keyType[i];
                }
            }
            return null;
        }
        @Override
        public String chooseEngineServerAlias(String keyTypejava.security.Principal[] issuersjavax.net.ssl.SSLEngine engine) {
            if ( == null || . == null) {
                return null;
            }
            if (keyType.equalsIgnoreCase(.)) {
                return keyType;
            }
            return null;
        }
        public String chooseClientAlias(String[] keyTypejava.security.Principal[] issuersjava.net.Socket socket) {
            return null;
        }
        public String chooseServerAlias(String keyTypejava.security.Principal[] issuersjava.net.Socket socket) {
            return null;
        }
        // c: ssl3_output_cert_chain
        public java.security.cert.X509Certificate[] getCertificateChain(String alias) {
            if ( == null) {
                return null;
            }
            if (. != null) {
                chain.addAll(.);
            } else if (. != null) {
                StoreContext storeCtx = .createStoreContext(null);
                X509AuxCertificate x = .;
                while (true) {
                    chain.add(x);
                    if (x.getIssuerDN().equals(x.getSubjectDN())) {
                        break;
                    }
                    try {
                        Name xn = new Name(x.getIssuerX500Principal());
                        X509Object[] s_obj = new X509Object[1];
                        if (storeCtx.getBySubject(.xns_obj) <= 0) {
                            break;
                        }
                        x = ((Certificates_obj[0]).;
                    } catch (Exception e) {
                        break;
                    }
                }
            }
            return chain.toArray(new java.security.cert.X509Certificate[0]);
        }
        public String[] getClientAliases(String keyTypejava.security.Principal[] issuers) {
            return null;
        }
        public java.security.PrivateKey getPrivateKey(String alias) {
            if ( == null || . == null) {
                return null;
            }
            return .;
        }
        public String[] getServerAliases(String keyTypejava.security.Principal[] issuers) {
            return null;
        }
    }
    private static class TM implements javax.net.ssl.X509TrustManager {
        private InternalContext ctx;
        public TM(InternalContext ctx) {
            super();
            this. = ctx;
        }
        public void checkClientTrusted(java.security.cert.X509Certificate[] chainString authTypethrows CertificateException {
            checkTrusted("ssl_client"chain);
        }
        public void checkServerTrusted(java.security.cert.X509Certificate[] chainString authTypethrows CertificateException {
            checkTrusted("ssl_server"chain);
        }
        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
            if ( == null) {
                return null;
            }
            chain.addAll(.);
            return chain.toArray(new java.security.cert.X509Certificate[0]);
        }
        // c: ssl_verify_cert_chain
        private void checkTrusted(String purposeX509Certificate[] chainthrows CertificateException {
            if ( == null) {
                throw new CertificateException("uninitialized trust manager");
            }
            if (chain != null && chain.length > 0) {
                if ((. & .) != 0) {
                    // verify_peer
                    StoreContext storeCtx = .createStoreContext(purpose);
                    if (storeCtx == null) {
                        throw new CertificateException("couldn't initialize store");
                    }
                    storeCtx.setCertificate(chain[0]);
                    storeCtx.setChain(chain);
                    verifyChain(storeCtx);
                }
            } else {
                if ((. & .) != 0) {
                    // fail if no peer cert
                    throw new CertificateException("no peer certificate");
                }
            }
        }
        private void verifyChain(StoreContext storeCtxthrows CertificateException {
            try {
                int ok = storeCtx.verifyCertificate();
                .setLastVerifyResultInternal(storeCtx.error);
                if (ok == 0) {
                    throw new CertificateException("certificate verify failed");
                }
            } catch (Exception e) {
                .setLastVerifyResultInternal(storeCtx.error);
                if (storeCtx.error == .) {
                    .setLastVerifyResultInternal(.);
                }
                throw new CertificateException("certificate verify failed"e);
            }
        }
    }
}// SSLContext