Skip to content

Commit

Permalink
Merge pull request #432 from F43nd1r/keystorefactory
Browse files Browse the repository at this point in the history
Keystorefactory: Pass class instead of object
  • Loading branch information
william-ferguson-au committed Apr 20, 2016
2 parents c5940ff + 5628b82 commit a78a101
Show file tree
Hide file tree
Showing 12 changed files with 290 additions and 74 deletions.
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 android.support.annotation.StyleRes;

Expand All @@ -31,6 +32,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 @@ -568,4 +571,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 android.support.annotation.StyleRes;

Expand Down Expand Up @@ -130,8 +131,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 @@ -189,7 +194,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 @@ -1004,9 +1012,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 android.support.annotation.StyleRes;

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 @@ -92,8 +94,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 @@ -109,8 +111,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 @@ -170,6 +176,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 @@ -697,11 +707,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 @@ -1090,9 +1131,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

0 comments on commit a78a101

Please sign in to comment.