Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keystorefactory: Pass class instead of object #432

Merged
merged 4 commits into from
Apr 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/org/acra/ACRAConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,6 @@ private ACRAConstants(){}
INSTALLATION_ID, DEVICE_FEATURES, ENVIRONMENT, SHARED_PREFERENCES };

public static final String DATE_TIME_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";

public static final String DEFAULT_CERTIFICATE_TYPE = "X.509";
}
23 changes: 23 additions & 0 deletions src/main/java/org/acra/annotation/ReportsCrashes.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.preference.PreferenceManager;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.RawRes;
import android.support.annotation.StringRes;

import org.acra.ACRA;
Expand All @@ -30,6 +31,8 @@
import org.acra.builder.ReportPrimer;
import org.acra.dialog.BaseCrashReportDialog;
import org.acra.dialog.CrashReportDialog;
import org.acra.security.KeyStoreFactory;
import org.acra.security.NoKeyStoreFactory;
import org.acra.sender.DefaultReportSenderFactory;
import org.acra.sender.HttpSender.Method;
import org.acra.sender.HttpSender.Type;
Expand Down Expand Up @@ -561,4 +564,24 @@
@NonNull Method httpMethod() default Method.POST;

@NonNull Type reportType() default Type.FORM;

/**
* @return Class which creates a keystore that can contain trusted certificates
*/
@NonNull Class<? extends KeyStoreFactory> keyStoreFactoryClass() default NoKeyStoreFactory.class;

/**
* @return path to a custom trusted certificate. Must start with "asset://" if the file is in the assets folder
*/
@NonNull String certificatePath() default ACRAConstants.DEFAULT_STRING_VALUE;

/**
* @return resource id of a custom trusted certificate.
*/
@RawRes int resCertificate() default ACRAConstants.DEFAULT_RES_VALUE;

/**
* @return specify the type of the certificate set in either {@link #certificatePath()} or {@link #resCertificate()}
*/
@NonNull String certificateType() default ACRAConstants.DEFAULT_CERTIFICATE_TYPE;
}
31 changes: 26 additions & 5 deletions src/main/java/org/acra/config/ACRAConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RawRes;
import android.support.annotation.StringRes;

