mirror of
https://github.com/moparisthebest/k-9
synced 2024-08-13 17:03:48 -04:00
130 lines
4.7 KiB
Java
130 lines
4.7 KiB
Java
|
|
package com.fsck.k9.mail;
|
|
|
|
import java.security.cert.CertPathValidatorException;
|
|
import java.security.cert.CertificateException;
|
|
import java.security.cert.X509Certificate;
|
|
|
|
import javax.net.ssl.SSLHandshakeException;
|
|
|
|
import android.security.KeyChainException;
|
|
|
|
public class CertificateValidationException extends MessagingException {
|
|
public static final long serialVersionUID = -1;
|
|
private final Reason mReason;
|
|
private X509Certificate[] mCertChain;
|
|
private boolean mNeedsUserAttention = false;
|
|
private String mAlias;
|
|
|
|
public enum Reason {
|
|
Unknown, UseMessage, Expired, MissingCapability, RetrievalFailure
|
|
}
|
|
|
|
public CertificateValidationException(String message) {
|
|
this(message, Reason.UseMessage, null);
|
|
}
|
|
|
|
public CertificateValidationException(Reason reason) {
|
|
this(null, reason, null);
|
|
}
|
|
|
|
public CertificateValidationException(String message, Reason reason, String alias) {
|
|
super(message);
|
|
/*
|
|
* Instances created without a Throwable parameter as a cause are
|
|
* presumed to need user attention.
|
|
*/
|
|
mNeedsUserAttention = true;
|
|
mReason = reason;
|
|
mAlias = alias;
|
|
}
|
|
|
|
public CertificateValidationException(final String message, Throwable throwable) {
|
|
super(message, throwable);
|
|
mReason = Reason.Unknown;
|
|
scanForCause();
|
|
}
|
|
|
|
public String getAlias() {
|
|
return mAlias;
|
|
}
|
|
|
|
public Reason getReason() {
|
|
return mReason;
|
|
}
|
|
|
|
private void scanForCause() {
|
|
Throwable throwable = getCause();
|
|
|
|
/*
|
|
* User attention is required if the server certificate was deemed
|
|
* invalid or if there was a problem with a client certificate.
|
|
*
|
|
* A CertificateException is known to be thrown by the default
|
|
* X509TrustManager.checkServerTrusted() if the server certificate
|
|
* doesn't validate. The cause of the CertificateException will be a
|
|
* CertPathValidatorException. However, it's unlikely those exceptions
|
|
* will be encountered here, because they are caught in
|
|
* SecureX509TrustManager.checkServerTrusted(), which throws a
|
|
* CertificateChainException instead (an extension of
|
|
* CertificateException).
|
|
*
|
|
* A CertificateChainException will likely result in (or, be the cause
|
|
* of) an SSLHandshakeException (an extension of SSLException).
|
|
*
|
|
* The various mail protocol handlers (IMAP, POP3, ...) will catch an
|
|
* SSLException and throw a CertificateValidationException (this class)
|
|
* with the SSLException as the cause. (They may also throw a
|
|
* CertificateValidationException when STARTTLS is not available, just
|
|
* for the purpose of triggering a user notification.)
|
|
*
|
|
* SSLHandshakeException is also known to occur if the *client*
|
|
* certificate was not accepted by the server (unknown CA, certificate
|
|
* expired, etc.). In this case, the SSLHandshakeException will not have
|
|
* a CertificateChainException as a cause.
|
|
*
|
|
* KeyChainException is known to occur if the device has no client
|
|
* certificate that's associated with the alias stored in the server
|
|
* settings.
|
|
*/
|
|
while (throwable != null
|
|
&& !(throwable instanceof CertPathValidatorException)
|
|
&& !(throwable instanceof CertificateException)
|
|
&& !(throwable instanceof KeyChainException)
|
|
&& !(throwable instanceof SSLHandshakeException)) {
|
|
throwable = throwable.getCause();
|
|
}
|
|
|
|
if (throwable != null) {
|
|
mNeedsUserAttention = true;
|
|
|
|
// See if there is a server certificate chain attached to the SSLHandshakeException
|
|
if (throwable instanceof SSLHandshakeException) {
|
|
while (throwable != null && !(throwable instanceof CertificateChainException)) {
|
|
throwable = throwable.getCause();
|
|
}
|
|
}
|
|
|
|
if (throwable != null && throwable instanceof CertificateChainException) {
|
|
mCertChain = ((CertificateChainException) throwable).getCertChain();
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean needsUserAttention() {
|
|
return mNeedsUserAttention;
|
|
}
|
|
|
|
/**
|
|
* If the cause of this {@link CertificateValidationException} was a
|
|
* {@link CertificateChainException}, then the offending chain is available
|
|
* for return.
|
|
*
|
|
* @return An {@link X509Certificate X509Certificate[]} containing the Cert.
|
|
* chain, or else null.
|
|
*/
|
|
public X509Certificate[] getCertChain() {
|
|
return mCertChain;
|
|
}
|
|
}
|