/***** BEGIN LICENSE BLOCK ***** * Version: EPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Eclipse 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/epl-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 EPL, 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 EPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.ext.openssl.x509store; import java.util.ArrayList; import java.util.List; import java.security.cert.CertificateException; /** * c: X509_PURPOSE * * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> */ public class Purpose { private static final String XKU_EMAIL_PROTECT = "1.3.6.1.5.5.7.3.4"; // Email protection private static final String XKU_SSL_CLIENT = "1.3.6.1.5.5.7.3.2"; // SSL Client Authentication private static final String[] XKU_SSL_SERVER = new String[]{ "1.3.6.1.5.5.7.3.1", // SSL Server Authentication "2.16.840.1.113730.4.1", // Netscape Server Gated Crypto "1.3.6.1.4.1.311.10.3.3" // Microsoft Server Gated Crypto }; static interface CheckPurposeFunction extends Function3<Purpose, X509AuxCertificate, Integer> { int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException ; } public int purpose; public int trust; /* Default trust ID */ public int flags; CheckPurposeFunction checkPurpose; public String name; public String sname; public Object userData; private Purpose() {} Purpose(int p, int t, int f, CheckPurposeFunction cp, String n, String s, Object u) { this.purpose = p; this.trust = t; this.flags = f; this.checkPurpose = cp; this.name = n; this.sname = s; this.userData = u; } /** * c: X509_check_purpose */ public static int checkPurpose(X509AuxCertificate x, int id, int ca) throws CertificateException { if ( id == -1 ) return 1; int idx = getByID(id); if ( idx == -1 ) return -1; Purpose pt = getFirst(idx); return pt.checkPurpose.call(pt, x ,Integer.valueOf(ca)); } /** * c: X509_PURPOSE_set */ public static int set(int[] p, int purpose) { if(getByID(purpose) == -1) { X509Error.addError(X509Utils.X509V3_R_INVALID_PURPOSE); return 0; } p[0] = purpose; return 1; } private final static List<Purpose> xptable = new ArrayList<Purpose>(); /** * c: X509_PURPOSE_get_count */ public static int getCount() { return xptable.size() + xstandard.length; } /** * c: X509_PURPOSE_get0 */ public static Purpose getFirst(int idx) { if(idx < 0) { return null; } if(idx < xstandard.length) { return xstandard[idx]; } return xptable.get(idx - xstandard.length); } /** * c: X509_PURPOSE_get_by_sname */ public static int getBySName(String sname) { for(int i=0;i<getCount();i++) { Purpose xptmp = getFirst(i); if(xptmp.sname.equals(sname)) { return i; } } return -1; } /** * c: X509_PURPOSE_getby_id */ public static int getByID(int purpose) { if(purpose >= X509Utils.X509_PURPOSE_MIN && (purpose <= X509Utils.X509_PURPOSE_MAX)) { return purpose - X509Utils.X509_PURPOSE_MIN; } int i = 0; for(Purpose p : xptable) { if(p.purpose == purpose) { return i + xstandard.length; } } return -1; } /** * c: X509_PURPOSE_add */ static int add(int id, int trust, int flags, CheckPurposeFunction ck, String name, String sname, Object arg) { flags &= ~X509Utils.X509_PURPOSE_DYNAMIC; flags |= X509Utils.X509_PURPOSE_DYNAMIC_NAME; int idx = getByID(id); Purpose ptmp; if(idx == -1) { ptmp = new Purpose(); ptmp.flags = X509Utils.X509_PURPOSE_DYNAMIC; } else { ptmp = getFirst(idx); } ptmp.name = name; ptmp.sname = sname; ptmp.flags &= X509Utils.X509_PURPOSE_DYNAMIC; ptmp.flags |= flags; ptmp.purpose = id; ptmp.trust = trust; ptmp.checkPurpose = ck; ptmp.userData = arg; if (idx == -1) { xptable.add(ptmp); } return 1; } /** * c: X509_PURPOSE_cleanup */ public static void cleanup() { xptable.clear(); } /** * c: X509_PURPOSE_get_id */ public int getID() { return purpose; } /** * c: X509_PURPOSE_get0_name */ public String getName() { return name; } /** * c: X509_PURPOSE_get0_sname */ public String getSName() { return sname; } /** * c: X509_PURPOSE_get_trust */ public int getTrust() { return trust; } /** * c: X509_check_ca */ public static int checkCA(X509AuxCertificate x) throws CertificateException { if(x.getKeyUsage() != null && !x.getKeyUsage()[5]) { // KEY_CERT_SIGN return 0; } if(x.getExtensionValue("2.5.29.19") != null) { // BASIC_CONSTRAINTS if(x.getBasicConstraints() != -1) { // is CA. return 1; } else { return 0; } } else { if(x.getVersion() == 1 && x.getIssuerX500Principal().equals(x.getSubjectX500Principal())) { // V1_ROOT return 3; } if(x.getKeyUsage() != null) { return 4; } Integer nsCertType = x.getNsCertType(); if (nsCertType != null && (nsCertType & X509Utils.NS_ANY_CA) != 0) { return 5; } return 0; } } /** * c: check_ssl_ca */ public static int checkSSLCA(X509AuxCertificate x) throws CertificateException { int ca_ret = checkCA(x); if(ca_ret == 0) { return 0; } Integer nsCertType = x.getNsCertType(); boolean v2 = nsCertType != null && (nsCertType & X509Utils.NS_SSL_CA) != 0; if(ca_ret != 5 || v2) { return ca_ret; } return 0; } /** * c: xku_reject: check if the cert must be rejected(true) or not */ public static boolean xkuReject(X509AuxCertificate x, String mustHaveXku) throws CertificateException { List<String> xku = x.getExtendedKeyUsage(); return (xku != null) && !xku.contains(mustHaveXku); } public static boolean xkuReject(X509AuxCertificate x, String[] mustHaveOneOfXku) throws CertificateException { List<String> xku = x.getExtendedKeyUsage(); if(xku == null) { return false; } for (String mustHaveXku : mustHaveOneOfXku) { if(xku.contains(mustHaveXku)) { return false; } } return true; } /** * c: ns_reject */ public static boolean nsReject(X509AuxCertificate x, int mustHaveCertType) throws CertificateException { Integer nsCertType = x.getNsCertType(); return (nsCertType != null) && (nsCertType & mustHaveCertType) == 0; } /** * c: purpose_smime */ public static int purposeSMIME(X509AuxCertificate x, int ca) throws CertificateException { if(xkuReject(x,XKU_EMAIL_PROTECT)) { return 0; // must allow email protection } if(ca != 0) { int ca_ret = checkCA(x); if(ca_ret == 0) { return 0; } Integer nsCertType = x.getNsCertType(); boolean v2 = nsCertType != null && (nsCertType & X509Utils.NS_SMIME_CA) != 0; if(ca_ret != 5 || v2) { return ca_ret; } else { return 0; } } Integer nsCertType = x.getNsCertType(); if (nsCertType != null) { if ((nsCertType & X509Utils.NS_SMIME) != 0) { return 1; } if ((nsCertType & X509Utils.NS_SSL_CLIENT) != 0) { return 2; } return 0; } return 1; } /** * c: check_purpose_ssl_client */ final static CheckPurposeFunction checkPurposeSSLClient = new CheckPurposeFunction() { public int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException { if ( xkuReject(x, XKU_SSL_CLIENT) ) { return 0; } if (ca.intValue() != 0) { return checkSSLCA(x); } if ( x.getKeyUsage() != null && ! x.getKeyUsage()[0] ) { return 0; } if ( nsReject(x, X509Utils.NS_SSL_CLIENT) ) { // when the cert has nsCertType, it must include NS_SSL_CLIENT return 0; } return 1; } }; /** * c: check_purpose_ssl_server */ final static CheckPurposeFunction checkPurposeSSLServer = new CheckPurposeFunction() { public int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException { if ( xkuReject(x, XKU_SSL_SERVER) ) { return 0; } if ( ca.intValue() != 0 ) { return checkSSLCA(x); } if ( nsReject(x, X509Utils.NS_SSL_SERVER) ) { // when the cert has nsCertType, it must include NS_SSL_SERVER return 0; } /* Now as for keyUsage: we'll at least need to sign OR encipher */ if ( x.getKeyUsage() != null && ! ( x.getKeyUsage()[0] || x.getKeyUsage()[2] ) ) { return 0; } return 1; } }; /** * c: check_purpose_ns_ssl_server */ final static CheckPurposeFunction checkPurposeNSSSLServer = new CheckPurposeFunction() { public int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException { int ret = checkPurposeSSLServer.call(purpose, x, ca); if ( ret == 0 || ca != 0 ) { return ret; } if ( x.getKeyUsage() != null && ! x.getKeyUsage()[2] ) { return 0; } return 1; } }; /** * c: check_purpose_smime_sign */ final static CheckPurposeFunction checkPurposeSMIMESign = new CheckPurposeFunction() { public int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException { int ret = purposeSMIME(x, ca); if ( ret == 0 || ca != 0 ) { return ret; } if ( x.getKeyUsage() != null && ( ! x.getKeyUsage()[0] || ! x.getKeyUsage()[1] ) ) { return 0; } return ret; } }; /** * c: check_purpose_smime_encrypt */ final static CheckPurposeFunction checkPurposeSMIMEEncrypt = new CheckPurposeFunction() { public int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException { int ret = purposeSMIME(x,ca); if ( ret == 0 || ca != 0 ) { return ret; } if ( x.getKeyUsage() != null && ! x.getKeyUsage()[2] ) { return 0; } return ret; } }; /** * c: check_purpose_crl_sign */ final static CheckPurposeFunction checkPurposeCRLSign = new CheckPurposeFunction() { public int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException { if ( ca.intValue() != 0 ) { int ca_ret = checkCA(x); if ( ca_ret != 2 ) { return ca_ret; } return 0; } if ( x.getKeyUsage() != null && ! x.getKeyUsage()[6] ) { return 0; } return 1; } }; /** * c: no_check */ final static CheckPurposeFunction noCheck = new CheckPurposeFunction() { public int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException { return 1; } }; /** * c: ocsp_helper */ final static CheckPurposeFunction oscpHelper = new CheckPurposeFunction() { public int call(Purpose purpose, X509AuxCertificate x, Integer ca) throws CertificateException { if ( ca.intValue() != 0 ) { return checkCA(x); } return 1; } }; private final static Purpose[] xstandard = new Purpose[] { new Purpose(X509Utils.X509_PURPOSE_SSL_CLIENT, X509Utils.X509_TRUST_SSL_CLIENT, 0, checkPurposeSSLClient, "SSL client", "sslclient", null), new Purpose(X509Utils.X509_PURPOSE_SSL_SERVER, X509Utils.X509_TRUST_SSL_SERVER, 0, checkPurposeSSLServer, "SSL server", "sslserver", null), new Purpose(X509Utils.X509_PURPOSE_NS_SSL_SERVER, X509Utils.X509_TRUST_SSL_SERVER, 0, checkPurposeNSSSLServer, "Netscape SSL server", "nssslserver", null), new Purpose(X509Utils.X509_PURPOSE_SMIME_SIGN, X509Utils.X509_TRUST_EMAIL, 0, checkPurposeSMIMESign, "S/MIME signing", "smimesign", null), new Purpose(X509Utils.X509_PURPOSE_SMIME_ENCRYPT, X509Utils.X509_TRUST_EMAIL, 0, checkPurposeSMIMEEncrypt, "S/MIME encryption", "smimeencrypt", null), new Purpose(X509Utils.X509_PURPOSE_CRL_SIGN, X509Utils.X509_TRUST_COMPAT, 0, checkPurposeCRLSign, "CRL signing", "crlsign", null), new Purpose(X509Utils.X509_PURPOSE_ANY, X509Utils.X509_TRUST_DEFAULT, 0, noCheck, "Any Purpose", "any", null), new Purpose(X509Utils.X509_PURPOSE_OCSP_HELPER, X509Utils.X509_TRUST_COMPAT, 0, oscpHelper, "OCSP helper", "ocsphelper", null), }; }// X509_PURPOSE