/***** 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.security.GeneralSecurityException; import java.security.PublicKey; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.security.cert.X509Extension; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Sequence; import org.jruby.ext.openssl.SecurityHelper; /** * c: X509_STORE_CTX * * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a> */ public class StoreContext { private static final Integer ZERO = 0; private final Store store; private int currentMethod; X509AuxCertificate certificate; List<X509AuxCertificate> untrusted; List<X509CRL> crls; public VerifyParameter verifyParameter; public List<X509AuxCertificate> otherContext; public StoreContext(final Store store) { this.store = store; } public static interface CheckPolicyFunction extends Function1<StoreContext> { public static final CheckPolicyFunction EMPTY = new CheckPolicyFunction(){ public int call(StoreContext context) { return -1; } }; } Store.VerifyFunction verify; Store.VerifyCallbackFunction verifyCallback; Store.GetIssuerFunction getIssuer; Store.CheckIssuedFunction checkIssued; Store.CheckRevocationFunction checkRevocation; Store.GetCRLFunction getCRL; Store.CheckCRLFunction checkCRL; Store.CertificateCRLFunction certificateCRL; CheckPolicyFunction checkPolicy; Store.CleanupFunction cleanup; public boolean isValid; public int lastUntrusted; public List<X509AuxCertificate> chain; //List<X509AuxCertificate> public PolicyTree tree; public int explicitPolicy; public int error; public int errorDepth; X509AuxCertificate currentCertificate; X509AuxCertificate currentIssuer; X509CRL currentCRL; List<Object> extraData; public Store getStore() { return store; } /** * c: X509_STORE_CTX_set_depth */ public void setDepth(int depth) { verifyParameter.setDepth(depth); } /** * c: X509_STORE_CTX_set_app_data */ public void setApplicationData(Object data) { setExtraData(0, data); } /** * c: X509_STORE_CTX_get_app_data */ public Object getApplicationData() { return getExtraData(0); } /** * c: X509_STORE_CTX_get1_issuer */ int getFirstIssuer(final X509AuxCertificate[] issuers, final X509AuxCertificate x) throws Exception { final Name xn = new Name( x.getIssuerX500Principal() ); final X509Object[] s_obj = new X509Object[1]; int ok = store == null ? 0 : getBySubject(X509Utils.X509_LU_X509, xn, s_obj); if ( ok != X509Utils.X509_LU_X509 ) { if ( ok == X509Utils.X509_LU_RETRY ) { X509Error.addError(X509Utils.X509_R_SHOULD_RETRY); return -1; } else if ( ok != X509Utils.X509_LU_FAIL ) { return -1; } return 0; } X509Object obj = s_obj[0]; if ( checkIssued.call(this, x, ((Certificate) obj).x509) != 0 ) { issuers[0] = ((Certificate) obj).x509; return 1; } List<X509Object> objects = store.getObjects(); int idx = X509Object.indexBySubject(objects, X509Utils.X509_LU_X509, xn); if ( idx == -1 ) return 0; /* Look through all matching certificates for a suitable issuer */ for ( int i = idx; i < objects.size(); i++ ) { final X509Object pobj = objects.get(i); if ( pobj.type() != X509Utils.X509_LU_X509 ) { return 0; } final X509AuxCertificate x509 = ((Certificate) pobj).x509; if ( ! xn.equalTo( x509.getSubjectX500Principal() ) ) { return 0; } if ( checkIssued.call(this, x, x509) != 0 ) { issuers[0] = x509; return 1; } } return 0; } public static List<X509AuxCertificate> ensureAux(final Collection<X509Certificate> input) { if ( input == null ) return null; List<X509AuxCertificate> out = new ArrayList<X509AuxCertificate>(input.size()); for ( X509Certificate cert : input ) out.add( ensureAux(cert) ); return out; } public static List<X509AuxCertificate> ensureAux(final X509Certificate[] input) { if ( input == null ) return null; List<X509AuxCertificate> out = new ArrayList<X509AuxCertificate>(input.length); for ( X509Certificate cert : input ) out.add( ensureAux(cert) ); return out; } public static X509AuxCertificate ensureAux(final X509Certificate input) { if ( input == null ) return null; if ( input instanceof X509AuxCertificate ) { return (X509AuxCertificate) input; } return new X509AuxCertificate(input); } /** * c: X509_STORE_CTX_init */ public int init(X509AuxCertificate cert, List<X509AuxCertificate> chain) { int ret = 1; this.currentMethod = 0; this.certificate = cert; this.untrusted = chain; this.crls = null; this.lastUntrusted = 0; this.otherContext = null; this.isValid = false; this.chain = null; this.error = 0; this.explicitPolicy = 0; this.errorDepth = 0; this.currentCertificate = null; this.currentIssuer = null; this.tree = null; this.verifyParameter = new VerifyParameter(); if ( store != null ) { ret = verifyParameter.inherit(store.verifyParameter); } else { verifyParameter.flags |= X509Utils.X509_VP_FLAG_DEFAULT | X509Utils.X509_VP_FLAG_ONCE; } if ( store != null ) { verifyCallback = store.getVerifyCallback(); cleanup = store.cleanup; } else { cleanup = Store.CleanupFunction.EMPTY; } if ( ret != 0 ) { ret = verifyParameter.inherit(VerifyParameter.lookup("default")); } if ( ret == 0 ) { X509Error.addError(X509Utils.ERR_R_MALLOC_FAILURE); return 0; } this.checkIssued = defaultCheckIssued; this.getIssuer = getFirstIssuer; this.verifyCallback = nullCallback; this.verify = internalVerify; this.checkRevocation = defaultCheckRevocation; this.getCRL = defaultGetCRL; this.checkCRL = defaultCheckCRL; this.certificateCRL = defaultCertificateCRL; if ( store != null ) { if ( store.checkIssued != null && store.checkIssued != Store.CheckIssuedFunction.EMPTY ) { this.checkIssued = store.checkIssued; } if ( store.getIssuer != null && store.getIssuer != Store.GetIssuerFunction.EMPTY ) { this.getIssuer = store.getIssuer; } if ( store.verifyCallback != null && store.verifyCallback != Store.VerifyCallbackFunction.EMPTY ) { this.verifyCallback = store.verifyCallback; } if ( store.verify != null && store.verify != Store.VerifyFunction.EMPTY) { this.verify = store.verify; } if ( store.checkRevocation != null && store.checkRevocation != Store.CheckRevocationFunction.EMPTY) { this.checkRevocation = store.checkRevocation; } if ( store.getCRL != null && store.getCRL != Store.GetCRLFunction.EMPTY) { this.getCRL = store.getCRL; } if( store.checkCRL != null && store.checkCRL != Store.CheckCRLFunction.EMPTY) { this.checkCRL = store.checkCRL; } if ( store.certificateCRL != null && store.certificateCRL != Store.CertificateCRLFunction.EMPTY) { this.certificateCRL = store.certificateCRL; } } this.checkPolicy = defaultCheckPolicy; // getExtraData(); return 1; } /** * c: X509_STORE_CTX_trusted_stack */ public void trustedStack(List<X509AuxCertificate> sk) { otherContext = sk; getIssuer = getIssuerStack; } /** * c: X509_STORE_CTX_cleanup */ public void cleanup() throws Exception { if (cleanup != null && cleanup != Store.CleanupFunction.EMPTY) { cleanup.call(this); } verifyParameter = null; tree = null; chain = null; extraData = null; } /** * c: find_issuer */ public X509AuxCertificate findIssuer(final List<X509AuxCertificate> certs, final X509AuxCertificate cert) throws Exception { for ( X509AuxCertificate issuer : certs ) { if ( checkIssued.call(this, cert, issuer) != 0 ) { return issuer; } } return null; } public List<Object> getExtraData() { if ( this.extraData != null ) return this.extraData; ArrayList<Object> extraData = new ArrayList<Object>(8); extraData.add(null); extraData.add(null); extraData.add(null); extraData.add(null); extraData.add(null); extraData.add(null); return this.extraData = extraData; } /** * c: X509_STORE_CTX_set_ex_data */ public int setExtraData(int idx, Object data) { getExtraData().set(idx, data); return 1; } /** * c: X509_STORE_CTX_get_ex_data */ public Object getExtraData(int idx) { return getExtraData().get(idx); } /** * c: X509_STORE_CTX_get_error */ public int getError() { return error; } /** * c: X509_STORE_CTX_set_error */ public void setError(int s) { this.error = s; } /** * c: X509_STORE_CTX_get_error_depth */ public int getErrorDepth() { return errorDepth; } /** * c: X509_STORE_CTX_get_current_cert */ public X509AuxCertificate getCurrentCertificate() { return currentCertificate; } public X509CRL getCurrentCRL() { return currentCRL; } /** * c: X509_STORE_CTX_get_chain */ public List<X509AuxCertificate> getChain() { return chain; } /** * c: X509_STORE_CTX_get1_chain */ public List<X509AuxCertificate> getFirstChain() { if ( chain == null ) return null; return new ArrayList<X509AuxCertificate>(chain); } /** * c: X509_STORE_CTX_set_cert */ public void setCertificate(X509AuxCertificate x) { this.certificate = x; } public void setCertificate(X509Certificate x) { this.certificate = ensureAux(x); } /** * c: X509_STORE_CTX_set_chain */ public void setChain(List<X509Certificate> chain) { this.untrusted = ensureAux(chain); } public void setChain(X509Certificate[] sk) { this.untrusted = ensureAux(sk); } /** * c: X509_STORE_CTX_set0_crls */ public void setCRLs(List<X509CRL> sk) { this.crls = sk; } /** * c: X509_STORE_CTX_set_purpose */ public int setPurpose(int purpose) { return purposeInherit(0, purpose, 0); } /** * c: X509_STORE_CTX_set_trust */ public int setTrust(int trust) { return purposeInherit(0, 0, trust); } /* private void resetSettingsToWithoutStore() { store = null; this.verifyParameter = new VerifyParameter(); this.verifyParameter.flags |= X509Utils.X509_VP_FLAG_DEFAULT | X509Utils.X509_VP_FLAG_ONCE; this.verifyParameter.inherit(VerifyParameter.lookup("default")); this.cleanup = Store.CleanupFunction.EMPTY; this.checkIssued = defaultCheckIssued; this.getIssuer = getFirstIssuer; this.verifyCallback = nullCallback; this.verify = internalVerify; this.checkRevocation = defaultCheckRevocation; this.getCRL = defaultGetCRL; this.checkCRL = defaultCheckCRL; this.certificateCRL = defaultCertificateCRL; } */ /** * c: SSL_CTX_load_verify_locations */ /* public int loadVerifyLocations(Ruby runtime, String CAfile, String CApath) { boolean reset = false; try { if ( store == null ) { reset = true; store = new Store(); this.verifyParameter.inherit(store.verifyParameter); verifyParameter.inherit(VerifyParameter.lookup("default")); this.cleanup = store.cleanup; if ( store.checkIssued != null && store.checkIssued != Store.CheckIssuedFunction.EMPTY ) { this.checkIssued = store.checkIssued; } if ( store.getIssuer != null && store.getIssuer != Store.GetIssuerFunction.EMPTY ) { this.getIssuer = store.getIssuer; } if ( store.verify != null && store.verify != Store.VerifyFunction.EMPTY ) { this.verify = store.verify; } if ( store.verifyCallback != null && store.verifyCallback != Store.VerifyCallbackFunction.EMPTY ) { this.verifyCallback = store.verifyCallback; } if ( store.checkRevocation != null && store.checkRevocation != Store.CheckRevocationFunction.EMPTY ) { this.checkRevocation = store.checkRevocation; } if ( store.getCRL != null && store.getCRL != Store.GetCRLFunction.EMPTY ) { this.getCRL = store.getCRL; } if ( store.checkCRL != null && store.checkCRL != Store.CheckCRLFunction.EMPTY ) { this.checkCRL = store.checkCRL; } if ( store.certificateCRL != null && store.certificateCRL != Store.CertificateCRLFunction.EMPTY ) { this.certificateCRL = store.certificateCRL; } } final int ret = store.loadLocations(runtime, CAfile, CApath); if ( ret == 0 && reset ) resetSettingsToWithoutStore(); return ret; } catch (Exception e) { if ( reset ) resetSettingsToWithoutStore(); return 0; } } */ /** * c: X509_STORE_CTX_purpose_inherit */ public int purposeInherit(int defaultPurpose,int purpose, int trust) { int idx; if(purpose == 0) { purpose = defaultPurpose; } if(purpose != 0) { idx = Purpose.getByID(purpose); if(idx == -1) { X509Error.addError(X509Utils.X509_R_UNKNOWN_PURPOSE_ID); return 0; } Purpose ptmp = Purpose.getFirst(idx); if(ptmp.trust == X509Utils.X509_TRUST_DEFAULT) { idx = Purpose.getByID(defaultPurpose); if(idx == -1) { X509Error.addError(X509Utils.X509_R_UNKNOWN_PURPOSE_ID); return 0; } ptmp = Purpose.getFirst(idx); } if(trust == 0) { trust = ptmp.trust; } } if(trust != 0) { idx = Trust.getByID(trust); if(idx == -1) { X509Error.addError(X509Utils.X509_R_UNKNOWN_TRUST_ID); return 0; } } if(purpose != 0 && verifyParameter.purpose == 0) { verifyParameter.purpose = purpose; } if(trust != 0 && verifyParameter.trust == 0) { verifyParameter.trust = trust; } return 1; } /** * c: X509_STORE_CTX_set_flags */ public void setFlags(long flags) { verifyParameter.setFlags(flags); } /** * c: X509_STORE_CTX_set_time */ public void setTime(long flags,Date t) { verifyParameter.setTime(t); } /** * c: X509_STORE_CTX_set_verify_cb */ public void setVerifyCallback(Store.VerifyCallbackFunction verifyCallback) { this.verifyCallback = verifyCallback; } /** * c: X509_STORE_CTX_get0_policy_tree */ PolicyTree getPolicyTree() { return tree; } /** * c: X509_STORE_CTX_get_explicit_policy */ public int getExplicitPolicy() { return explicitPolicy; } /** * c: X509_STORE_CTX_get0_param */ public VerifyParameter getParam() { return verifyParameter; } /** * c: X509_STORE_CTX_set0_param */ public void setParam(VerifyParameter param) { this.verifyParameter = param; } /** * c: X509_STORE_CTX_set_default */ public int setDefault(String name) { VerifyParameter p = VerifyParameter.lookup(name); if ( p == null ) return 0; return verifyParameter.inherit(p); } /** * c: X509_STORE_get_by_subject (it gets X509_STORE_CTX as the first parameter) */ public int getBySubject(int type,Name name,X509Object[] ret) throws Exception { Store c = store; X509Object tmp = X509Object.retrieveBySubject(c.getObjects(),type,name); if ( tmp == null ) { List<Lookup> certificateMethods = c.getCertificateMethods(); for(int i=currentMethod; i<certificateMethods.size(); i++) { Lookup lu = certificateMethods.get(i); X509Object[] stmp = new X509Object[1]; int j = lu.bySubject(type,name,stmp); if ( j < 0 ) { currentMethod = i; return j; } else if( j > 0 ) { tmp = stmp[0]; break; } } currentMethod = 0; if ( tmp == null ) return 0; } ret[0] = tmp; return 1; } /** * c: X509_verify_cert */ public int verifyCertificate() throws Exception { X509AuxCertificate x, xtmp = null, chain_ss = null; //X509_NAME xn; int bad_chain = 0, depth, i, num; if ( certificate == null ) { X509Error.addError(X509Utils.X509_R_NO_CERT_SET_FOR_US_TO_VERIFY); return -1; } // first we make sure the chain we are going to build is // present and that the first entry is in place if ( chain == null ) { chain = new ArrayList<X509AuxCertificate>(); chain.add(certificate); lastUntrusted = 1; } // We use a temporary STACK so we can chop and hack at it List<X509AuxCertificate> sktmp = null; if ( untrusted != null ) { sktmp = new ArrayList<X509AuxCertificate>(untrusted); } num = chain.size(); x = chain.get(num - 1); depth = verifyParameter.depth; for(;;) { if ( depth < num ) break; if ( checkIssued.call(this, x, x) != 0 ) break; if ( sktmp != null ) { xtmp = findIssuer(sktmp, x); if ( xtmp != null ) { chain.add(xtmp); sktmp.remove(xtmp); lastUntrusted++; x = xtmp; num++; continue; } } break; } // at this point, chain should contain a list of untrusted // certificates. We now need to add at least one trusted one, // if possible, otherwise we complain. // Examine last certificate in chain and see if it is self signed. i = chain.size(); x = chain.get(i - 1); if ( checkIssued.call(this, x, x) != 0 ) { // we have a self signed certificate if ( chain.size() == 1 ) { // We have a single self signed certificate: see if // we can find it in the store. We must have an exact // match to avoid possible impersonation. X509AuxCertificate[] p_xtmp = new X509AuxCertificate[]{ xtmp }; int ok = getIssuer.call(this, p_xtmp, x); xtmp = p_xtmp[0]; if ( ok <= 0 || ! x.equals(xtmp) ) { error = X509Utils.V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; currentCertificate = x; errorDepth = i-1; bad_chain = 1; ok = verifyCallback.call(this, ZERO); if ( ok == 0 ) return ok; } else { // We have a match: replace certificate with store version // so we get any trust settings. x = xtmp; chain.set(i-1,x); lastUntrusted = 0; } } else { // extract and save self signed certificate for later use chain_ss = chain.remove(chain.size()-1); lastUntrusted--; num--; x = chain.get(num-1); } } // We now lookup certs from the certificate store for(;;) { // If we have enough, we break if ( depth < num ) break; //xn = new X509_NAME(x.getIssuerX500Principal()); // If we are self signed, we break if ( checkIssued.call(this, x, x) != 0 ) break; X509AuxCertificate[] p_xtmp = new X509AuxCertificate[]{ xtmp }; int ok = getIssuer.call(this, p_xtmp, x); xtmp = p_xtmp[0]; if ( ok < 0 ) return ok; if ( ok == 0 ) break; x = xtmp; chain.add(x); num++; } /* we now have our chain, lets check it... */ //xn = new X509_NAME(x.getIssuerX500Principal()); /* Is last certificate looked up self signed? */ if ( checkIssued.call(this, x, x) == 0 ) { if ( chain_ss == null || checkIssued.call(this, x, chain_ss) == 0 ) { if(lastUntrusted >= num) { error = X509Utils.V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; } else { error = X509Utils.V_ERR_UNABLE_TO_GET_ISSUER_CERT; } currentCertificate = x; } else { chain.add(chain_ss); num++; lastUntrusted = num; currentCertificate = chain_ss; error = X509Utils.V_ERR_SELF_SIGNED_CERT_IN_CHAIN; } errorDepth = num - 1; bad_chain = 1; int ok = verifyCallback.call(this, ZERO); if ( ok == 0 ) return ok; } // We have the chain complete: now we need to check its purpose int ok = checkChainExtensions(); if ( ok == 0 ) return ok; /* TODO: Check name constraints (from 1.0.0) */ // The chain extensions are OK: check trust if ( verifyParameter.trust > 0 ) ok = checkTrust(); if ( ok == 0 ) return ok; // Check revocation status: we do this after copying parameters // because they may be needed for CRL signature verification. ok = checkRevocation.call(this); if ( ok == 0 ) return ok; /* At this point, we have a chain and need to verify it */ if ( verify != null && verify != Store.VerifyFunction.EMPTY ) { ok = verify.call(this); } else { ok = internalVerify.call(this); } if ( ok == 0 ) return ok; /* TODO: RFC 3779 path validation, now that CRL check has been done (from 1.0.0) */ /* If we get this far evaluate policies */ if ( bad_chain == 0 && (verifyParameter.flags & X509Utils.V_FLAG_POLICY_CHECK) != 0 ) { ok = checkPolicy.call(this); } return ok; } private final static Set<String> CRITICAL_EXTENSIONS = new HashSet<String>(8); static { CRITICAL_EXTENSIONS.add("2.16.840.1.113730.1.1"); // netscape cert type, NID 71 CRITICAL_EXTENSIONS.add("2.5.29.15"); // key usage, NID 83 CRITICAL_EXTENSIONS.add("2.5.29.17"); // subject alt name, NID 85 CRITICAL_EXTENSIONS.add("2.5.29.19"); // basic constraints, NID 87 CRITICAL_EXTENSIONS.add("2.5.29.37"); // ext key usage, NID 126 CRITICAL_EXTENSIONS.add("1.3.6.1.5.5.7.1.14"); // proxy cert info, NID 661 } private static boolean supportsCriticalExtension(final String oid) { return CRITICAL_EXTENSIONS.contains(oid); } private static boolean unhandledCritical(final X509Extension ext) { final Set<String> criticalOIDs = ext.getCriticalExtensionOIDs(); if ( criticalOIDs == null || criticalOIDs.size() == 0 ) { return false; } for ( final String oid : criticalOIDs ) { if ( ! supportsCriticalExtension(oid) ) return true; } return false; } /** * c: check_chain_extensions */ public int checkChainExtensions() throws Exception { int ok, must_be_ca; X509AuxCertificate x; int proxy_path_length = 0; int allow_proxy_certs = (verifyParameter.flags & X509Utils.V_FLAG_ALLOW_PROXY_CERTS) != 0 ? 1 : 0; must_be_ca = -1; try { final String allowProxyCerts = System.getenv("OPENSSL_ALLOW_PROXY_CERTS"); if ( allowProxyCerts != null && ! "false".equalsIgnoreCase(allowProxyCerts) ) { allow_proxy_certs = 1; } } catch (SecurityException e) { /* ignore if we can't use System.getenv */ } for ( int i = 0; i < lastUntrusted; i++ ) { int ret; x = chain.get(i); if ( (verifyParameter.flags & X509Utils.V_FLAG_IGNORE_CRITICAL) == 0 && unhandledCritical(x) ) { error = X509Utils.V_ERR_UNHANDLED_CRITICAL_EXTENSION; errorDepth = i; currentCertificate = x; ok = verifyCallback.call(this, ZERO); if ( ok == 0 ) return ok; } if ( allow_proxy_certs == 0 && x.getExtensionValue("1.3.6.1.5.5.7.1.14") != null ) { error = X509Utils.V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED; errorDepth = i; currentCertificate = x; ok = verifyCallback.call(this, ZERO); if ( ok == 0 ) return ok; } ret = Purpose.checkCA(x); switch(must_be_ca) { case -1: if((verifyParameter.flags & X509Utils.V_FLAG_X509_STRICT) != 0 && ret != 1 && ret != 0) { ret = 0; error = X509Utils.V_ERR_INVALID_CA; } else { ret = 1; } break; case 0: if(ret != 0) { ret = 0; error = X509Utils.V_ERR_INVALID_NON_CA; } else { ret = 1; } break; default: if(ret == 0 || ((verifyParameter.flags & X509Utils.V_FLAG_X509_STRICT) != 0 && ret != 1)) { ret = 0; error = X509Utils.V_ERR_INVALID_CA; } else { ret = 1; } break; } if(ret == 0) { errorDepth = i; currentCertificate = x; ok = verifyCallback.call(this, ZERO); if ( ok == 0 ) return ok; } if(verifyParameter.purpose > 0) { ret = Purpose.checkPurpose(x,verifyParameter.purpose, must_be_ca > 0 ? 1 : 0); if(ret == 0 || ((verifyParameter.flags & X509Utils.V_FLAG_X509_STRICT) != 0 && ret != 1)) { error = X509Utils.V_ERR_INVALID_PURPOSE; errorDepth = i; currentCertificate = x; ok = verifyCallback.call(this, ZERO); if(ok == 0) { return ok; } } } if(i > 1 && x.getBasicConstraints() != -1 && x.getBasicConstraints() != Integer.MAX_VALUE && (i > (x.getBasicConstraints() + proxy_path_length + 1))) { error = X509Utils.V_ERR_PATH_LENGTH_EXCEEDED; errorDepth = i; currentCertificate = x; ok = verifyCallback.call(this, ZERO); if ( ok == 0 ) return ok; } if(x.getExtensionValue("1.3.6.1.5.5.7.1.14") != null) { ASN1Sequence pci = (ASN1Sequence)new ASN1InputStream(x.getExtensionValue("1.3.6.1.5.5.7.1.14")).readObject(); if(pci.size() > 0 && pci.getObjectAt(0) instanceof ASN1Integer) { int pcpathlen = ((ASN1Integer)pci.getObjectAt(0)).getValue().intValue(); if(i > pcpathlen) { error = X509Utils.V_ERR_PROXY_PATH_LENGTH_EXCEEDED; errorDepth = i; currentCertificate = x; ok = verifyCallback.call(this, ZERO); if ( ok == 0 ) return ok; } } proxy_path_length++; must_be_ca = 0; } else { must_be_ca = 1; } } return 1; } /** * c: X509_check_trust */ public int checkTrust() throws Exception { int i,ok; X509AuxCertificate x; i = chain.size()-1; x = chain.get(i); ok = Trust.checkTrust(x, verifyParameter.trust, 0); if ( ok == X509Utils.X509_TRUST_TRUSTED ) { return 1; } errorDepth = 1; currentCertificate = x; if ( ok == X509Utils.X509_TRUST_REJECTED ) { error = X509Utils.V_ERR_CERT_REJECTED; } else { error = X509Utils.V_ERR_CERT_UNTRUSTED; } return verifyCallback.call(this, ZERO); } /** * c: check_cert_time */ public int checkCertificateTime(X509AuxCertificate x) throws Exception { final Date pTime; if ( (verifyParameter.flags & X509Utils.V_FLAG_USE_CHECK_TIME) != 0 ) { pTime = this.verifyParameter.checkTime; } else { pTime = Calendar.getInstance().getTime(); } if ( ! x.getNotBefore().before(pTime) ) { error = X509Utils.V_ERR_CERT_NOT_YET_VALID; currentCertificate = x; if ( verifyCallback.call(this, ZERO) == 0 ) { return 0; } } if ( ! x.getNotAfter().after(pTime) ) { error = X509Utils.V_ERR_CERT_HAS_EXPIRED; currentCertificate = x; if ( verifyCallback.call(this, ZERO) == 0 ) { return 0; } } return 1; } /** * c: check_cert */ public int checkCertificate() throws Exception { final X509CRL[] crl = new X509CRL[1]; X509AuxCertificate x; int ok, cnum; cnum = errorDepth; x = chain.get(cnum); currentCertificate = x; ok = getCRL.call(this, crl, x); if ( ok == 0 ) { error = X509Utils.V_ERR_UNABLE_TO_GET_CRL; ok = verifyCallback.call(this, ZERO); currentCRL = null; return ok; } currentCRL = crl[0]; ok = checkCRL.call(this, crl[0]); if ( ok == 0 ) { currentCRL = null; return ok; } ok = certificateCRL.call(this, crl[0], x); currentCRL = null; return ok; } /** * c: check_crl_time */ public int checkCRLTime(X509CRL crl, int notify) throws Exception { currentCRL = crl; final Date pTime; if ( (verifyParameter.flags & X509Utils.V_FLAG_USE_CHECK_TIME) != 0 ) { pTime = this.verifyParameter.checkTime; } else { pTime = Calendar.getInstance().getTime(); } if ( ! crl.getThisUpdate().before(pTime) ) { error = X509Utils.V_ERR_CRL_NOT_YET_VALID; if ( notify == 0 || verifyCallback.call(this, ZERO) == 0 ) { return 0; } } if ( crl.getNextUpdate() != null && !crl.getNextUpdate().after(pTime) ) { error = X509Utils.V_ERR_CRL_HAS_EXPIRED; if ( notify == 0 || verifyCallback.call(this, ZERO) == 0 ) { return 0; } } currentCRL = null; return 1; } /** * c: get_crl_sk */ public int getCRLStack(X509CRL[] pcrl, Name name, List<X509CRL> crls) throws Exception { X509CRL bestCrl = null; if ( crls != null ) { for ( final X509CRL crl : crls ) { if( ! name.equalTo( crl.getIssuerX500Principal() ) ) { continue; } if ( checkCRLTime(crl, 0) != 0 ) { pcrl[0] = crl; return 1; } bestCrl = crl; } } if ( bestCrl != null ) { pcrl[0] = bestCrl; } return 0; } final static Store.GetIssuerFunction getFirstIssuer = new Store.GetIssuerFunction() { public int call(StoreContext context, X509AuxCertificate[] issuer, X509AuxCertificate cert) throws Exception { return context.getFirstIssuer(issuer, cert); } }; /** * c: get_issuer_sk */ final static Store.GetIssuerFunction getIssuerStack = new Store.GetIssuerFunction() { public int call(StoreContext context, X509AuxCertificate[] issuer, X509AuxCertificate x) throws Exception { issuer[0] = context.findIssuer(context.otherContext, x); if ( issuer[0] != null ) { return 1; } else { return 0; } } }; /** * c: check_issued */ final static Store.CheckIssuedFunction defaultCheckIssued = new Store.CheckIssuedFunction() { public int call(StoreContext context, X509AuxCertificate cert, X509AuxCertificate issuer) throws Exception { int ret = X509Utils.checkIfIssuedBy(issuer, cert); if ( ret == X509Utils.V_OK ) return 1; if ( (context.verifyParameter.flags & X509Utils.V_FLAG_CB_ISSUER_CHECK) == 0 ) { return 0; } context.error = ret; context.currentCertificate = cert; context.currentIssuer = issuer; return context.verifyCallback.call(context, ZERO); } }; /** * c: null_callback */ final static Store.VerifyCallbackFunction nullCallback = new Store.VerifyCallbackFunction() { public int call(StoreContext context, Integer outcome) { return outcome.intValue(); } }; /** * c: internal_verify */ final static Store.VerifyFunction internalVerify = new Store.VerifyFunction() { public int call(final StoreContext context) throws Exception { Store.VerifyCallbackFunction verifyCallback = context.verifyCallback; int n = context.chain.size(); context.errorDepth = n - 1; n--; X509AuxCertificate xi = context.chain.get(n); X509AuxCertificate xs = null; int ok; if ( context.checkIssued.call(context,xi,xi) != 0 ) { xs = xi; } else { if ( n <= 0 ) { context.error = X509Utils.V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE; context.currentCertificate = xi; ok = verifyCallback.call(context, ZERO); return ok; } else { n--; context.errorDepth = n; xs = context.chain.get(n); } } while ( n >= 0 ) { context.errorDepth = n; if ( ! xs.isValid() ) { try { xs.verify(xi.getPublicKey()); } catch(Exception e) { /* System.err.println("n: " + n); System.err.println("verifying: " + xs); System.err.println("verifying with issuer?: " + xi); System.err.println("verifying with issuer.key?: " + xi.getPublicKey()); System.err.println("exception: " + e); */ context.error = X509Utils.V_ERR_CERT_SIGNATURE_FAILURE; context.currentCertificate = xs; ok = verifyCallback.call(context, ZERO); if ( ok == 0 ) return ok; } } xs.setValid(true); ok = context.checkCertificateTime(xs); if ( ok == 0 ) return ok; context.currentIssuer = xi; context.currentCertificate = xs; ok = verifyCallback.call(context, Integer.valueOf(1)); if ( ok == 0 ) return ok; n--; if ( n >= 0 ) { xi = xs; xs = context.chain.get(n); } } ok = 1; return ok; } }; /** * c: check_revocation */ final static Store.CheckRevocationFunction defaultCheckRevocation = new Store.CheckRevocationFunction() { public int call(final StoreContext context) throws Exception { if ( (context.verifyParameter.flags & X509Utils.V_FLAG_CRL_CHECK) == 0 ) { return 1; } final int last; if ( (context.verifyParameter.flags & X509Utils.V_FLAG_CRL_CHECK_ALL) != 0 ) { last = context.chain.size() - 1; } else { last = 0; } int ok; for ( int i=0; i<=last; i++ ) { context.errorDepth = i; ok = context.checkCertificate(); if ( ok == 0 ) return 0; } return 1; } }; /** * c: get_crl */ final static Store.GetCRLFunction defaultGetCRL = new Store.GetCRLFunction() { public int call(final StoreContext context, final X509CRL[] crls, X509AuxCertificate x) throws Exception { final Name name = new Name( x.getIssuerX500Principal() ); final X509CRL[] crl = new X509CRL[1]; int ok = context.getCRLStack(crl, name, context.crls); if ( ok != 0 ) { crls[0] = crl[0]; return 1; } final X509Object[] xobj = new X509Object[1]; ok = context.getBySubject(X509Utils.X509_LU_CRL, name, xobj); if ( ok == 0 ) { if ( crl[0] != null ) { crls[0] = crl[0]; return 1; } return 0; } crls[0] = (X509CRL) ( (CRL) xobj[0] ).crl; return 1; } }; /** * c: check_crl */ final static Store.CheckCRLFunction defaultCheckCRL = new Store.CheckCRLFunction() { public int call(final StoreContext context, final X509CRL crl) throws Exception { final int errorDepth = context.errorDepth; final int lastInChain = context.chain.size() - 1; int ok; final X509AuxCertificate issuer; if ( errorDepth < lastInChain ) { issuer = context.chain.get(errorDepth + 1); } else { issuer = context.chain.get(lastInChain); if ( context.checkIssued.call(context,issuer,issuer) == 0 ) { context.error = X509Utils.V_ERR_UNABLE_TO_GET_CRL_ISSUER; ok = context.verifyCallback.call(context, ZERO); if ( ok == 0 ) return ok; } } if ( issuer != null ) { if ( issuer.getKeyUsage() != null && ! issuer.getKeyUsage()[6] ) { context.error = X509Utils.V_ERR_KEYUSAGE_NO_CRL_SIGN; ok = context.verifyCallback.call(context, ZERO); if ( ok == 0 ) return ok; } final PublicKey ikey = issuer.getPublicKey(); if ( ikey == null ) { context.error = X509Utils.V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY; ok = context.verifyCallback.call(context, ZERO); if ( ok == 0 ) return ok; } else { try { SecurityHelper.verify(crl, ikey); } catch (GeneralSecurityException ex) { context.error = X509Utils.V_ERR_CRL_SIGNATURE_FAILURE; ok = context.verifyCallback.call(context, ZERO); if ( ok == 0 ) return ok; } } } ok = context.checkCRLTime(crl, 1); if ( ok == 0 ) return ok; return 1; } }; /** * c: cert_crl */ final static Store.CertificateCRLFunction defaultCertificateCRL = new Store.CertificateCRLFunction() { public int call(final StoreContext context, final X509CRL crl, X509AuxCertificate x) throws Exception { int ok; if ( crl.getRevokedCertificate( x.getSerialNumber() ) != null ) { context.error = X509Utils.V_ERR_CERT_REVOKED; ok = context.verifyCallback.call(context, ZERO); if ( ok == 0 ) return 0; } if ( (context.verifyParameter.flags & X509Utils.V_FLAG_IGNORE_CRITICAL) != 0 ) { return 1; } if ( crl.getCriticalExtensionOIDs() != null && crl.getCriticalExtensionOIDs().size() > 0 ) { context.error = X509Utils.V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION; ok = context.verifyCallback.call(context, ZERO); if ( ok == 0 ) return 0; } return 1; } }; /** * c: check_policy */ final static CheckPolicyFunction defaultCheckPolicy = new CheckPolicyFunction() { public int call(StoreContext context) throws Exception { return 1; } }; }// X509_STORE_CTX