From 3d98bcdfe83f18dc52ee98dc12ae1e6181cf6f94 Mon Sep 17 00:00:00 2001 From: Michael Buck Date: Thu, 19 Apr 2018 12:56:42 +0100 Subject: [PATCH] API-476: Final tidy up --- README.md | 4 +- .../client/spi/remote/AttributeConverter.java | 64 +++++++------------ .../client/spi/remote/SecureYotiClient.java | 46 +++++++------ .../remote/util/AnchorCertificateParser.java | 31 +++++---- .../spi/remote/SimpleActivityDetailsTest.java | 16 +++-- yoti-sdk-spring-boot-auto-config/README.md | 2 +- yoti-sdk-spring-boot-auto-config/pom.xml | 2 +- yoti-sdk-spring-boot-example/README.md | 2 +- yoti-sdk-spring-security/README.md | 4 +- yoti-sdk-spring-security/pom.xml | 31 +++++++++ 10 files changed, 108 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 27840324f..a60a60a34 100644 --- a/README.md +++ b/README.md @@ -99,13 +99,13 @@ If you are using Maven, you need to add the following dependency: com.yoti yoti-sdk-impl - 1.4.2 + 1.5.0 ``` If you are using Gradle, here is the dependency to add: -`compile group: 'com.yoti', name: 'yoti-sdk-impl', version: '1.4.2'` +`compile group: 'com.yoti', name: 'yoti-sdk-impl', version: '1.5.0'` You will find all classes packaged under `com.yoti.api` diff --git a/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/AttributeConverter.java b/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/AttributeConverter.java index 37f3a42a4..d157b925e 100644 --- a/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/AttributeConverter.java +++ b/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/AttributeConverter.java @@ -8,69 +8,53 @@ import java.util.Map; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.databind.ObjectMapper; import com.yoti.api.client.Attribute; import com.yoti.api.client.spi.remote.proto.AttrProto; import com.yoti.api.client.spi.remote.proto.AttrProto.Anchor; -import com.yoti.api.client.spi.remote.proto.ContentTypeProto.ContentType; import com.yoti.api.client.spi.remote.util.AnchorCertificateParser; -import com.yoti.api.client.spi.remote.util.AnchorType; import com.yoti.api.client.spi.remote.util.AnchorCertificateParser.AnchorVerifierSourceData; +import com.yoti.api.client.spi.remote.util.AnchorType; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AttributeConverter { private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); private static final Logger LOG = LoggerFactory.getLogger(AttributeConverter.class); - - public static Attribute convertAttribute(AttrProto.Attribute attribute) throws ParseException, IOException{ - if (ContentType.STRING.equals(attribute.getContentType())) { - return new com.yoti.api.client.Attribute(attribute.getName(), - attribute.getValue().toString(DEFAULT_CHARSET), - extractMetadata(attribute, AnchorType.SOURCE), - extractMetadata(attribute, AnchorType.VERIFIER)); - } else if (ContentType.DATE.equals(attribute.getContentType())) { - return new com.yoti.api.client.Attribute(attribute.getName(), - DateAttributeValue.parseFrom(attribute.getValue().toByteArray()), - extractMetadata(attribute, AnchorType.SOURCE), - extractMetadata(attribute, AnchorType.VERIFIER)); - } else if (ContentType.JPEG.equals(attribute.getContentType())) { - return new com.yoti.api.client.Attribute(attribute.getName(), - new JpegAttributeValue(attribute.getValue().toByteArray()), - extractMetadata(attribute, AnchorType.SOURCE), - extractMetadata(attribute, AnchorType.VERIFIER)); - } else if (ContentType.PNG.equals(attribute.getContentType())) { - return new com.yoti.api.client.Attribute(attribute.getName(), - new PngAttributeValue(attribute.getValue().toByteArray()), - extractMetadata(attribute, AnchorType.SOURCE), - extractMetadata(attribute, AnchorType.VERIFIER)); - } else if (ContentType.JSON.equals(attribute.getContentType())) { - return new com.yoti.api.client.Attribute(attribute.getName(), - JSON_MAPPER.readValue(attribute.getValue().toString(DEFAULT_CHARSET), Map.class), - extractMetadata(attribute, AnchorType.SOURCE), - extractMetadata(attribute, AnchorType.VERIFIER)); + public static Attribute convertAttribute(com.yoti.api.client.spi.remote.proto.AttrProto.Attribute attribute) throws ParseException, IOException { + switch (attribute.getContentType()) { + case STRING: + return attributeWithMetadata(attribute, attribute.getValue().toString(DEFAULT_CHARSET)); + case DATE: + return attributeWithMetadata(attribute, DateAttributeValue.parseFrom(attribute.getValue().toByteArray())); + case JPEG: + return attributeWithMetadata(attribute, new JpegAttributeValue(attribute.getValue().toByteArray())); + case PNG: + return attributeWithMetadata(attribute, new PngAttributeValue(attribute.getValue().toByteArray())); + case JSON: + return attributeWithMetadata(attribute, JSON_MAPPER.readValue(attribute.getValue().toString(DEFAULT_CHARSET), Map.class)); + default: + LOG.error("Unknown type {} for attribute {}", attribute.getContentType(), attribute.getName()); + return attributeWithMetadata(attribute, attribute.getValue().toString(DEFAULT_CHARSET)); } + } - LOG.error("Unknown type {} for attribute {}", attribute.getContentType(), attribute.getName()); - return new com.yoti.api.client.Attribute(attribute.getName(), - attribute.getValue().toString(DEFAULT_CHARSET), - extractMetadata(attribute, AnchorType.SOURCE), - extractMetadata(attribute, AnchorType.VERIFIER)); + private static Attribute attributeWithMetadata(AttrProto.Attribute attribute, Object value) { + return new Attribute(attribute.getName(), value, extractMetadata(attribute, AnchorType.SOURCE), extractMetadata(attribute, AnchorType.VERIFIER)); } private static Set extractMetadata(AttrProto.Attribute attribute, AnchorType anchorType) { Set entries = new HashSet(); for (Anchor anchor : attribute.getAnchorsList()) { AnchorVerifierSourceData anchorData = AnchorCertificateParser.getTypesFromAnchor(anchor); - if(anchorData.getType().equals(anchorType)) { + if (anchorData.getType().equals(anchorType)) { entries.addAll(anchorData.getEntries()); } } return entries; } - } diff --git a/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/SecureYotiClient.java b/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/SecureYotiClient.java index 69e8de44e..db6cf1c94 100644 --- a/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/SecureYotiClient.java +++ b/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/SecureYotiClient.java @@ -1,11 +1,12 @@ package com.yoti.api.client.spi.remote; +import static javax.crypto.Cipher.DECRYPT_MODE; + import static com.yoti.api.client.spi.remote.call.YotiConstants.ASYMMETRIC_CIPHER; import static com.yoti.api.client.spi.remote.call.YotiConstants.BOUNCY_CASTLE_PROVIDER; import static com.yoti.api.client.spi.remote.call.YotiConstants.DEFAULT_CHARSET; import static com.yoti.api.client.spi.remote.call.YotiConstants.SYMMETRIC_CIPHER; import static com.yoti.api.client.spi.remote.util.Validation.notNull; -import static javax.crypto.Cipher.DECRYPT_MODE; import java.io.BufferedReader; import java.io.IOException; @@ -27,18 +28,10 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import org.bouncycastle.openssl.PEMException; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.protobuf.ByteString; -import com.google.protobuf.InvalidProtocolBufferException; import com.yoti.api.client.ActivityDetails; import com.yoti.api.client.ActivityFailureException; import com.yoti.api.client.AmlException; +import com.yoti.api.client.Attribute; import com.yoti.api.client.InitialisationException; import com.yoti.api.client.KeyPairSource; import com.yoti.api.client.KeyPairSource.StreamVisitor; @@ -50,10 +43,19 @@ import com.yoti.api.client.spi.remote.call.ProfileService; import com.yoti.api.client.spi.remote.call.Receipt; import com.yoti.api.client.spi.remote.call.aml.RemoteAmlService; -import com.yoti.api.client.spi.remote.proto.AttrProto.Attribute; +import com.yoti.api.client.spi.remote.proto.AttrProto; import com.yoti.api.client.spi.remote.proto.AttributeListProto.AttributeList; import com.yoti.api.client.spi.remote.proto.EncryptedDataProto.EncryptedData; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import org.bouncycastle.openssl.PEMException; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * YotiClient talking to the Yoti Connect API remotely. */ @@ -140,7 +142,7 @@ private void validateReceipt(Receipt receipt) throws ActivityFailureException { } private Profile createProfile(byte[] profileBytes, Key secretKey) throws ProfileException { - List attributeList = new ArrayList(); + List attributeList = new ArrayList(); if (profileBytes != null && profileBytes.length > 0) { EncryptedData encryptedData = parseProfileContent(profileBytes); byte[] profileData = decrypt(encryptedData.getCipherText(), secretKey, encryptedData.getIv()); @@ -161,26 +163,22 @@ private Key parseKey(byte[] keyVal) { return new SecretKeySpec(keyVal, SYMMETRIC_CIPHER); } - private List parseProfile(byte[] profileData) throws ProfileException { - List attributeList = null; + private List parseProfile(byte[] profileData) throws ProfileException { try { AttributeList message = AttributeList.parseFrom(profileData); - attributeList = parseAttributes(message); + List attributeList = parseAttributes(message); LOG.debug("{} attribute(s) parsed", attributeList.size()); + return attributeList; } catch (InvalidProtocolBufferException e) { throw new ProfileException("Cannot parse profile data", e); } - return attributeList; } - private List parseAttributes(AttributeList message) { - List parsedAttributes = new ArrayList(); - for (Attribute attribute : message.getAttributesList()) { + private List parseAttributes(AttributeList message) { + List parsedAttributes = new ArrayList(); + for (AttrProto.Attribute attribute : message.getAttributesList()) { try { - com.yoti.api.client.Attribute parsedAttribute = AttributeConverter.convertAttribute(attribute); - if (parsedAttribute != null) { - parsedAttributes.add(parsedAttribute); - } + parsedAttributes.add(AttributeConverter.convertAttribute(attribute)); } catch (IOException e) { LOG.info("Cannot decode value for attribute {}", attribute.getName()); } catch (ParseException e) { @@ -191,7 +189,7 @@ private List parseAttributes(AttributeList messag } - private Profile createProfile(List attributeList) { + private Profile createProfile(List attributeList) { return new SimpleProfile(attributeList); } diff --git a/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/util/AnchorCertificateParser.java b/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/util/AnchorCertificateParser.java index 832c50cb3..64c8d43d0 100644 --- a/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/util/AnchorCertificateParser.java +++ b/yoti-sdk-impl/src/main/java/com/yoti/api/client/spi/remote/util/AnchorCertificateParser.java @@ -1,7 +1,10 @@ package com.yoti.api.client.spi.remote.util; +import static com.yoti.api.client.spi.remote.call.YotiConstants.DEFAULT_CHARSET; + import java.io.ByteArrayInputStream; import java.io.IOException; +import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -10,7 +13,9 @@ import java.util.List; import java.util.Set; -import org.bouncycastle.asn1.ASN1InputStream; +import com.yoti.api.client.spi.remote.proto.AttrProto.Anchor; + +import com.google.protobuf.ByteString; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1TaggedObject; @@ -19,13 +24,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.protobuf.ByteString; -import com.yoti.api.client.spi.remote.proto.AttrProto.Anchor; - public class AnchorCertificateParser { + private static final Logger LOG = LoggerFactory.getLogger(AnchorCertificateParser.class); - public static AnchorVerifierSourceData getTypesFromAnchor(Anchor anchor) { Set types = new HashSet(anchor.getOriginServerCertsCount()); AnchorType anchorType = AnchorType.UNKNOWN; @@ -46,29 +48,29 @@ public static AnchorVerifierSourceData getTypesFromAnchor(Anchor anchor) { } types.addAll(extensions); } - } catch (Exception e) { + } catch (IOException e) { + LOG.warn("Could not extract anchor type from certificate.", e); + } catch (CertificateException e) { LOG.warn("Could not extract anchor type from certificate.", e); } return new AnchorVerifierSourceData(types, anchorType); } - private static List getListOfStringFromExtension(X509Certificate certificate, String extensionValue) throws IOException { List extensionsStrings = new ArrayList(); byte[] extension = certificate.getExtensionValue(extensionValue); if (extension != null) { // Read the First object - ASN1InputStream asn1InputStream = new ASN1InputStream(extension); - ASN1Primitive derObject = asn1InputStream.readObject(); + ASN1Primitive derObject = ASN1Primitive.fromByteArray(extension); if (derObject != null && derObject instanceof DEROctetString) { DEROctetString derOctetString = (DEROctetString) derObject; // Read the sub object which is expected to be a sequence - ASN1InputStream derAsn1stream = new ASN1InputStream(derOctetString.getOctets()); - DLSequence dlSequence = (DLSequence) derAsn1stream.readObject(); + ASN1Primitive asn1Primitive1 = ASN1Primitive.fromByteArray(derOctetString.getOctets()); + DLSequence dlSequence = (DLSequence) asn1Primitive1; // Enumerate all the objects in the sequence, we expect only one ! Enumeration seqEnum = dlSequence.getObjects(); @@ -79,15 +81,12 @@ private static List getListOfStringFromExtension(X509Certificate certifi ASN1OctetString string = DEROctetString.getInstance(seqObj, false); // Convert to a java String - extensionsStrings.add(new String(string.getOctets())); + extensionsStrings.add(new String(string.getOctets(), DEFAULT_CHARSET)); } - derAsn1stream.close(); + LOG.debug("Anchor certificate types : '{}' for extension: {}", extensionsStrings.toString(), extensionValue); } - - asn1InputStream.close(); } - return extensionsStrings; } diff --git a/yoti-sdk-impl/src/test/java/com/yoti/api/client/spi/remote/SimpleActivityDetailsTest.java b/yoti-sdk-impl/src/test/java/com/yoti/api/client/spi/remote/SimpleActivityDetailsTest.java index 1d2fec680..2ba23973f 100644 --- a/yoti-sdk-impl/src/test/java/com/yoti/api/client/spi/remote/SimpleActivityDetailsTest.java +++ b/yoti-sdk-impl/src/test/java/com/yoti/api/client/spi/remote/SimpleActivityDetailsTest.java @@ -13,6 +13,7 @@ import com.yoti.api.client.Profile; public class SimpleActivityDetailsTest { + private static final String USER_ID = "YmFkYWRhZGEtZGFkYWJhZGEK"; private static final Profile USER_PROFILE = Mockito.mock(Profile.class); private static final Profile APP_PROFILE = Mockito.mock(Profile.class); @@ -21,6 +22,7 @@ public class SimpleActivityDetailsTest { private static final byte[] RECEIPT_ID = { 1, 2, 3, 4, 5, 6, 7, 8 }; private static final String RECEIPT_ID_STRING = Base64.toBase64String(RECEIPT_ID); private static final Date TIMESTAMP = new Date(); + private static final byte[] SOME_SELFIE_BYTES = "selfieTestVal".getBytes(); @Test(expected = IllegalArgumentException.class) public void shouldFailConstructionForNullRememberMeId() { @@ -78,13 +80,13 @@ public void shouldReturnReceiptId() { @Test public void shouldReturnBase64SelfieIfSelfieSet() { - Attribute selfie = new Attribute("selfie", new JpegAttributeValue("selfieTestVal".getBytes()), null); + Attribute selfie = new Attribute("selfie", new JpegAttributeValue(SOME_SELFIE_BYTES), null); SimpleProfile profile = new SimpleProfile(singletonList(selfie)); - SimpleActivityDetails s = new SimpleActivityDetails(USER_ID, profile, APP_PROFILE, TIMESTAMP, RECEIPT_ID); - String expected = "data:image/jpeg;base64," + Base64.toBase64String(s.getUserProfile().getSelfie().getContent()); - - assertEquals(expected, s.getBase64Selfie()); + SimpleActivityDetails result = new SimpleActivityDetails(USER_ID, profile, APP_PROFILE, TIMESTAMP, RECEIPT_ID); + + String expected = "data:image/jpeg;base64," + Base64.toBase64String(SOME_SELFIE_BYTES); + assertEquals(expected, result.getBase64Selfie()); } @Test @@ -92,8 +94,8 @@ public void shouldReturnBlankBase64SelfieIfSelfieNotSet() { Attribute familyName = new Attribute("family_name", "Smith", null); SimpleProfile profile = new SimpleProfile(singletonList(familyName)); - SimpleActivityDetails s = new SimpleActivityDetails(USER_ID, profile, APP_PROFILE, TIMESTAMP, RECEIPT_ID); + SimpleActivityDetails result = new SimpleActivityDetails(USER_ID, profile, APP_PROFILE, TIMESTAMP, RECEIPT_ID); - assertEquals("", s.getBase64Selfie()); + assertEquals("", result.getBase64Selfie()); } } diff --git a/yoti-sdk-spring-boot-auto-config/README.md b/yoti-sdk-spring-boot-auto-config/README.md index ea3e89815..d6efe0fb4 100644 --- a/yoti-sdk-spring-boot-auto-config/README.md +++ b/yoti-sdk-spring-boot-auto-config/README.md @@ -26,7 +26,7 @@ If you are using Maven, you need to add the following dependencies: If you are using Gradle, here is the dependency to add: ``` -compile group: 'com.yoti', name: 'yoti-sdk-spring-boot-auto-config', version: '1.4.1' +compile group: 'com.yoti', name: 'yoti-sdk-spring-boot-auto-config', version: '1.5.0' ``` diff --git a/yoti-sdk-spring-boot-auto-config/pom.xml b/yoti-sdk-spring-boot-auto-config/pom.xml index ba20322e2..4b033be6d 100644 --- a/yoti-sdk-spring-boot-auto-config/pom.xml +++ b/yoti-sdk-spring-boot-auto-config/pom.xml @@ -53,7 +53,7 @@ org.springframework.boot spring-boot-autoconfigure - 1.5.6.RELEASE + 1.5.10.RELEASE provided diff --git a/yoti-sdk-spring-boot-example/README.md b/yoti-sdk-spring-boot-example/README.md index ced7daca3..f0cdbbac6 100644 --- a/yoti-sdk-spring-boot-example/README.md +++ b/yoti-sdk-spring-boot-example/README.md @@ -16,7 +16,7 @@ Before you start, you'll need to create an Application in [Dashboard](https://ww com.yoti yoti-sdk-impl - 1.4.2 + 1.5.0 ``` diff --git a/yoti-sdk-spring-security/README.md b/yoti-sdk-spring-security/README.md index 4f4ef5c5d..c46fe0131 100644 --- a/yoti-sdk-spring-security/README.md +++ b/yoti-sdk-spring-security/README.md @@ -25,14 +25,14 @@ If you are using Maven, you need to add the following dependencies: com.yoti yoti-sdk-spring-security - 1.4.2 + 1.5.0 ``` If you are using Gradle, here is the dependency to add: ``` -compile group: 'com.yoti', name: 'yoti-sdk-spring-security', version: '1.4.2' +compile group: 'com.yoti', name: 'yoti-sdk-spring-security', version: '1.5.0' ``` ### Provide a `YotiClient` instance diff --git a/yoti-sdk-spring-security/pom.xml b/yoti-sdk-spring-security/pom.xml index 79a7d7d26..184e3be13 100644 --- a/yoti-sdk-spring-security/pom.xml +++ b/yoti-sdk-spring-security/pom.xml @@ -11,6 +11,7 @@ 4 24 3.0.0 + 4.3.14.RELEASE com.yoti @@ -46,6 +47,36 @@ hamcrest-core 1.3 + + org.springframework + spring-core + ${spring.core.version} + + + org.springframework + spring-aop + ${spring.core.version} + + + org.springframework + spring-beans + ${spring.core.version} + + + org.springframework + spring-web + ${spring.core.version} + + + org.springframework + spring-context + ${spring.core.version} + + + org.springframework + spring-expression + ${spring.core.version} +