import org.acra.ACRA;
Expand Down Expand Up @@ -127,8 +128,12 @@ public final class ACRAConfiguration implements Serializable {
private Method httpMethod;
private Type reportType;
private final Map<String, String> httpHeaders = new HashMap<String, String>();
private KeyStoreFactory keyStoreFactory;
private Class<? extends KeyStoreFactory> keyStoreFactoryClass;
private Class<? extends ReportSenderFactory>[] reportSenderFactoryClasses;
@RawRes
private int resCertificate;
private String certificatePath;
private String certificateType;

/**
* @param builder ConfigurationBuilder with which to initialise this {@link ACRAConfiguration}.
Expand Down Expand Up @@ -185,7 +190,10 @@ public final class ACRAConfiguration implements Serializable {
httpHeaders.putAll(builder.httpHeaders());
reportType = builder.reportType();
reportSenderFactoryClasses = copyArray(builder.reportSenderFactoryClasses());
keyStoreFactory = builder.keyStoreFactory();
keyStoreFactoryClass = builder.keyStoreFactoryClass();
resCertificate = builder.resCertificate();
certificatePath = builder.certificatePath();
certificateType = builder.certificateType();
}


Expand Down Expand Up @@ -995,9 +1003,22 @@ public Class<? extends ReportSenderFactory>[] reportSenderFactoryClasses() {
return copyArray(reportSenderFactoryClasses);
}

@Nullable
public KeyStoreFactory keyStoreFactory() {
return keyStoreFactory;
@NonNull
public Class<? extends KeyStoreFactory> keyStoreFactoryClass() {
return keyStoreFactoryClass;
}

@RawRes
public int resCertificate() {
return resCertificate;
}

public String certificatePath() {
return certificatePath;
}

public String certificateType() {
return certificateType;
}

/**
Expand Down
86 changes: 77 additions & 9 deletions src/main/java/org/acra/config/ConfigurationBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RawRes;
import android.support.annotation.StringRes;

import org.acra.ACRA;
Expand All @@ -31,6 +32,7 @@
import org.acra.dialog.BaseCrashReportDialog;
import org.acra.dialog.CrashReportDialog;
import org.acra.security.KeyStoreFactory;
import org.acra.security.NoKeyStoreFactory;
import org.acra.sender.DefaultReportSenderFactory;
import org.acra.sender.HttpSender;
import org.acra.sender.HttpSender.Method;
Expand Down Expand Up @@ -91,8 +93,8 @@ public final class ConfigurationBuilder {
@DrawableRes private Integer resNotifIcon;
@StringRes private Integer resNotifText;
@StringRes private Integer resNotifTickerText;
@StringRes private Integer resNotifTitle;
@StringRes private Integer resToastText;
@StringRes private Integer resNotifTitle;
@StringRes private Integer resToastText;
private Integer sharedPreferencesMode;
private String sharedPreferencesName;
private Integer socketTimeout;
Expand All @@ -108,8 +110,12 @@ public final class ConfigurationBuilder {
private Method httpMethod;
private Type reportType;
private final Map<String, String> httpHeaders = new HashMap<String, String>();
private KeyStoreFactory keyStoreFactory;
private Class<? extends KeyStoreFactory> keyStoreFactoryClass;
private Class<? extends ReportSenderFactory>[] reportSenderFactoryClasses;
@RawRes private Integer resCertificate;
private String certificatePath;
private String certificateType;


/**
* Constructs a ConfigurationBuilder that is prepopulated with any
Expand Down Expand Up @@ -168,6 +174,10 @@ public ConfigurationBuilder(@NonNull Application app) {
httpMethod = annotationConfig.httpMethod();
reportType = annotationConfig.reportType();
reportSenderFactoryClasses = annotationConfig.reportSenderFactoryClasses();
keyStoreFactoryClass = annotationConfig.keyStoreFactoryClass();
resCertificate = annotationConfig.resCertificate();
certificatePath = annotationConfig.certificatePath();
certificateType = annotationConfig.certificateType();
} else {
annotationType = null;
}
Expand Down Expand Up @@ -687,11 +697,42 @@ public ConfigurationBuilder setReportType(@NonNull Type type) {
}

/**
* @param keyStoreFactory Set this to a factory which creates a the keystore that contains the trusted certificates
* @param keyStoreFactoryClass Set this to a factory class which creates a the keystore that contains the trusted certificates
* @return this instance
*/
@NonNull
public ConfigurationBuilder setKeyStoreFactoryClass(Class<?extends KeyStoreFactory> keyStoreFactoryClass) {
this.keyStoreFactoryClass = keyStoreFactoryClass;
return this;
}

/**
* @param resCertificate a raw resource of a custom certificate file
* @return this instance
*/
@NonNull
public ConfigurationBuilder setCertificate(@RawRes int resCertificate){
this.resCertificate = resCertificate;
return this;
}

/**
* @param certificatePath path to a custom trusted certificate. Must start with "asset://" if the file is in the assets folder
* @return this instance
*/
@NonNull
public ConfigurationBuilder setKeyStoreFactory(KeyStoreFactory keyStoreFactory) {
this.keyStoreFactory = keyStoreFactory;
public ConfigurationBuilder setCertificate(@NonNull String certificatePath) {
this.certificatePath = certificatePath;
return this;
}

/**
* @param type custom certificate type
* @return this instance
*/
@NonNull
public ConfigurationBuilder setCertificateType(@NonNull String type) {
this.certificateType = type;
return this;
}

Expand Down Expand Up @@ -1071,9 +1112,36 @@ Class<? extends ReportSenderFactory>[] reportSenderFactoryClasses() {
return new Class[]{DefaultReportSenderFactory.class};
}

@Nullable
KeyStoreFactory keyStoreFactory() {
return keyStoreFactory;
@NonNull
Class<? extends KeyStoreFactory> keyStoreFactoryClass() {
if(keyStoreFactoryClass != null) {
return keyStoreFactoryClass;
}
return NoKeyStoreFactory.class;
}

@RawRes
int resCertificate() {
if(resCertificate != null){
return resCertificate;
}
return DEFAULT_RES_VALUE;
}

@NonNull
String certificatePath() {
if(certificatePath != null){
return certificatePath;
}
return DEFAULT_STRING_VALUE;
}

@NonNull
String certificateType() {
if(certificateType != null){
return certificateType;
}
return DEFAULT_CERTIFICATE_TYPE;
}

@NonNull
Expand Down
14 changes: 2 additions & 12 deletions src/main/java/org/acra/security/AssetKeyStoreFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,10 @@
* @author F43nd1r
* @since 4.8.3
*/
@SuppressWarnings("unused")
public final class AssetKeyStoreFactory extends BaseKeyStoreFactory {
final class AssetKeyStoreFactory extends BaseKeyStoreFactory {

private final String assetName;

/**
* creates a new KeyStoreFactory for the specified asset
* @param assetName the asset
*/
public AssetKeyStoreFactory(String assetName) {
super();
this.assetName = assetName;
}

/**
* creates a new KeyStoreFactory for the specified asset with a custom certificate type
* @param certificateType the certificate type
Expand All @@ -60,7 +50,7 @@ public InputStream getInputStream(@NonNull Context context) {
try {
return context.getAssets().open(assetName);
} catch (IOException e) {
ACRA.log.e(LOG_TAG, "", e);
ACRA.log.e(LOG_TAG, "Could not open certificate in asset://"+assetName, e);
}
return null;
}
Expand Down
60 changes: 41 additions & 19 deletions src/main/java/org/acra/security/BaseKeyStoreFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import android.support.annotation.Nullable;

import org.acra.ACRA;
import org.acra.ACRAConstants;
import org.acra.util.IOUtils;

import java.io.BufferedInputStream;
import java.io.IOException;
Expand All @@ -39,27 +41,45 @@
* @author F43nd1r
* @since 4.8.3
*/
@SuppressWarnings("WeakerAccess")
@SuppressWarnings({"WeakerAccess", "unused"})
public abstract class BaseKeyStoreFactory implements KeyStoreFactory {

public enum Type {
CERTIFICATE,
KEYSTORE
}

private final String certificateType;

/**
* creates a new KeyStoreFactory for the default certificate type (X.509)
* creates a new KeyStoreFactory for the default certificate type {@link ACRAConstants#DEFAULT_CERTIFICATE_TYPE}
*/
public BaseKeyStoreFactory(){
this("X.509");
public BaseKeyStoreFactory() {
this(ACRAConstants.DEFAULT_CERTIFICATE_TYPE);
}

/**
* creates a new KeyStoreFactory with the specified certificate type
*
* @param certificateType the certificate type
*/
public BaseKeyStoreFactory(String certificateType) {
this.certificateType = certificateType;
}

abstract public InputStream getInputStream(@NonNull Context context);
abstract protected InputStream getInputStream(@NonNull Context context);

protected String getKeyStoreType() {
return KeyStore.getDefaultType();
}

protected Type getStreamType() {
return Type.CERTIFICATE;
}

protected char[] getPassword() {
return null;
}

@Override
@Nullable
Expand All @@ -68,26 +88,28 @@ public final KeyStore create(@NonNull Context context) {
if (inputStream != null) {
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance(certificateType);
Certificate certificate = certificateFactory.generateCertificate(bufferedInputStream);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", certificate);
KeyStore keyStore = KeyStore.getInstance(getKeyStoreType());
switch (getStreamType()) {
case CERTIFICATE:
CertificateFactory certificateFactory = CertificateFactory.getInstance(certificateType);
Certificate certificate = certificateFactory.generateCertificate(bufferedInputStream);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", certificate);
break;
case KEYSTORE:
keyStore.load(bufferedInputStream, getPassword());
}
return keyStore;
} catch (CertificateException e) {
ACRA.log.e(LOG_TAG, "", e);
ACRA.log.e(LOG_TAG, "Could not load certificate", e);
} catch (KeyStoreException e) {
ACRA.log.e(LOG_TAG, "", e);
ACRA.log.e(LOG_TAG, "Could not load keystore", e);
} catch (NoSuchAlgorithmException e) {
ACRA.log.e(LOG_TAG, "", e);
ACRA.log.e(LOG_TAG, "Could not load keystore", e);
} catch (IOException e) {
ACRA.log.e(LOG_TAG, "", e);
ACRA.log.e(LOG_TAG, "Could not load keystore", e);
} finally {
try {
bufferedInputStream.close();
} catch (IOException e) {
ACRA.log.e(LOG_TAG, "", e);
}
IOUtils.safeClose(bufferedInputStream);
}
}
return null;
Expand Down
Loading