/* * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved. * See LICENCE file for licensing information. */ package eu.emi.security.authn.x509; import java.security.cert.X509Certificate; import java.text.MessageFormat; import java.util.MissingResourceException; import java.util.ResourceBundle; import eu.emi.security.authn.x509.impl.X500NameUtils; /** * Holds information about a single validation problem with a reference to * the certificate chain. * Each error may refer to particular certificate in the chain, contains an unique * code and a coarse grained category. * * @author K. Benedyczak * @see ValidationResult * @see ValidationErrorListener * @see ValidationErrorCategory */ public class ValidationError { private static final String BUNDLE_NAME = ValidationError.class.getPackage().getName() + "." + "valiadationErrors"; private int position; private ValidationErrorCode errorCode; private ValidationErrorCategory errorCategory; private String message; private Object[] parameters; private X509Certificate[] chain; public ValidationError(X509Certificate[] chain, int position, ValidationErrorCode errorCode, Object... params) { this.position = position; this.chain = chain; if (errorCode == null) throw new IllegalArgumentException("errorCode can not be null"); this.errorCode = errorCode; this.errorCategory = ValidationErrorCategory.getErrorCategory(errorCode); this.parameters = params; ResourceBundle bundle = ResourceBundle.getBundle(BUNDLE_NAME); String pattern; try { pattern = bundle.getString(errorCode.name()); } catch (MissingResourceException e) { pattern = "Other validation error"; } if (parameters.length > 0 && parameters[0] instanceof Throwable && !pattern.matches(".*\\{[0-9]\\}.*")) { message = pattern + makeReasonFromStack((Throwable) parameters[0]); } else message = MessageFormat.format(pattern, params); } public static String makeReasonFromStack(Throwable t) { StringBuilder sb = new StringBuilder(); do { sb.append(" Cause: ").append(makeReason(t)); t = t.getCause(); } while (t != null); return sb.toString(); } public static String makeReason(Throwable t) { return (t.getMessage() != null) ? t.getMessage() : t.getClass().getSimpleName(); } /** * Returns position in chain of the certificate causing the error. * If the error is related to chain inconsistency (so more then one certificate is * involved) then the lowest number of the certificate * involved must be returned. * @return position of the erroneous certificate in chain or -1 if not defied. */ public int getPosition() { return position; } /** * Returns human readable message describing this error. The message is * formatted in accordance to the current locale settings. * @return the error message */ public String getMessage() { return message; } /** * Gets the unique error code. Error codes are defined in bundle with messages * (in a properties file). * * @return the error code */ public ValidationErrorCode getErrorCode() { return errorCode; } /** * Gets the error parameters. * * @return the error parameters */ public Object[] getParameters() { return parameters; } /** * Returns a coarse grained error category. * @return error category */ public ValidationErrorCategory getErrorCategory() { return errorCategory; } /** * * @return the certificate chain which caused the validation error */ public X509Certificate[] getChain() { return chain; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("error"); if (position != -1) { sb.append(" at position ").append(getPosition()).append(" in chain"); sb.append(", problematic certificate subject: ").append( X500NameUtils.getReadableForm(chain[position].getSubjectX500Principal())); } else sb.append(" affecting the whole chain"); sb.append(" (category: ").append(errorCategory).append(")"); sb.append(": ").append(getMessage()); return sb.toString(); } }