diff --git a/.gitignore b/.gitignore index 5760a7a..5def27e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,54 +1,11 @@ -# ---> Java -# Compiled class file -*.class - -# Log file -*.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -replay_pid* - -# ---> macOS -# General +out/ +build +classes .DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - +.gradle +.classpath +.project +.settings +*.iml +*.ipr +*.iws diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..65d4e52 --- /dev/null +++ b/build.gradle @@ -0,0 +1,100 @@ +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' + +def JsonSlurper = Class.forName('groovy.json.JsonSlurper'); +def buildJSON = JsonSlurper.newInstance().parseText(new File("build.json").text) + +if (buildJSON.application) { apply plugin: 'application' } + +def baseProjectName = buildJSON?.project?.name ?: '__project_name__'; +def shellCommandName = baseProjectName +def eclipseProjectName = baseProjectName +def eclipseProjectComment = buildJSON?.project?.comment ?: '__project_name_comment__' +def jarManifestMainClass = buildJSON?.project?.main ?: 'SampleMain' + +if (buildJSON.application) { mainClassName = jarManifestMainClass } +archivesBaseName = buildJSON?.project?.archiveName ?: baseProjectName +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +def addRepo = new File(System.getProperty("user.home"), ".build_add.repo") + +repositories { + mavenLocal() + mavenCentral() + maven() { url 'https://maven.aliyun.com/repository/central' } + if (addRepo.exists()) { maven() { url addRepo.text.trim() } } +} + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +// '-x test' skip unit test +defaultTasks 'packjar' + +task packjarsrc << { + ant.jar(destfile: "${baseProjectName}-sources.jar") { + fileset(dir: 'src/main/java', includes: '**/*.java') + } +} +packjarsrc.dependsOn build + +task packjar << { + def packtempclasses = "packtempclasses" + def libs = ant.path { + fileset(dir: 'build/libs', includes: '*.jar') + } + libs.list().each { + ant.unzip(dest: packtempclasses, src: it) + } + new File(packtempclasses + "/jar-version-build.txt").write(new Date().format("yyyyMMdd"), "UTF-8") + ant.jar(destfile: "${baseProjectName}.jar") { + fileset(dir: packtempclasses, includes: '**/*.*') + } + ant.delete(dir: packtempclasses) +} +packjar.dependsOn packjarsrc + +dependencies { + compile files(fileTree(dir: 'lib', includes: ['*.jar'], excludes: ['*-sources.jar', '*-javadoc.jar'])) + + if (buildJSON.repo != null && buildJSON.repo.dependencies != null) { + buildJSON.repo.dependencies.each { + compile("${it}") + } + } + if (buildJSON.repo != null && buildJSON.repo.testDependencies != null) { + buildJSON.repo.testDependencies.each { + testCompile("${it}") + } + } +} + +eclipse { + project { + name = eclipseProjectName + comment = eclipseProjectComment + } + classpath { + defaultOutputDir = file('classes') + downloadSources = true + file { + whenMerged { classpath -> + classpath.entries.findAll { it.kind=='lib' }.each { + if ((it.path != null) && (it.sourcePath == null) && file(it.path.replace(".jar", "-sources.jar")).exists()) { + it.sourcePath = getFileReferenceFactory().fromPath(it.path.replace(".jar", "-sources.jar")) + } + } + } + } + } +} + +eclipseJdt << { + File f = file('.settings/org.eclipse.core.resources.prefs') + f.write('eclipse.preferences.version=1\n') + f.append('encoding/=utf-8') +} + diff --git a/build.json b/build.json new file mode 100644 index 0000000..2694db8 --- /dev/null +++ b/build.json @@ -0,0 +1,25 @@ +{ + "project": { + "name": "webauthnn4j-tests", + "main": "SampleMain", + "archiveName": "sample" + }, + "application": false, + "java": "1.8", + "builder": { + "name": "gradle", + "version": "3.1" + }, + "repo": { + "dependencies": [ + "me.hatter:commons:3.0", + "com.webauthn4j:webauthn4j-core:0.20.3.RELEASE", + "com.webauthn4j:webauthn4j-util:0.20.3.RELEASE", + "com.webauthn4j:webauthn4j-metadata:0.20.3.RELEASE", + "com.webauthn4j:webauthn4j-device-check:0.20.3.RELEASE" + ], + "testDependencies": [ + "junit:junit:4.12" + ] + } +} diff --git a/src/main/java/me/hatter/webauthn/tests/MdsTest.java b/src/main/java/me/hatter/webauthn/tests/MdsTest.java new file mode 100644 index 0000000..a0737ad --- /dev/null +++ b/src/main/java/me/hatter/webauthn/tests/MdsTest.java @@ -0,0 +1,52 @@ +package me.hatter.webauthn.tests; + +import com.webauthn4j.converter.util.ObjectConverter; +import com.webauthn4j.metadata.FidoMDS3MetadataBLOBProvider; +import com.webauthn4j.metadata.MetadataBLOBProvider; +import com.webauthn4j.metadata.data.MetadataBLOB; +import com.webauthn4j.metadata.data.MetadataBLOBPayload; +import com.webauthn4j.metadata.data.statement.MetadataStatement; +import com.webauthn4j.util.Base64Util; +import com.webauthn4j.util.CertificateUtil; + +import java.security.cert.X509Certificate; + +public class MdsTest { + static final X509Certificate rootCertificate = CertificateUtil.generateX509Certificate(Base64Util.decode( + "MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G" + + "A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp" + + "Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4" + + "MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG" + + "A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI" + + "hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8" + + "RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT" + + "gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm" + + "KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd" + + "QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ" + + "XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw" + + "DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o" + + "LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU" + + "RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp" + + "jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK" + + "6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX" + + "mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs" + + "Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH" + + "WD9f")); + + public static void main(String[] args) { + final MetadataBLOBProvider metadataBLOBProvider = new FidoMDS3MetadataBLOBProvider( + new ObjectConverter(), rootCertificate); + final MetadataBLOB metadataBLOB = metadataBLOBProvider.provide(); +// System.out.println(metadataBLOB.getHeader()); + final MetadataBLOBPayload metadataBLOBPayload = metadataBLOB.getPayload(); + metadataBLOBPayload.getEntries().forEach(entry -> { + System.out.println(entry.getAaguid()); + final MetadataStatement metadataStatement = entry.getMetadataStatement(); + if (metadataStatement != null) { + System.out.println("- " + metadataStatement.getDescription()); + System.out.println("- " + metadataStatement.getProtocolFamily()); + System.out.println("- " + metadataStatement.getKeyProtection()); + } + }); + } +} diff --git a/src/main/java/me/hatter/webauthn/tests/RegistrationTest.java b/src/main/java/me/hatter/webauthn/tests/RegistrationTest.java new file mode 100644 index 0000000..97ff1d4 --- /dev/null +++ b/src/main/java/me/hatter/webauthn/tests/RegistrationTest.java @@ -0,0 +1,87 @@ +package me.hatter.webauthn.tests; + +import com.webauthn4j.WebAuthnManager; +import com.webauthn4j.authenticator.Authenticator; +import com.webauthn4j.authenticator.AuthenticatorImpl; +import com.webauthn4j.converter.exception.DataConversionException; +import com.webauthn4j.data.RegistrationData; +import com.webauthn4j.data.RegistrationParameters; +import com.webauthn4j.data.RegistrationRequest; +import com.webauthn4j.data.client.Origin; +import com.webauthn4j.data.client.challenge.Challenge; +import com.webauthn4j.server.ServerProperty; +import com.webauthn4j.validator.attestation.statement.androidkey.AndroidKeyAttestationStatementValidator; +import com.webauthn4j.validator.attestation.statement.none.NoneAttestationStatementValidator; +import com.webauthn4j.validator.attestation.statement.packed.PackedAttestationStatementValidator; +import com.webauthn4j.validator.attestation.statement.u2f.FIDOU2FAttestationStatementValidator; +import com.webauthn4j.validator.attestation.trustworthiness.certpath.NullCertPathTrustworthinessValidator; +import com.webauthn4j.validator.attestation.trustworthiness.self.DefaultSelfAttestationTrustworthinessValidator; +import com.webauthn4j.validator.exception.ValidationException; + +import java.util.Arrays; +import java.util.Set; + +public class RegistrationTest { + public static void main(String[] args) { + NoneAttestationStatementValidator noneAttestationStatementValidator = new NoneAttestationStatementValidator(); + PackedAttestationStatementValidator packedAttestationStatementValidator = new PackedAttestationStatementValidator(); + FIDOU2FAttestationStatementValidator fidoU2FAttestationStatementValidator = new FIDOU2FAttestationStatementValidator(); + AndroidKeyAttestationStatementValidator androidKeyAttestationStatementValidator = new AndroidKeyAttestationStatementValidator(); + WebAuthnManager webAuthnManager = new WebAuthnManager( + Arrays.asList( + noneAttestationStatementValidator, + packedAttestationStatementValidator, + fidoU2FAttestationStatementValidator, + androidKeyAttestationStatementValidator), + new NullCertPathTrustworthinessValidator(), + new DefaultSelfAttestationTrustworthinessValidator() + ); + + // Client properties + byte[] attestationObject = null /* set attestationObject */; + byte[] clientDataJSON = null /* set clientDataJSON */; + String clientExtensionJSON = null; /* set clientExtensionJSON */ + Set transports = null /* set transports */; + + // Server properties + Origin origin = null /* set origin */; + String rpId = null /* set rpId */; + Challenge challenge = null /* set challenge */; + byte[] tokenBindingId = null /* set tokenBindingId */; + ServerProperty serverProperty = new ServerProperty(origin, rpId, challenge, tokenBindingId); + + // expectations + boolean userVerificationRequired = false; + boolean userPresenceRequired = true; + + RegistrationRequest registrationRequest = new RegistrationRequest(attestationObject, clientDataJSON, clientExtensionJSON, transports); + RegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, +// Arrays.asList( +// new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.RS256), +// new PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, COSEAlgorithmIdentifier.ES256) +// ), + null, + userVerificationRequired, userPresenceRequired); + RegistrationData registrationData; + try { + registrationData = webAuthnManager.parse(registrationRequest); + } catch (DataConversionException e) { + // If you would like to handle WebAuthn data structure parse error, please catch DataConversionException + throw e; + } + try { + webAuthnManager.validate(registrationData, registrationParameters); + } catch (ValidationException e) { + // If you would like to handle WebAuthn data validation error, please catch ValidationException + throw e; + } + + // please persist Authenticator object, which will be used in the authentication process. + Authenticator authenticator = new AuthenticatorImpl( + // You may create your own Authenticator implementation to save friendly authenticator name + registrationData.getAttestationObject().getAuthenticatorData().getAttestedCredentialData(), + registrationData.getAttestationObject().getAttestationStatement(), + registrationData.getAttestationObject().getAuthenticatorData().getSignCount() + ); + } +}