/***** 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