diff --git a/u2f-gae-demo/src/soy/card.soy b/u2f-gae-demo/src/soy/card.soy
index 892d6f1..c85834f 100644
--- a/u2f-gae-demo/src/soy/card.soy
+++ b/u2f-gae-demo/src/soy/card.soy
@@ -26,13 +26,16 @@
Android Attestation
- - Keymaster Version:
- - Attestation Challenge:
+ - Cert Chain Verified: false
+ - Keymaster Version: none
+ - Attestation Challenge: none
- Software Enforced:
- - Purpose:
-
- Algorithm:
+
- Purpose: none
+
- Algorithm: none
+
- Key Size: none
+
- Block Mode: none
- TEE Enforced:
diff --git a/u2f-gae-demo/war/js/u2fdemo.js b/u2f-gae-demo/war/js/u2fdemo.js
index cb44bfd..e6cea16 100644
--- a/u2f-gae-demo/war/js/u2fdemo.js
+++ b/u2f-gae-demo/war/js/u2fdemo.js
@@ -33,12 +33,22 @@ function tokenToDom(token) {
card.querySelector('.challenge').textContent
= token.android_attestation.attestation_challenge;
- card.querySelector('.softwareEnforced .algorithm').textContent
- = token.android_attestation.software_encoded.algorithm;
+ if (token.android_attestation.software_encoded.algorithm) {
+ card.querySelector('.softwareEnforced .algorithm').textContent
+ = token.android_attestation.software_encoded.algorithm;
+ }
if (token.android_attestation.software_encoded.purpose) {
card.querySelector('.softwareEnforced .purpose').textContent
= token.android_attestation.software_encoded.purpose.join(', ');
}
+ if (token.android_attestation.software_encoded.keysize) {
+ card.querySelector('.softwareEnforced .keysize').textContent
+ = token.android_attestation.software_encoded.keysize;
+ }
+ if (token.android_attestation.software_encoded.blockmode) {
+ card.querySelector('.softwareEnforced .blockmode').textContent
+ = token.android_attestation.software_encoded.blockmode.join(', ');
+ }
card.querySelector('.teeEnforced').textContent
= JSON.stringify(token.android_attestation.tee_encoded, null, 2);
diff --git a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestation.java b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestation.java
index eadf7eb..d14d995 100644
--- a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestation.java
+++ b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestation.java
@@ -34,6 +34,8 @@ public class AndroidKeyStoreAttestation {
// Tags for Authorization List
private static final int AUTHZ_PURPOSE_TAG = 1;
private static final int AUTHZ_ALGORITHM_TAG = 2;
+ private static final int AUTHZ_KEY_SIZE_TAG = 3;
+ private static final int AUTHZ_BLOCK_MODE_TAG = 4;
private final int keymasterVersion;
private final byte[] attestationChallenge;
@@ -298,12 +300,48 @@ private static byte[] getAttestationChallenge(ASN1Sequence keyDescriptionSequenc
return X509ExtensionParsingUtil.getByteArray(asn1Encodable);
}
- private static List getPurpose(
+ private static List getPurpose(HashMap taggedObjects)
+ throws CertificateParsingException {
+ return getListFromTaggedObjectSet(taggedObjects, AUTHZ_PURPOSE_TAG, Purpose.class);
+ }
+
+ private static Algorithm getAlgorithm(HashMap taggedObjects)
+ throws CertificateParsingException {
+ ASN1Primitive asn1Primitive = taggedObjects.get(AUTHZ_ALGORITHM_TAG);
+ if (asn1Primitive == null) {
+ // No algorithm found
+ return null;
+ }
+ return Algorithm.fromValue(X509ExtensionParsingUtil.getInt(asn1Primitive));
+ }
+
+ private static Integer getKeySize(HashMap taggedObjects)
+ throws CertificateParsingException {
+ ASN1Primitive asn1Primitive = taggedObjects.get(AUTHZ_KEY_SIZE_TAG);
+ if (asn1Primitive == null) {
+ // No key size found
+ return null;
+ }
+ return X509ExtensionParsingUtil.getInt(asn1Primitive);
+ }
+
+ private static List getBlockMode(
HashMap softwareEnforcedTaggedObjects)
throws CertificateParsingException {
- ASN1Primitive asn1Primitive = softwareEnforcedTaggedObjects.get(AUTHZ_PURPOSE_TAG);
+ return getListFromTaggedObjectSet(
+ softwareEnforcedTaggedObjects, AUTHZ_BLOCK_MODE_TAG, BlockMode.class);
+ }
+
+ // TODO(aczeskis): There is a cleaner way of doing this in Java 8. In Java 8, we can make Purpose
+ // & BlockMode implement an interface (so the function could call .fromInt() on the
+ // parameterized type). Unfortunately, Java 7 does not allow interfaces to have static methods!
+ // This decision was fixed in Java 8.
+ private static List getListFromTaggedObjectSet(
+ HashMap taggedObjects, int tag, Class type)
+ throws CertificateParsingException {
+ ASN1Primitive asn1Primitive = taggedObjects.get(tag);
if (asn1Primitive == null) {
- // No purpose found
+ // No tagged object mode found
return null;
}
@@ -312,33 +350,36 @@ private static List getPurpose(
}
ASN1Set set = (ASN1Set) asn1Primitive;
- List purpose = new ArrayList();
+ List list = new ArrayList();
for (ASN1Encodable asn1Encodable : set.toArray()) {
- purpose.add(Purpose.fromValue(X509ExtensionParsingUtil.getInt(asn1Encodable)));
+ list.add(buildTypeFromInt(X509ExtensionParsingUtil.getInt(asn1Encodable), type));
}
- return purpose;
+ return list;
}
- private static Algorithm getAlgorithm(
- HashMap softwareEnforcedTaggedObjects)
+ @SuppressWarnings("unchecked")
+ private static T buildTypeFromInt(int value, Class type)
throws CertificateParsingException {
- ASN1Primitive asn1Primitive = softwareEnforcedTaggedObjects.get(AUTHZ_ALGORITHM_TAG);
- if (asn1Primitive == null) {
- // No algorithm found
- return null;
+ if (type == Purpose.class) {
+ return (T) Purpose.fromValue(value);
+ } else if (type == BlockMode.class) {
+ return (T) BlockMode.fromValue(value);
+ } else {
+ throw new CertificateParsingException("Cannot build type " + type.getSimpleName());
}
- return Algorithm.fromValue(X509ExtensionParsingUtil.getInt(asn1Primitive));
}
private static AuthorizationList extractAuthorizationList(ASN1Sequence authorizationSequence)
throws CertificateParsingException {
- HashMap softwareEnforcedTaggedObjects =
+ HashMap taggedObjects =
X509ExtensionParsingUtil.extractTaggedObjects(authorizationSequence);
return new AuthorizationList.Builder()
- .setPurpose(getPurpose(softwareEnforcedTaggedObjects))
- .setAlgorithm(getAlgorithm(softwareEnforcedTaggedObjects))
+ .setPurpose(getPurpose(taggedObjects))
+ .setAlgorithm(getAlgorithm(taggedObjects))
+ .setKeySize(getKeySize(taggedObjects))
+ .setBlockMode(getBlockMode(taggedObjects))
.build();
}
}
diff --git a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java
index 45c4fdb..33baa03 100644
--- a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java
+++ b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/AuthorizationList.java
@@ -5,6 +5,7 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -14,19 +15,27 @@
public class AuthorizationList {
private final List purposeList;
private final Algorithm algorithm;
+ private final Integer keySize;
+ private final List blockModeList;
@VisibleForTesting
public static final String JSON_ALGORITHM_KEY = "algorithm";
-
@VisibleForTesting
public static final String JSON_PURPOSE_KEY = "purpose";
+ @VisibleForTesting
+ public static final String JSON_KEY_SIZE_KEY = "keysize";
+ @VisibleForTesting
+ public static final String JSON_BLOCK_MODE_KEY = "blockmode";
- protected AuthorizationList(List purpose, Algorithm algorithm) {
- this.purposeList = purpose;
+ protected AuthorizationList(List purposeList, Algorithm algorithm, Integer keySize,
+ List blockModeList) {
+ this.purposeList = purposeList;
this.algorithm = algorithm;
+ this.keySize = keySize;
+ this.blockModeList = blockModeList;
}
- public List getPurpose() {
+ public List getPurposeList() {
return purposeList;
}
@@ -34,9 +43,17 @@ public Algorithm getAlgorithm() {
return algorithm;
}
+ public Integer getKeySize() {
+ return keySize;
+ }
+
+ public List getBlockModeList() {
+ return blockModeList;
+ }
+
@Override
public int hashCode() {
- return Objects.hash(purposeList, algorithm);
+ return Objects.hash(purposeList, algorithm, keySize, blockModeList);
}
@Override
@@ -50,7 +67,9 @@ public boolean equals(Object obj) {
AuthorizationList other = (AuthorizationList) obj;
return Objects.equals(algorithm, other.algorithm)
- && Objects.equals(purposeList, other.purposeList);
+ && areEqualIgnoringOrder(purposeList, other.purposeList)
+ && Objects.equals(keySize, other.keySize)
+ && areEqualIgnoringOrder(blockModeList, other.blockModeList);
}
@Override
@@ -59,7 +78,7 @@ public String toString() {
stringRepresentation.append("[");
if (purposeList != null) {
- stringRepresentation.append("\n purpose: ");
+ stringRepresentation.append("\n purpose list: ");
stringRepresentation.append(purposeList);
}
@@ -68,6 +87,16 @@ public String toString() {
stringRepresentation.append(algorithm);
}
+ if (keySize != null) {
+ stringRepresentation.append("\n key size: ");
+ stringRepresentation.append(keySize);
+ }
+
+ if (blockModeList != null) {
+ stringRepresentation.append("\n block mode list: ");
+ stringRepresentation.append(blockModeList);
+ }
+
stringRepresentation.append("\n]");
return stringRepresentation.toString();
@@ -77,28 +106,44 @@ public JsonObject toJson() {
JsonObject json = new JsonObject();
if (purposeList != null) {
JsonArray purposeJsonArray = new JsonArray();
- for (Purpose p : purposeList) {
- purposeJsonArray.add(new JsonPrimitive(p.toString()));
+ for (Purpose purpose : purposeList) {
+ purposeJsonArray.add(new JsonPrimitive(purpose.toString()));
}
json.add(JSON_PURPOSE_KEY, purposeJsonArray);
}
if (algorithm != null) {
json.addProperty(JSON_ALGORITHM_KEY, algorithm.toString());
}
+ if (keySize != null) {
+ json.addProperty(JSON_KEY_SIZE_KEY, keySize);
+ }
+ if (blockModeList != null) {
+ JsonArray blockModeJsonArray = new JsonArray();
+ for (BlockMode blockMode : blockModeList) {
+ blockModeJsonArray.add(new JsonPrimitive(blockMode.toString()));
+ }
+ json.add(JSON_BLOCK_MODE_KEY, blockModeJsonArray);
+ }
return json;
}
public static class Builder {
private List purpose;
private Algorithm algorithm;
+ private Integer keySize;
+ private List blockMode;
public Builder() {
this.purpose = null;
this.algorithm = null;
+ this.keySize = null;
+ this.blockMode = null;
}
public Builder setPurpose(List purpose) {
- this.purpose = purpose;
+ if (purpose != null) {
+ this.purpose = new ArrayList(purpose);
+ }
return this;
}
@@ -107,8 +152,28 @@ public Builder setAlgorithm(Algorithm algorithm) {
return this;
}
+ public Builder setKeySize(Integer keySize) {
+ this.keySize = keySize;
+ return this;
+ }
+
+ public Builder setBlockMode(List blockMode) {
+ if (blockMode != null) {
+ this.blockMode = new ArrayList(blockMode);
+ }
+ return this;
+ }
+
public AuthorizationList build() {
- return new AuthorizationList(this.purpose, this.algorithm);
+ return new AuthorizationList(this.purpose, this.algorithm, this.keySize, this.blockMode);
+ }
+ }
+
+ private boolean areEqualIgnoringOrder(List list1, List list2) {
+ if (list1 != null && list2 != null) {
+ return list1.containsAll(list2) && list2.containsAll(list1);
}
+
+ return list1 == null && list2 == null;
}
}
diff --git a/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/BlockMode.java b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/BlockMode.java
new file mode 100644
index 0000000..5535029
--- /dev/null
+++ b/u2f-ref-code/java/src/com/google/u2f/server/impl/attestation/android/BlockMode.java
@@ -0,0 +1,50 @@
+package com.google.u2f.server.impl.attestation.android;
+
+import java.security.cert.CertificateParsingException;
+
+/**
+ * Keymaster block mode values as taken from: keymaster_defs.h / KeymasterDefs.java
+ */
+public enum BlockMode {
+ KM_MODE_ECB(1, "ecb"),
+ KM_MODE_CBC(2, "cbc"),
+ KM_MODE_CTR(3, "ctr"),
+ KM_MODE_GCM(32, "gcm");
+
+ private final int value;
+ private final String description;
+
+ public static BlockMode fromValue(int value) throws CertificateParsingException {
+ for (BlockMode mode : BlockMode.values()) {
+ if (mode.getValue() == value) {
+ return mode;
+ }
+ }
+
+ throw new CertificateParsingException("Invalid block mode value: " + value);
+ }
+
+ public static BlockMode fromString(String string) throws CertificateParsingException {
+ for (BlockMode mode : BlockMode.values()) {
+ if (mode.toString().equals(string)) {
+ return mode;
+ }
+ }
+
+ throw new CertificateParsingException("Invalid block mode string: " + string);
+ }
+
+ private BlockMode(int value, String description) {
+ this.value = value;
+ this.description = description;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return description;
+ }
+}
diff --git a/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestationTest.java b/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestationTest.java
index 3894ce4..897d5a8 100644
--- a/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestationTest.java
+++ b/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AndroidKeyStoreAttestationTest.java
@@ -38,23 +38,31 @@ public void testValidCert() throws Exception {
// Check purpose
assertEquals("Incorrect software authorization list purpose list size", 2,
- softwareAuthorizationList.getPurpose().size());
+ softwareAuthorizationList.getPurposeList().size());
assertTrue(
"Software authorization list purpose list doesn't have SIGN",
- softwareAuthorizationList.getPurpose().contains(Purpose.KM_PURPOSE_SIGN));
+ softwareAuthorizationList.getPurposeList().contains(Purpose.KM_PURPOSE_SIGN));
assertTrue(
"Software authorization list purpose list doesn't have VERIFY",
- softwareAuthorizationList.getPurpose().contains(Purpose.KM_PURPOSE_VERIFY));
+ softwareAuthorizationList.getPurposeList().contains(Purpose.KM_PURPOSE_VERIFY));
// Check algorithm
assertEquals("Software authorization list incorrect algorithm", Algorithm.KM_ALGORITHM_EC,
softwareAuthorizationList.getAlgorithm());
+ // Check key size
+ assertEquals("Software authorization list incorrect keysize", 256,
+ softwareAuthorizationList.getKeySize().intValue());
+
+ // Block mode
+ assertEquals("Not expecting software authorization list block mode", null,
+ softwareAuthorizationList.getBlockModeList());
+
// Get the TEE authorization list
AuthorizationList teeAuthorizationList = attestation.getTeeAuthorizationList();
assertNotNull("Not expecting null TEE authorization list", teeAuthorizationList);
assertEquals(
- "Expecting null TEE authorization list purpose", null, teeAuthorizationList.getPurpose());
+ "Expecting null TEE authorization list purpose", null, teeAuthorizationList.getPurposeList());
assertEquals("Expecting null TEE authorization list algorithm", null,
teeAuthorizationList.getAlgorithm());
}
diff --git a/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AuthorizationListTest.java b/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AuthorizationListTest.java
index ffaf09d..a1b3e84 100644
--- a/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AuthorizationListTest.java
+++ b/u2f-ref-code/java/tests/com/google/u2f/server/impl/attestation/android/AuthorizationListTest.java
@@ -12,6 +12,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.security.cert.CertificateParsingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -26,26 +27,28 @@ public class AuthorizationListTest {
private static final List ONE_PURPOSE = Arrays.asList(Purpose.KM_PURPOSE_SIGN);
private static final List TWO_PURPOSES =
Arrays.asList(Purpose.KM_PURPOSE_SIGN, Purpose.KM_PURPOSE_VERIFY);
+ private static final Integer KEY_SIZE = 256;
+ private static final List EMPTY_BLOCKMODE = new ArrayList();
+ private static final List ONE_BLOCKMODE = Arrays.asList(BlockMode.KM_MODE_CBC);
+ private static final List TWO_BLOCKMODE =
+ Arrays.asList(BlockMode.KM_MODE_CBC, BlockMode.KM_MODE_CTR);
@Test
public void toJson_nullValues() throws Exception {
- JsonObject json = new AuthorizationList(null, null).toJson();
+ JsonObject json = new AuthorizationList(null, null, null, null).toJson();
assertFalse(json.has(AuthorizationList.JSON_ALGORITHM_KEY));
assertFalse(json.has(AuthorizationList.JSON_PURPOSE_KEY));
+ assertFalse(json.has(AuthorizationList.JSON_KEY_SIZE_KEY));
+ assertFalse(json.has(AuthorizationList.JSON_BLOCK_MODE_KEY));
}
@Test
public void toJson_emptyPurpose() throws Exception {
- AuthorizationList authorizationList =
- new AuthorizationList(EMPTY_PURPOSE, Algorithm.KM_ALGORITHM_EC);
- JsonObject json = authorizationList.toJson();
+ JsonObject json = new AuthorizationList(EMPTY_PURPOSE, null, null, null).toJson();
- assertEquals(
- Algorithm.KM_ALGORITHM_EC.toString(),
- json.get(AuthorizationList.JSON_ALGORITHM_KEY).getAsString());
- List extractedPurpose = extractPurposeListFromJsonArray(
- json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray());
+ List extractedPurpose = extractListFromJsonArray(
+ json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray(), Purpose.class);
assertTrue(EMPTY_PURPOSE.containsAll(extractedPurpose));
assertTrue(extractedPurpose.containsAll(EMPTY_PURPOSE));
@@ -53,15 +56,11 @@ public void toJson_emptyPurpose() throws Exception {
@Test
public void toJson_onePurpose() throws Exception {
- AuthorizationList authorizationList =
- new AuthorizationList(ONE_PURPOSE, Algorithm.KM_ALGORITHM_HMAC);
+ AuthorizationList authorizationList = new AuthorizationList(ONE_PURPOSE, null, null, null);
JsonObject json = authorizationList.toJson();
- assertEquals(
- Algorithm.KM_ALGORITHM_HMAC.toString(),
- json.get(AuthorizationList.JSON_ALGORITHM_KEY).getAsString());
- List extractedPurpose = extractPurposeListFromJsonArray(
- json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray());
+ List extractedPurpose = extractListFromJsonArray(
+ json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray(), Purpose.class);
assertTrue(ONE_PURPOSE.containsAll(extractedPurpose));
assertTrue(extractedPurpose.containsAll(ONE_PURPOSE));
@@ -69,24 +68,87 @@ public void toJson_onePurpose() throws Exception {
@Test
public void toJson_twoPurposes() throws Exception {
- JsonObject json = new AuthorizationList(TWO_PURPOSES, Algorithm.KM_ALGORITHM_RSA).toJson();
+ JsonObject json = new AuthorizationList(TWO_PURPOSES, null, null, null).toJson();
- assertEquals(
- Algorithm.KM_ALGORITHM_RSA.toString(),
- json.get(AuthorizationList.JSON_ALGORITHM_KEY).getAsString());
- List extractedPurpose = extractPurposeListFromJsonArray(
- json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray());
+ List extractedPurpose = extractListFromJsonArray(
+ json.get(AuthorizationList.JSON_PURPOSE_KEY).getAsJsonArray(), Purpose.class);
assertTrue(TWO_PURPOSES.containsAll(extractedPurpose));
assertTrue(extractedPurpose.containsAll(TWO_PURPOSES));
}
- private List extractPurposeListFromJsonArray(JsonArray array) throws Exception {
+ @Test
+ public void toJson_algorithm() throws Exception {
+ JsonObject json = new AuthorizationList(null, Algorithm.KM_ALGORITHM_EC, null, null).toJson();
+
+ assertEquals(
+ Algorithm.KM_ALGORITHM_EC.toString(),
+ json.get(AuthorizationList.JSON_ALGORITHM_KEY).getAsString());
+ }
+
+ @Test
+ public void toJson_keysize() throws Exception {
+ JsonObject json = new AuthorizationList(null, null, KEY_SIZE, null).toJson();
+
+ assertEquals(KEY_SIZE.intValue(), json.get(AuthorizationList.JSON_KEY_SIZE_KEY).getAsInt());
+ }
+
+ @Test
+ public void toJson_emptyBlockMode() throws Exception {
+ JsonObject json = new AuthorizationList(null, null, null, EMPTY_BLOCKMODE).toJson();
+
+ List extractedBlockMode = extractListFromJsonArray(
+ json.get(AuthorizationList.JSON_BLOCK_MODE_KEY).getAsJsonArray(), BlockMode.class);
+
+ assertTrue(EMPTY_BLOCKMODE.containsAll(extractedBlockMode));
+ assertTrue(extractedBlockMode.containsAll(EMPTY_BLOCKMODE));
+ }
+
+ @Test
+ public void toJson_oneBlockMode() throws Exception {
+ AuthorizationList authorizationList = new AuthorizationList(null, null, null, ONE_BLOCKMODE);
+ JsonObject json = authorizationList.toJson();
+
+ List extractedBlockMode = extractListFromJsonArray(
+ json.get(AuthorizationList.JSON_BLOCK_MODE_KEY).getAsJsonArray(), BlockMode.class);
+
+ assertTrue(ONE_BLOCKMODE.containsAll(extractedBlockMode));
+ assertTrue(extractedBlockMode.containsAll(ONE_BLOCKMODE));
+ }
+
+ @Test
+ public void toJson_twoBlockMode() throws Exception {
+ JsonObject json = new AuthorizationList(null, null, null, TWO_BLOCKMODE).toJson();
+
+ List extractedBlockMode = extractListFromJsonArray(
+ json.get(AuthorizationList.JSON_BLOCK_MODE_KEY).getAsJsonArray(), BlockMode.class);
+
+ assertTrue(TWO_BLOCKMODE.containsAll(extractedBlockMode));
+ assertTrue(extractedBlockMode.containsAll(TWO_BLOCKMODE));
+ }
+
+ // TODO(aczeskis): There is a cleaner way of doing this in Java 8. In Java 8, we can make Purpose
+ // & BlockMode implement an interface (so the function could call .fromString() on the
+ // parameterized type). Unfortunately, Java 7 does not allow interfaces to have static methods!
+ // This decision was fixed in Java 8.
+ private List extractListFromJsonArray(JsonArray array, Class type) throws Exception {
Iterator iterator = array.iterator();
- List result = new ArrayList();
+ List result = new ArrayList();
while (iterator.hasNext()) {
- result.add(Purpose.fromString(iterator.next().getAsString()));
+ result.add(buildTypeFromString(iterator.next().getAsString(), type));
}
return result;
}
+
+ @SuppressWarnings("unchecked")
+ private static T buildTypeFromString(String string, Class type)
+ throws CertificateParsingException {
+ if (type == Purpose.class) {
+ return (T) Purpose.fromString(string);
+ } else if (type == BlockMode.class) {
+ return (T) BlockMode.fromString(string);
+ } else {
+ throw new CertificateParsingException("Cannot build type " + type.getSimpleName());
+ }
+ }
}