diff --git a/app/build.gradle b/app/build.gradle index 9a91e0e..3348336 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,17 @@ -apply plugin: 'com.android.application' +plugins { + alias(libs.plugins.android.application) +} android { - compileSdkVersion toolVersions.android.compileSdk - buildToolsVersion toolVersions.android.buildTools + namespace = 'com.datatheorem.android.trustkit.demoapp' + compileSdkVersion libs.versions.compileSdk.get().toInteger() defaultConfig { applicationId "com.datatheorem.android.trustkit.demoapp" - minSdkVersion toolVersions.android.minSdk - targetSdkVersion toolVersions.android.targetSdk - versionCode demoAppTrustKitVersionCode - versionName demoAppTrustKitVersionName + minSdkVersion libs.versions.minSdk.get().toInteger() + targetSdkVersion libs.versions.targetSdk.get().toInteger() + versionCode libs.versions.demoAppVersionCode.get().toInteger() + versionName libs.versions.demoAppVersionName.get() } buildTypes { release { @@ -17,11 +19,16 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + + compileOptions { + sourceCompatibility JavaVersion.toVersion(libs.versions.jvmTarget.get()) + targetCompatibility JavaVersion.toVersion(libs.versions.jvmTarget.get()) + } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation project(':trustkit') - implementation "androidx.appcompat:appcompat:$rootProject.libVersions.androidx.appcompat" - implementation "com.google.android.material:material:$rootProject.libVersions.google.material" + implementation libs.androidx.appcompat + implementation libs.google.material } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 15ae82b..7a837ba 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + @@ -14,6 +13,7 @@ @@ -23,4 +23,4 @@ - \ No newline at end of file + diff --git a/app/src/main/java/com/datatheorem/android/trustkit/demoapp/DemoMainActivity.java b/app/src/main/java/com/datatheorem/android/trustkit/demoapp/DemoMainActivity.java index 8d71729..98091d1 100644 --- a/app/src/main/java/com/datatheorem/android/trustkit/demoapp/DemoMainActivity.java +++ b/app/src/main/java/com/datatheorem/android/trustkit/demoapp/DemoMainActivity.java @@ -3,30 +3,26 @@ import android.content.IntentFilter; import android.os.AsyncTask; import android.os.Bundle; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.widget.TextView; - +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import com.datatheorem.android.trustkit.TrustKit; import com.datatheorem.android.trustkit.reporting.BackgroundReporter; - import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; - import javax.net.ssl.HttpsURLConnection; - public class DemoMainActivity extends AppCompatActivity { protected static final String DEBUG_TAG = "TrustKit-Demo"; - private static final PinningFailureReportBroadcastReceiver pinningFailureReportBroadcastReceiver - = new PinningFailureReportBroadcastReceiver(); + private static final PinningFailureReportBroadcastReceiver + pinningFailureReportBroadcastReceiver = new PinningFailureReportBroadcastReceiver(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -49,7 +45,7 @@ protected void onCreate(Bundle savedInstanceState) { IntentFilter intentFilter = new IntentFilter(BackgroundReporter.REPORT_VALIDATION_EVENT); LocalBroadcastManager.getInstance(getApplicationContext()) - .registerReceiver(pinningFailureReportBroadcastReceiver,intentFilter); + .registerReceiver(pinningFailureReportBroadcastReceiver, intentFilter); } @Override @@ -65,10 +61,11 @@ private class DownloadWebpageTask extends AsyncTask { protected String doInBackground(String... params) { try { URL url = new URL(params[0]); - HttpsURLConnection connection; + HttpsURLConnection connection = null; connection = (HttpsURLConnection) url.openConnection(); - connection.setSSLSocketFactory(TrustKit.getInstance().getSSLSocketFactory(url.getHost())); - connection.getInputStream(); + connection.setSSLSocketFactory( + TrustKit.getInstance().getSSLSocketFactory(url.getHost())); + InputStream inputStream = connection.getInputStream(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { @@ -107,4 +104,3 @@ public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } } - diff --git a/app/src/main/java/com/datatheorem/android/trustkit/demoapp/PinningFailureReportBroadcastReceiver.java b/app/src/main/java/com/datatheorem/android/trustkit/demoapp/PinningFailureReportBroadcastReceiver.java index 362976e..6fe1419 100644 --- a/app/src/main/java/com/datatheorem/android/trustkit/demoapp/PinningFailureReportBroadcastReceiver.java +++ b/app/src/main/java/com/datatheorem/android/trustkit/demoapp/PinningFailureReportBroadcastReceiver.java @@ -5,17 +5,14 @@ import android.content.Intent; import android.util.Log; import com.datatheorem.android.trustkit.reporting.BackgroundReporter; - import java.io.Serializable; /** * Class that provides an example broadcast receiver * - *

- * Applications using TrustKit can listen for local broadcasts and receive the same report that - * would be sent to the report_url. - *

- **/ + *

Applications using TrustKit can listen for local broadcasts and receive the same report that + * would be sent to the report_url. + */ class PinningFailureReportBroadcastReceiver extends BroadcastReceiver { @Override diff --git a/build.gradle b/build.gradle index d88adf6..fe383ee 100644 --- a/build.gradle +++ b/build.gradle @@ -1,76 +1,36 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - ext.kotlin_version = '1.3.21' - - repositories { - mavenCentral() - jcenter() // TODO: Remove when org.jetbrains.trove4j:trove4j moves to Maven Central - google() - } - dependencies { - classpath 'com.android.tools.build:gradle:3.6.1' - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.android.library) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.spotless) } allprojects { + apply { plugin(libs.plugins.spotless.get().pluginId) } + + spotless { + java { + target("src/**/*.java") + googleJavaFormat(libs.versions.google.java.format.get()).aosp() + } + kotlin { + target("src/**/*.kt") + ktfmt(libs.versions.ktfmt.get()).kotlinlangStyle() + } + groovyGradle { + target("*.gradle") + greclipse().configProperties("org.eclipse.jdt.core.formatter.tabulation.char=space") + } + kotlinGradle { + target("*.gradle.kts") + ktfmt(libs.versions.ktfmt.get()).kotlinlangStyle() + } + } + repositories { mavenCentral() - jcenter() // TODO: Remove when org.jetbrains.trove4j:trove4j moves to Maven Central google() } - apply plugin: 'maven' - apply plugin: 'maven-publish' -} - - -ext{ - trustkitVersionCode = 10 - trustkitVersionName = "1.1.5" - - demoAppTrustKitVersionCode = 2 - demoAppTrustKitVersionName = "1.1" - - demoAppKotlinTrustKitVersionCode = 2 - demoAppKotlinTrustKitVersionName = "1.1" - javaSourceCompatibilty = '1.6' - toolVersions = [ - android : [ - compileSdk : 28, - gradlePlugin : '2.1.0', - buildTools : '28.0.3', - minSdk : 16, - targetSdk: 28 - ] - ] - - libVersions = [ - junit: '4.12', - mockito : [ - android: '1.10.19' - ], - dexmaker : '1.4', - androidx : [ - annotation: '1.0.0', - test: '1.1.0', - legacySupport: '1.0.0', - appcompat: '1.0.2', - preference: '1.0.0' - ], - testing: [ - okhttp3: '3.11.0', - 'playServicesBase': '11.0.0', - ], - google: [ - material: '1.0.0' - ], - squareup: [ - okhttp3: '3.11.0', - okhttp2: '2.4.0' - ] - ] - } diff --git a/demoappkotlin/build.gradle b/demoappkotlin/build.gradle index 1aa585c..d4eb22e 100644 --- a/demoappkotlin/build.gradle +++ b/demoappkotlin/build.gradle @@ -1,17 +1,20 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) +} android { - compileSdkVersion toolVersions.android.compileSdk + namespace = 'com.datatheorem.android.trustkit.demoappkotlin' + compileSdkVersion libs.versions.compileSdk.get().toInteger() defaultConfig { applicationId "com.datatheorem.android.trustkit.demoappkotlin" - minSdkVersion toolVersions.android.minSdk - targetSdkVersion toolVersions.android.targetSdk - versionCode demoAppKotlinTrustKitVersionCode - versionName demoAppKotlinTrustKitVersionName + minSdkVersion libs.versions.minSdk.get().toInteger() + targetSdkVersion libs.versions.targetSdk.get().toInteger() + versionCode libs.versions.demoAppVersionCode.get().toInteger() + versionName libs.versions.demoAppVersionName.get() } buildTypes { @@ -21,15 +24,20 @@ android { } } + compileOptions { + sourceCompatibility JavaVersion.toVersion(libs.versions.jvmTarget.get()) + targetCompatibility JavaVersion.toVersion(libs.versions.jvmTarget.get()) + } + + kotlinOptions { + jvmTarget = libs.versions.jvmTarget.get() + } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation "androidx.appcompat:appcompat:$rootProject.libVersions.androidx.appcompat" - implementation "com.google.android.material:material:$rootProject.libVersions.google.material" - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation libs.androidx.appcompat + implementation libs.google.material + implementation libs.kotlin.stdlib.jdk7 implementation project(path: ':trustkit') } -repositories { - mavenCentral() -} diff --git a/demoappkotlin/src/main/AndroidManifest.xml b/demoappkotlin/src/main/AndroidManifest.xml index 883fc1b..0a3d4f6 100644 --- a/demoappkotlin/src/main/AndroidManifest.xml +++ b/demoappkotlin/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + @@ -13,6 +12,7 @@ android:networkSecurityConfig="@xml/network_security_config"> diff --git a/demoappkotlin/src/main/java/com/datatheorem/android/trustkit/demoappkotlin/DemoMainActivity.kt b/demoappkotlin/src/main/java/com/datatheorem/android/trustkit/demoappkotlin/DemoMainActivity.kt index 1c64a71..76f9254 100644 --- a/demoappkotlin/src/main/java/com/datatheorem/android/trustkit/demoappkotlin/DemoMainActivity.kt +++ b/demoappkotlin/src/main/java/com/datatheorem/android/trustkit/demoappkotlin/DemoMainActivity.kt @@ -3,14 +3,14 @@ package com.datatheorem.android.trustkit.demoappkotlin import android.content.IntentFilter import android.os.AsyncTask import android.os.Bundle -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.View import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.datatheorem.android.trustkit.TrustKit import com.datatheorem.android.trustkit.reporting.BackgroundReporter import java.io.IOException @@ -18,9 +18,9 @@ import java.net.MalformedURLException import java.net.URL import javax.net.ssl.HttpsURLConnection - class DemoMainActivity : AppCompatActivity() { - private lateinit var pinningFailureReportBroadcastReceiver: PinningFailureReportBroadcastReceiver + private lateinit var pinningFailureReportBroadcastReceiver: + PinningFailureReportBroadcastReceiver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -41,15 +41,15 @@ class DemoMainActivity : AppCompatActivity() { textView.text = "Connection results are in the logs" // Adding a local broadcast receiver to listen for validation report events - pinningFailureReportBroadcastReceiver = PinningFailureReportBroadcastReceiver() + pinningFailureReportBroadcastReceiver = PinningFailureReportBroadcastReceiver() val intentFilter = IntentFilter(BackgroundReporter.REPORT_VALIDATION_EVENT) LocalBroadcastManager.getInstance(this.applicationContext) - .registerReceiver(pinningFailureReportBroadcastReceiver,intentFilter) + .registerReceiver(pinningFailureReportBroadcastReceiver, intentFilter) } override fun onDestroy() { LocalBroadcastManager.getInstance(this.applicationContext) - .unregisterReceiver(pinningFailureReportBroadcastReceiver) + .unregisterReceiver(pinningFailureReportBroadcastReceiver) super.onDestroy() } @@ -90,11 +90,9 @@ class DemoMainActivity : AppCompatActivity() { // as you specify a parent activity in AndroidManifest.xml. val id = item.itemId - return if (id == R.id.action_settings) { true } else super.onOptionsItemSelected(item) - } companion object { @@ -102,4 +100,3 @@ class DemoMainActivity : AppCompatActivity() { internal const val DEBUG_TAG = "TrustKit-Demo" } } - diff --git a/demoappkotlin/src/main/java/com/datatheorem/android/trustkit/demoappkotlin/PinningFailureReportBroadcastReceiver.kt b/demoappkotlin/src/main/java/com/datatheorem/android/trustkit/demoappkotlin/PinningFailureReportBroadcastReceiver.kt index acbab09..689915b 100644 --- a/demoappkotlin/src/main/java/com/datatheorem/android/trustkit/demoappkotlin/PinningFailureReportBroadcastReceiver.kt +++ b/demoappkotlin/src/main/java/com/datatheorem/android/trustkit/demoappkotlin/PinningFailureReportBroadcastReceiver.kt @@ -10,15 +10,13 @@ import com.datatheorem.android.trustkit.reporting.BackgroundReporter * Class that provides an example broadcast receiver * *

- * Applications using TrustKit can listen for local broadcasts and receive the same report that - * would be sent to the report_url. - *

- **/ + * Applications using TrustKit can listen for local broadcasts and receive the same report that + * would be sent to the report_url.

+ */ class PinningFailureReportBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val result = intent.getSerializableExtra(BackgroundReporter.EXTRA_REPORT) Log.i(DemoMainActivity.DEBUG_TAG, result.toString()) } - } diff --git a/deploymentScripts/publish-mavencentral.gradle b/deploymentScripts/publish-mavencentral.gradle index 713edea..1c6adfa 100644 --- a/deploymentScripts/publish-mavencentral.gradle +++ b/deploymentScripts/publish-mavencentral.gradle @@ -3,7 +3,7 @@ apply plugin: 'signing' ext { PUBLISH_GROUP_ID = 'com.datatheorem.android.trustkit' - PUBLISH_VERSION = trustkitVersionName + PUBLISH_VERSION = project.version.toString() PUBLISH_ARTIFACT_ID = 'trustkit' DESCRIPTION = 'TrustKit Android is an open source library that makes it easy to deploy SSL public key pinning and reporting in any Android App.' LICENSE_NAME = 'The MIT License (MIT)' @@ -65,9 +65,9 @@ publishing { publications { release(MavenPublication) { // 'groupId' = namespace, 'artifactId' = library name, 'version' = library version - groupId PUBLISH_GROUP_ID - artifactId PUBLISH_ARTIFACT_ID - version PUBLISH_VERSION + groupId = PUBLISH_GROUP_ID + artifactId = PUBLISH_ARTIFACT_ID + version = PUBLISH_VERSION // Two artifacts, the truskit '.aar' and the sources '.jar' artifact("$buildDir/outputs/aar/${project.getName()}-release.aar") artifact androidSourcesJar @@ -125,8 +125,8 @@ publishing { name = "mavencentral" url = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" credentials { - username ossrhUsername - password ossrhPassword + username = ossrhUsername + password = ossrhPassword } } } @@ -135,4 +135,4 @@ publishing { signing { sign publishing.publications -} \ No newline at end of file +} diff --git a/gradle.properties b/gradle.properties index 915f0e6..b804431 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,11 +10,11 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true android.enableJetifier=true -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..df2f745 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,50 @@ +[versions] +agp = "8.13.2" +androidx-annotation = "1.0.0" +androidx-appcompat = "1.0.2" +androidx-preference = "1.0.0" +androidx-test = "1.1.0" +awaitility = "3.1.6" +compileSdk = "36" +demoAppVersionCode = "2" +demoAppVersionName = "1.1" +dexmaker = "1.4" +google-java-format = "1.15.0" +jvmTarget = "11" +junit = "4.12" +kotlin = "1.9.25" +ktfmt = "0.61" +material = "1.0.0" +minSdk = "15" +mockito = "1.10.19" +okhttp2 = "2.4.0" +okhttp3 = "3.11.0" +play-services-base = "11.0.0" +spotless = "8.10.0" +targetSdk = "36" +trustkitVersionCode = "10" +trustkitVersionName = "1.1.5" + +[libraries] +androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } +androidx-preference = { module = "androidx.preference:preference", version.ref = "androidx-preference" } +androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidx-test" } +androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test" } +awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" } +dexmaker-core = { module = "com.crittercism.dexmaker:dexmaker", version.ref = "dexmaker" } +dexmaker-dx = { module = "com.crittercism.dexmaker:dexmaker-dx", version.ref = "dexmaker" } +dexmaker-mockito = { module = "com.crittercism.dexmaker:dexmaker-mockito", version.ref = "dexmaker" } +google-material = { module = "com.google.android.material:material", version.ref = "material" } +google-play-services-base = { module = "com.google.android.gms:play-services-base", version.ref = "play-services-base" } +junit = { module = "junit:junit", version.ref = "junit" } +kotlin-stdlib-jdk7 = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" } +mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito" } +okhttp2 = { module = "com.squareup.okhttp:okhttp", version.ref = "okhttp2" } +okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp3" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372ae..9bbc975 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cc1060b..ed4c299 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Tue Mar 10 21:49:17 PDT 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip diff --git a/gradlew b/gradlew index 9d82f78..faf9300 100755 --- a/gradlew +++ b/gradlew @@ -1,74 +1,129 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -77,84 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..9d21a21 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,4 +1,22 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -8,26 +26,30 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -35,54 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/settings.gradle b/settings.gradle index deb4a9b..d1011df 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,9 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + include ':app', ':trustkit', ':demoappkotlin' diff --git a/trustkit/build.gradle b/trustkit/build.gradle index 3faae6c..10fe38e 100644 --- a/trustkit/build.gradle +++ b/trustkit/build.gradle @@ -1,31 +1,46 @@ -apply plugin: 'com.android.library' +plugins { + alias(libs.plugins.android.library) +} + +version = libs.versions.trustkitVersionName.get() android { + namespace = 'com.datatheorem.android.trustkit' + compileSdkVersion libs.versions.compileSdk.get().toInteger() + + buildFeatures { + buildConfig = true + } + defaultConfig { - compileSdkVersion toolVersions.android.compileSdk - buildToolsVersion toolVersions.android.buildTools - minSdkVersion toolVersions.android.minSdk - versionCode trustkitVersionCode - versionName trustkitVersionName + minSdkVersion libs.versions.minSdk.get().toInteger() + versionCode libs.versions.trustkitVersionCode.get().toInteger() + versionName project.version.toString() + buildConfigField 'String', 'VERSION_NAME', "\"${project.version}\"" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + + compileOptions { + sourceCompatibility JavaVersion.toVersion(libs.versions.jvmTarget.get()) + targetCompatibility JavaVersion.toVersion(libs.versions.jvmTarget.get()) + } } dependencies { - implementation "androidx.annotation:annotation:$rootProject.libVersions.androidx.annotation" - implementation "androidx.preference:preference:$rootProject.libVersions.androidx.preference" - compileOnly "com.squareup.okhttp3:okhttp:$rootProject.libVersions.squareup.okhttp3" - compileOnly "com.squareup.okhttp:okhttp:$rootProject.libVersions.squareup.okhttp2" - androidTestImplementation "junit:junit:$rootProject.libVersions.junit" - androidTestImplementation "androidx.test:runner:$rootProject.libVersions.androidx.test" - androidTestImplementation "androidx.test:rules:$rootProject.libVersions.androidx.test" - androidTestImplementation "org.mockito:mockito-core:$rootProject.libVersions.mockito.android" - androidTestImplementation "org.awaitility:awaitility:3.1.6" - androidTestImplementation "com.crittercism.dexmaker:dexmaker:$rootProject.libVersions.dexmaker" - androidTestImplementation "com.crittercism.dexmaker:dexmaker-dx:$rootProject.libVersions.dexmaker" - androidTestImplementation "com.crittercism.dexmaker:dexmaker-mockito:$rootProject.libVersions.dexmaker" - androidTestImplementation "com.squareup.okhttp3:okhttp:$rootProject.libVersions.testing.okhttp3" - androidTestImplementation "com.google.android.gms:play-services-base:$rootProject.libVersions.testing.playServicesBase" + implementation libs.androidx.annotation + implementation libs.androidx.preference + compileOnly libs.okhttp3 + compileOnly libs.okhttp2 + androidTestImplementation libs.junit + androidTestImplementation libs.androidx.test.runner + androidTestImplementation libs.androidx.test.rules + androidTestImplementation libs.mockito.core + androidTestImplementation libs.awaitility + androidTestImplementation libs.dexmaker.core + androidTestImplementation libs.dexmaker.dx + androidTestImplementation libs.dexmaker.mockito + androidTestImplementation libs.okhttp3 + androidTestImplementation libs.google.play.services.base } // MavenCentral deployment gradle script diff --git a/trustkit/src/androidTest/AndroidManifest.xml b/trustkit/src/androidTest/AndroidManifest.xml index 27c74c5..da10be4 100644 --- a/trustkit/src/androidTest/AndroidManifest.xml +++ b/trustkit/src/androidTest/AndroidManifest.xml @@ -1,5 +1,5 @@ - + diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/CertificateUtils.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/CertificateUtils.java index 64c48aa..a88350d 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/CertificateUtils.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/CertificateUtils.java @@ -11,66 +11,72 @@ public class CertificateUtils { - static private final String intermediatePem = - "-----BEGIN CERTIFICATE-----\n" + - "MIID8DCCAtigAwIBAgIDAjqSMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT\n" + - "MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\n" + - "YWwgQ0EwHhcNMTUwNDAxMDAwMDAwWhcNMTcxMjMxMjM1OTU5WjBJMQswCQYDVQQG\n" + - "EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy\n" + - "bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" + - "AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP\n" + - "VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv\n" + - "h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE\n" + - "ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ\n" + - "EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC\n" + - "DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7\n" + - "qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYD\n" + - "VR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDov\n" + - "L2cuc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCig\n" + - "JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMBcGA1UdIAQQ\n" + - "MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQsFAAOCAQEACE4Ep4B/EBZDXgKt\n" + - "10KA9LCO0q6z6xF9kIQYfeeQFftJf6iZBZG7esnWPDcYCZq2x5IgBzUzCeQoY3IN\n" + - "tOAynIeYxBt2iWfBUFiwE6oTGhsypb7qEZVMSGNJ6ZldIDfM/ippURaVS6neSYLA\n" + - "EHD0LPPsvCQk0E6spdleHm2SwaesSDWB+eXknGVpzYekQVA/LlelkVESWA6MCaGs\n" + - "eqQSpSfzmhCXfVUDBvdmWF9fZOGrXW2lOUh1mEwpWjqN0yvKnFUEv/TmFNWArCbt\n" + - "F4mmk2xcpMy48GaOZON9muIAs0nH5Aqq3VuDx3CQRk6+0NtZlmwu9RY23nHMAcIS\n" + - "wSHGFg==\n" + - "-----END CERTIFICATE-----"; + private static final String intermediatePem = + "-----BEGIN CERTIFICATE-----\n" + + "MIID8DCCAtigAwIBAgIDAjqSMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT\n" + + "MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i\n" + + "YWwgQ0EwHhcNMTUwNDAxMDAwMDAwWhcNMTcxMjMxMjM1OTU5WjBJMQswCQYDVQQG\n" + + "EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy\n" + + "bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" + + "AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP\n" + + "VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv\n" + + "h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE\n" + + "ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ\n" + + "EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC\n" + + "DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7\n" + + "qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYD\n" + + "VR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDov\n" + + "L2cuc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCig\n" + + "JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMBcGA1UdIAQQ\n" + + "MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQsFAAOCAQEACE4Ep4B/EBZDXgKt\n" + + "10KA9LCO0q6z6xF9kIQYfeeQFftJf6iZBZG7esnWPDcYCZq2x5IgBzUzCeQoY3IN\n" + + "tOAynIeYxBt2iWfBUFiwE6oTGhsypb7qEZVMSGNJ6ZldIDfM/ippURaVS6neSYLA\n" + + "EHD0LPPsvCQk0E6spdleHm2SwaesSDWB+eXknGVpzYekQVA/LlelkVESWA6MCaGs\n" + + "eqQSpSfzmhCXfVUDBvdmWF9fZOGrXW2lOUh1mEwpWjqN0yvKnFUEv/TmFNWArCbt\n" + + "F4mmk2xcpMy48GaOZON9muIAs0nH5Aqq3VuDx3CQRk6+0NtZlmwu9RY23nHMAcIS\n" + + "wSHGFg==\n" + + "-----END CERTIFICATE-----"; - static private final String leafPem = - "-----BEGIN CERTIFICATE-----\n" + - "MIID1jCCAr6gAwIBAgIIAPCznYJ9GMQwDQYJKoZIhvcNAQELBQAwSTELMAkGA1UE\n" + - "BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl\n" + - "cm5ldCBBdXRob3JpdHkgRzIwHhcNMTYwOTI5MTcwMjI5WhcNMTYxMjIyMTYzNzAw\n" + - "WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN\n" + - "TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp\n" + - "bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8W4z2S50gAvv\n" + - "0VC6l7isbjD0Q7d7BiKWeOQwqfY+dLTmxZvpxBpcrfPlh170R3ai+qn/BE7t4+k2\n" + - "a+LrfYag8KOCAWswggFnMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAs\n" + - "BgNVHREEJTAjgg9tYWlsLmdvb2dsZS5jb22CEGluYm94Lmdvb2dsZS5jb20wCwYD\n" + - "VR0PBAQDAgeAMGgGCCsGAQUFBwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3Br\n" + - "aS5nb29nbGUuY29tL0dJQUcyLmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVu\n" + - "dHMxLmdvb2dsZS5jb20vb2NzcDAdBgNVHQ4EFgQUTuRBMq9TH5Dh82EGV6U37V8b\n" + - "3AcwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqB\n" + - "LzAhBgNVHSAEGjAYMAwGCisGAQQB1nkCBQEwCAYGZ4EMAQICMDAGA1UdHwQpMCcw\n" + - "JaAjoCGGH2h0dHA6Ly9wa2kuZ29vZ2xlLmNvbS9HSUFHMi5jcmwwDQYJKoZIhvcN\n" + - "AQELBQADggEBADqnYrHvHoCc7ltooq4XVj3yEyFX+n/hgrdQMmOgVcl3bHNYV5EG\n" + - "IqOClo5g1RyWcRfji8RQGv0hvFb6L2Zef5sOpQs3COEVW05kmCdwWSlCCpp6pJma\n" + - "yf6Nf4TreI8gpokoJgTNNgmq5OgT9K+G16I2L/CKv8rTh9HaoOOXWx90s5rAn/G/\n" + - "JrRRcgICjonU7m+ab22vdVilOJlEuMdX7x1CBPtHY/c214oJ32AxSTewXUjjDsWY\n" + - "c2azHgSpG5uA/TocrImlvajjrdZwQjtj8wO4av35BdaaOlfMo/xqa6VQfpA6W9ml\n" + - "jTL1zvT0Sv8mKow3b3blztbbeaVifTHShrA=\n" + - "-----END CERTIFICATE-----"; + private static final String leafPem = + "-----BEGIN CERTIFICATE-----\n" + + "MIID1jCCAr6gAwIBAgIIAPCznYJ9GMQwDQYJKoZIhvcNAQELBQAwSTELMAkGA1UE\n" + + "BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl\n" + + "cm5ldCBBdXRob3JpdHkgRzIwHhcNMTYwOTI5MTcwMjI5WhcNMTYxMjIyMTYzNzAw\n" + + "WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN\n" + + "TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp\n" + + "bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8W4z2S50gAvv\n" + + "0VC6l7isbjD0Q7d7BiKWeOQwqfY+dLTmxZvpxBpcrfPlh170R3ai+qn/BE7t4+k2\n" + + "a+LrfYag8KOCAWswggFnMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAs\n" + + "BgNVHREEJTAjgg9tYWlsLmdvb2dsZS5jb22CEGluYm94Lmdvb2dsZS5jb20wCwYD\n" + + "VR0PBAQDAgeAMGgGCCsGAQUFBwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3Br\n" + + "aS5nb29nbGUuY29tL0dJQUcyLmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVu\n" + + "dHMxLmdvb2dsZS5jb20vb2NzcDAdBgNVHQ4EFgQUTuRBMq9TH5Dh82EGV6U37V8b\n" + + "3AcwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqB\n" + + "LzAhBgNVHSAEGjAYMAwGCisGAQQB1nkCBQEwCAYGZ4EMAQICMDAGA1UdHwQpMCcw\n" + + "JaAjoCGGH2h0dHA6Ly9wa2kuZ29vZ2xlLmNvbS9HSUFHMi5jcmwwDQYJKoZIhvcN\n" + + "AQELBQADggEBADqnYrHvHoCc7ltooq4XVj3yEyFX+n/hgrdQMmOgVcl3bHNYV5EG\n" + + "IqOClo5g1RyWcRfji8RQGv0hvFb6L2Zef5sOpQs3COEVW05kmCdwWSlCCpp6pJma\n" + + "yf6Nf4TreI8gpokoJgTNNgmq5OgT9K+G16I2L/CKv8rTh9HaoOOXWx90s5rAn/G/\n" + + "JrRRcgICjonU7m+ab22vdVilOJlEuMdX7x1CBPtHY/c214oJ32AxSTewXUjjDsWY\n" + + "c2azHgSpG5uA/TocrImlvajjrdZwQjtj8wO4av35BdaaOlfMo/xqa6VQfpA6W9ml\n" + + "jTL1zvT0Sv8mKow3b3blztbbeaVifTHShrA=\n" + + "-----END CERTIFICATE-----"; - static public final ArrayList testCertChainPem = new ArrayList() {{ - add(leafPem); - add(intermediatePem); - }}; + public static final ArrayList testCertChainPem = + new ArrayList() { + { + add(leafPem); + add(intermediatePem); + } + }; - static public final ArrayList testCertChain = new ArrayList() {{ - add((X509Certificate) certificateFromPem(leafPem)); - add((X509Certificate) certificateFromPem(intermediatePem)); - }}; + public static final ArrayList testCertChain = + new ArrayList() { + { + add((X509Certificate) certificateFromPem(leafPem)); + add((X509Certificate) certificateFromPem(intermediatePem)); + } + }; public static Certificate certificateFromPem(String pemCertificate) { pemCertificate = pemCertificate.replace("-----BEGIN CERTIFICATE-----\n", ""); @@ -80,7 +86,7 @@ public static Certificate certificateFromPem(String pemCertificate) { CertificateFactory cf = CertificateFactory.getInstance("X.509"); return cf.generateCertificate(is); } catch (CertificateException e) { - throw new RuntimeException("Should never happen"); + throw new RuntimeException("Should never happen"); } } } diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/HttpLibrariesTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/HttpLibrariesTest.java index 055b751..e9839e3 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/HttpLibrariesTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/HttpLibrariesTest.java @@ -4,10 +4,9 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.verify; -import androidx.test.platform.app.InstrumentationRegistry; import android.os.Build; -import androidx.annotation.RequiresApi; - +import androidx.test.filters.SdkSuppress; +import androidx.test.platform.app.InstrumentationRegistry; import com.datatheorem.android.trustkit.pinning.PinningValidationResult; import com.datatheorem.android.trustkit.reporting.BackgroundReporter; import java.io.IOException; @@ -29,10 +28,10 @@ @SuppressWarnings("unchecked") public class HttpLibrariesTest { - @Mock - private BackgroundReporter reporter; + @Mock private BackgroundReporter reporter; + + private static final URL testUrl; - static private final URL testUrl; static { try { // The network policy for the tests has invalid pins configured for this domain @@ -44,7 +43,7 @@ public class HttpLibrariesTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.initMocks(this); TestableTrustKit.reset(); } @@ -56,7 +55,7 @@ public void testHttpsUrlConnectionWithTrustKit() throws MalformedURLException { } // Initialize TrustKit TestableTrustKit.initializeWithNetworkSecurityConfiguration( - InstrumentationRegistry.getInstrumentation().getContext(), reporter); + InstrumentationRegistry.getInstrumentation().getContext(), reporter); // Test a connection HttpsURLConnection connection = null; @@ -64,8 +63,7 @@ public void testHttpsUrlConnectionWithTrustKit() throws MalformedURLException { try { connection = (HttpsURLConnection) testUrl.openConnection(); connection.setSSLSocketFactory( - TestableTrustKit.getInstance().getSSLSocketFactory(testUrl.getHost()) - ); + TestableTrustKit.getInstance().getSSLSocketFactory(testUrl.getHost())); InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); @@ -88,17 +86,19 @@ public void testHttpsUrlConnectionWithTrustKit() throws MalformedURLException { assertTrue(didReceiveHandshakeError); // Ensure the reporter was called - verify(reporter).pinValidationFailed( - eq(testUrl.getHost()), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration() - .getPolicyForHostname(testUrl.getHost())), - eq(PinningValidationResult.FAILED)); + verify(reporter) + .pinValidationFailed( + eq(testUrl.getHost()), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(testUrl.getHost())), + eq(PinningValidationResult.FAILED)); } - @Test public void testHttpsUrlConnectionWithTrustKitApiLevelUnder17() throws IOException { if (Build.VERSION.SDK_INT >= 17) { @@ -116,8 +116,7 @@ public void testHttpsUrlConnectionWithTrustKitApiLevelUnder17() throws IOExcepti try { connection = (HttpsURLConnection) testUrl.openConnection(); connection.setSSLSocketFactory( - TestableTrustKit.getInstance().getSSLSocketFactory(testUrl.getHost()) - ); + TestableTrustKit.getInstance().getSSLSocketFactory(testUrl.getHost())); InputStream inputStream = connection.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); @@ -134,7 +133,6 @@ public void testHttpsUrlConnectionWithTrustKitApiLevelUnder17() throws IOExcepti } } - @Test public void testOkhttp3WithTrustKit() throws MalformedURLException { if (Build.VERSION.SDK_INT < 17) { @@ -147,9 +145,13 @@ public void testOkhttp3WithTrustKit() throws MalformedURLException { // Test a connection boolean didReceiveHandshakeError = false; - OkHttpClient client = new OkHttpClient().newBuilder() - .sslSocketFactory(TestableTrustKit.getInstance().getSSLSocketFactory(testUrl.getHost())) - .build(); + OkHttpClient client = + new OkHttpClient() + .newBuilder() + .sslSocketFactory( + TestableTrustKit.getInstance() + .getSSLSocketFactory(testUrl.getHost())) + .build(); try { Request request = new Request.Builder().url(testUrl).build(); client.newCall(request).execute(); @@ -163,24 +165,28 @@ public void testOkhttp3WithTrustKit() throws MalformedURLException { assertTrue(didReceiveHandshakeError); // Ensure the reporter was called - verify(reporter).pinValidationFailed( - eq(testUrl.getHost()), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration() - .getPolicyForHostname(testUrl.getHost())), - eq(PinningValidationResult.FAILED)); + verify(reporter) + .pinValidationFailed( + eq(testUrl.getHost()), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(testUrl.getHost())), + eq(PinningValidationResult.FAILED)); } // A specific test is needed for the previous version of the OkHttpClient Builder. - // The previous one signature only asks for a SSLSocketFactory compare to the newBuilder asking for + // The previous one signature only asks for a SSLSocketFactory compare to the newBuilder asking + // for // a SSLSocketFactory and a TrustManager. // It's a common issue with this version of OkHttp : // https://github.com/square/okhttp/issues/2323#issuecomment-185055040/ // More information about they're trying to extract all the SSL needed object here : // https://github.com/square/okhttp/blob/okhttp_31/okhttp/src/main/java/okhttp3/internal/Platform.java - @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN) @Test public void testOkhttp3WithTrustKitOldBuilder() throws MalformedURLException { if (Build.VERSION.SDK_INT < 17) { @@ -189,19 +195,22 @@ public void testOkhttp3WithTrustKitOldBuilder() throws MalformedURLException { } // Initialize TrustKit TestableTrustKit.initializeWithNetworkSecurityConfiguration( - InstrumentationRegistry.getInstrumentation().getContext(), reporter); + InstrumentationRegistry.getInstrumentation().getContext(), reporter); // Test a connection boolean didReceiveHandshakeError = false; - OkHttpClient client = new OkHttpClient.Builder() - .sslSocketFactory(TestableTrustKit.getInstance().getSSLSocketFactory(testUrl.getHost())) - .build(); + OkHttpClient client = + new OkHttpClient.Builder() + .sslSocketFactory( + TestableTrustKit.getInstance() + .getSSLSocketFactory(testUrl.getHost())) + .build(); try { Request request = new Request.Builder().url(testUrl).build(); client.newCall(request).execute(); } catch (IOException e) { if ((e.getCause() instanceof CertificateException - && (e.getCause().getMessage().startsWith("Pin verification failed")))) { + && (e.getCause().getMessage().startsWith("Pin verification failed")))) { didReceiveHandshakeError = true; } } @@ -209,13 +218,16 @@ public void testOkhttp3WithTrustKitOldBuilder() throws MalformedURLException { assertTrue(didReceiveHandshakeError); // Ensure the reporter was called - verify(reporter).pinValidationFailed( - eq(testUrl.getHost()), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration() - .getPolicyForHostname(testUrl.getHost())), - eq(PinningValidationResult.FAILED)); + verify(reporter) + .pinValidationFailed( + eq(testUrl.getHost()), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(testUrl.getHost())), + eq(PinningValidationResult.FAILED)); } } diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/TestableTrustKit.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/TestableTrustKit.java index 6c7fa74..13cf1d4 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/TestableTrustKit.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/TestableTrustKit.java @@ -1,6 +1,5 @@ package com.datatheorem.android.trustkit; - import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -12,18 +11,18 @@ import java.security.cert.Certificate; import java.util.Set; - // The main TrustKit class with some extra utility methods needed in the tests public class TestableTrustKit extends TrustKit { - private TestableTrustKit(Context context, TrustKitConfiguration trustKitConfiguration, - BackgroundReporter reporter) { + private TestableTrustKit( + Context context, + TrustKitConfiguration trustKitConfiguration, + BackgroundReporter reporter) { super(context, trustKitConfiguration); TestableTrustManagerBuilder.setReporter(reporter); } - - public static TrustKit initializeWithNetworkSecurityConfiguration(@NonNull Context context, - BackgroundReporter reporter) { + public static TrustKit initializeWithNetworkSecurityConfiguration( + @NonNull Context context, BackgroundReporter reporter) { TrustKit.initializeWithNetworkSecurityConfiguration(context); TestableTrustManagerBuilder.setReporter(reporter); return TrustKit.getInstance(); @@ -31,21 +30,26 @@ public static TrustKit initializeWithNetworkSecurityConfiguration(@NonNull Conte // This lets us directly specify domain settings without parsing an XML file and inject/mock // the background reporter - public static void init(@NonNull Set domainConfigSet, - @NonNull Context context, - BackgroundReporter reporter) { - trustKitInstance = new TrustKit(context, new TestableTrustKitConfiguration(domainConfigSet)); + public static void init( + @NonNull Set domainConfigSet, + @NonNull Context context, + BackgroundReporter reporter) { + trustKitInstance = + new TrustKit(context, new TestableTrustKitConfiguration(domainConfigSet)); TestableTrustManagerBuilder.setReporter(reporter); } - - public static void init(@NonNull Set domainConfigSet, - boolean shouldOverridePins, - @Nullable Set debugCaCerts, - @NonNull Context context, - BackgroundReporter reporter) { - trustKitInstance = new TrustKit(context, new TestableTrustKitConfiguration(domainConfigSet, - shouldOverridePins, debugCaCerts)); + public static void init( + @NonNull Set domainConfigSet, + boolean shouldOverridePins, + @Nullable Set debugCaCerts, + @NonNull Context context, + BackgroundReporter reporter) { + trustKitInstance = + new TrustKit( + context, + new TestableTrustKitConfiguration( + domainConfigSet, shouldOverridePins, debugCaCerts)); TestableTrustManagerBuilder.setReporter(reporter); } diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/TrustKitTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/TrustKitTest.java index 8053220..2b95dd1 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/TrustKitTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/TrustKitTest.java @@ -1,19 +1,16 @@ package com.datatheorem.android.trustkit; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + import android.content.Context; import android.content.res.Resources; import android.os.Build; - import androidx.test.platform.app.InstrumentationRegistry; - import com.datatheorem.android.trustkit.config.ConfigurationException; - import org.junit.Before; import org.junit.Test; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; - public class TrustKitTest { @Before @@ -44,10 +41,12 @@ public void testInitializeWithDefaultXmlFile() { @Test public void testInitializeWithValidXmlFile() { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - int networkSecurityConfigId = context.getResources().getIdentifier( - "network_security_config", "xml", context.getPackageName()); - TrustKit trustkit = TrustKit.initializeWithNetworkSecurityConfiguration(context, - networkSecurityConfigId); + int networkSecurityConfigId = + context.getResources() + .getIdentifier("network_security_config", "xml", context.getPackageName()); + TrustKit trustkit = + TrustKit.initializeWithNetworkSecurityConfiguration( + context, networkSecurityConfigId); assertNotNull(trustkit); } @@ -72,8 +71,8 @@ public void testInitializeWithBadResourceId() { @Test public void testInitializeWithBadFile() { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - int pemFileId = context.getResources().getIdentifier("cacertorg", "raw", - context.getPackageName()); + int pemFileId = + context.getResources().getIdentifier("cacertorg", "raw", context.getPackageName()); boolean didInitFail = false; try { diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/DomainPinningPolicyTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/DomainPinningPolicyTest.java index 272520c..adf31ac 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/DomainPinningPolicyTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/DomainPinningPolicyTest.java @@ -1,41 +1,39 @@ package com.datatheorem.android.trustkit.config; - -import org.junit.Test; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; import java.net.MalformedURLException; import java.net.URL; import java.util.Date; import java.util.HashSet; import java.util.Set; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; - +import org.junit.Test; public class DomainPinningPolicyTest { - private final static Set pins = new HashSet<>(); + private static final Set pins = new HashSet<>(); + static { pins.add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); pins.add("rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE="); } - private final static Set reportUris = new HashSet<>(); + private static final Set reportUris = new HashSet<>(); + static { reportUris.add("https://www.test.com"); reportUris.add("https://www.test2.com"); } - private final static Date date = new Date(); + private static final Date date = new Date(); @Test public void testValidPolicy() throws MalformedURLException { // Given a valid policy for a domain // When parsing it, it succeeds - DomainPinningPolicy policy = new DomainPinningPolicy( - "www.test.com", true, pins, true, date, reportUris, false - ); + DomainPinningPolicy policy = + new DomainPinningPolicy("www.test.com", true, pins, true, date, reportUris, false); // And the right configuration was saved assertEquals("www.test.com", policy.getHostname()); assertEquals(date, policy.getExpirationDate()); @@ -46,7 +44,6 @@ public void testValidPolicy() throws MalformedURLException { Set expectedPins = new HashSet<>(); for (String pinStr : pins) { expectedPins.add(new PublicKeyPin(pinStr)); - } assertEquals(expectedPins, policy.getPublicKeyPins()); @@ -65,9 +62,9 @@ public void testValidPolicyInternationalizeHostname() throws MalformedURLExcepti String internationalDomain = "českárepublika.icom.museum"; // When parsing it, it succeeds - DomainPinningPolicy policy = new DomainPinningPolicy( - internationalDomain, true, pins, true, date, reportUris, false - ); + DomainPinningPolicy policy = + new DomainPinningPolicy( + internationalDomain, true, pins, true, date, reportUris, false); assertEquals(policy.getHostname(), internationalDomain); assertEquals(policy.getHostname(), "českárepublika.icom.museum"); } @@ -92,7 +89,6 @@ public void testBadPolicyOnlyOnePin() throws MalformedURLException { assertTrue(didReceiveConfigError); } - @Test public void testNoPinsButPinningEnforceDisabledShouldBeValid() throws MalformedURLException { // Given a bad policy for a domain that has one pins at all @@ -121,8 +117,7 @@ public void testBadPolicyPinTld() throws MalformedURLException { boolean didReceiveConfigError = false; try { new DomainPinningPolicy(badDomain, true, pins, true, date, reportUris, false); - } - catch (ConfigurationException e) { + } catch (ConfigurationException e) { if (e.getMessage().startsWith("Tried to pin an invalid domain")) { didReceiveConfigError = true; } else { diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/PublicKeyPinTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/PublicKeyPinTest.java index ae09e61..ea69ee5 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/PublicKeyPinTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/PublicKeyPinTest.java @@ -1,39 +1,35 @@ package com.datatheorem.android.trustkit.config; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; import com.datatheorem.android.trustkit.CertificateUtils; - -import org.junit.Test; - import java.security.cert.Certificate; import java.security.cert.CertificateException; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; - +import org.junit.Test; public class PublicKeyPinTest { @Test public void testFromCertificate() throws CertificateException { String pemCertificate = - "MIIDGTCCAgGgAwIBAgIJAI1jD1qixIPLMA0GCSqGSIb3DQEBBQUAMCMxITAfBgNV\n" + - "BAMMGGV2aWxjZXJ0LmRhdGF0aGVvcmVtLmNvbTAeFw0xNTEyMjAxMzU4NDNaFw0y\n" + - "NTEyMTcxMzU4NDNaMCMxITAfBgNVBAMMGGV2aWxjZXJ0LmRhdGF0aGVvcmVtLmNv\n" + - "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdltqsRJtO7Nqypkehh\n" + - "4DSEirp9RM+hJXkBE9nRleTO+utV/snWqX/0wsUrz0wgWyPnAHybGOOXvkrWfXSt\n" + - "c2/8PyONOeFEU/9S/lWBXGZkaPhgTvkEzPmOOhf06rBMTwXUMGNDI45gKFgkO6Br\n" + - "bGPeSCuheQj0TKeWdwwNoJ+kczUE06IKu2tcuFRjHXci6VeHjANJzrfKro4ivIRy\n" + - "bewOGJj1onnpKbui/EOytsmW9MPpOSEXMoVksHOKBQ9nhpL6cDODRvG+t8u7qfFt\n" + - "mhphemK3IYNMNA4MMXpbJ+Au2hnPApZPEOit34bAwOiGi/batcS3iA+nl06dPYA9\n" + - "nPkCAwEAAaNQME4wHQYDVR0OBBYEFANxdSXS1JSvjdNtNbYBbRlgii93MB8GA1Ud\n" + - "IwQYMBaAFANxdSXS1JSvjdNtNbYBbRlgii93MAwGA1UdEwQFMAMBAf8wDQYJKoZI\n" + - "hvcNAQEFBQADggEBAAM78Bt2aLUgl2Yq4KMIGDeHdWYcRB7QPQ8sp3Q1TOQQzw0i\n" + - "AukRccl9iYNLgaSJDvlVMapD76jo3okydoWgDogWJhtZpMU/9xegIpukmu5hvF6i\n" + - "NpqE99PFO5E8BpMkNz+2nskwu//D0as6P9F3tA/o3jC6n6fWX0gt/e9th2ZgVwNQ\n" + - "9JTH1ZcyFbX9hdBI4xPAtzFX51AsSa8dpRdG+8DmI41Q/1ludoMZboExHldlUbQH\n" + - "zUuHKF8/T+aNo/9FfpqDz1fFnuoF7tuwyRh73B0YDyDVTNuq7LJ4tmzpVvqIt2tn\n" + - "RJnQoL4pLQ40SQsoUi4FYG/gxJMoQX6ROWe2nyg="; + "MIIDGTCCAgGgAwIBAgIJAI1jD1qixIPLMA0GCSqGSIb3DQEBBQUAMCMxITAfBgNV\n" + + "BAMMGGV2aWxjZXJ0LmRhdGF0aGVvcmVtLmNvbTAeFw0xNTEyMjAxMzU4NDNaFw0y\n" + + "NTEyMTcxMzU4NDNaMCMxITAfBgNVBAMMGGV2aWxjZXJ0LmRhdGF0aGVvcmVtLmNv\n" + + "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdltqsRJtO7Nqypkehh\n" + + "4DSEirp9RM+hJXkBE9nRleTO+utV/snWqX/0wsUrz0wgWyPnAHybGOOXvkrWfXSt\n" + + "c2/8PyONOeFEU/9S/lWBXGZkaPhgTvkEzPmOOhf06rBMTwXUMGNDI45gKFgkO6Br\n" + + "bGPeSCuheQj0TKeWdwwNoJ+kczUE06IKu2tcuFRjHXci6VeHjANJzrfKro4ivIRy\n" + + "bewOGJj1onnpKbui/EOytsmW9MPpOSEXMoVksHOKBQ9nhpL6cDODRvG+t8u7qfFt\n" + + "mhphemK3IYNMNA4MMXpbJ+Au2hnPApZPEOit34bAwOiGi/batcS3iA+nl06dPYA9\n" + + "nPkCAwEAAaNQME4wHQYDVR0OBBYEFANxdSXS1JSvjdNtNbYBbRlgii93MB8GA1Ud\n" + + "IwQYMBaAFANxdSXS1JSvjdNtNbYBbRlgii93MAwGA1UdEwQFMAMBAf8wDQYJKoZI\n" + + "hvcNAQEFBQADggEBAAM78Bt2aLUgl2Yq4KMIGDeHdWYcRB7QPQ8sp3Q1TOQQzw0i\n" + + "AukRccl9iYNLgaSJDvlVMapD76jo3okydoWgDogWJhtZpMU/9xegIpukmu5hvF6i\n" + + "NpqE99PFO5E8BpMkNz+2nskwu//D0as6P9F3tA/o3jC6n6fWX0gt/e9th2ZgVwNQ\n" + + "9JTH1ZcyFbX9hdBI4xPAtzFX51AsSa8dpRdG+8DmI41Q/1ludoMZboExHldlUbQH\n" + + "zUuHKF8/T+aNo/9FfpqDz1fFnuoF7tuwyRh73B0YDyDVTNuq7LJ4tmzpVvqIt2tn\n" + + "RJnQoL4pLQ40SQsoUi4FYG/gxJMoQX6ROWe2nyg="; Certificate cert = CertificateUtils.certificateFromPem(pemCertificate); PublicKeyPin pin = new PublicKeyPin(cert); assertEquals("Ckvh+UFO2eHunqaB2w0jsrwrJJQcSoES+p9FUhVoszQ=", pin.toString()); @@ -41,9 +37,8 @@ public void testFromCertificate() throws CertificateException { @Test public void testFromString() { - PublicKeyPin pin = - new PublicKeyPin("rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE="); - assertEquals(pin.toString(),"rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE="); + PublicKeyPin pin = new PublicKeyPin("rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE="); + assertEquals(pin.toString(), "rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE="); } @Test @@ -54,8 +49,7 @@ public void testFromBadStringNotBase64() { } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("bad base-64")) { didReturnError = true; - } - else { + } else { throw e; } } @@ -70,8 +64,7 @@ public void testFromBadStringBadLength() { } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("Invalid pin")) { didReturnError = true; - } - else { + } else { throw e; } } diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/TestableTrustKitConfiguration.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/TestableTrustKitConfiguration.java index 7043e08..62308ea 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/TestableTrustKitConfiguration.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/TestableTrustKitConfiguration.java @@ -2,7 +2,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import java.security.cert.Certificate; import java.util.Set; @@ -11,9 +10,10 @@ public TestableTrustKitConfiguration(@NonNull Set domainCon super(domainConfigSet); } - public TestableTrustKitConfiguration(@NonNull Set domainConfigSet, - boolean shouldOverridePins, - @Nullable Set debugCaCerts) { + public TestableTrustKitConfiguration( + @NonNull Set domainConfigSet, + boolean shouldOverridePins, + @Nullable Set debugCaCerts) { super(domainConfigSet, shouldOverridePins, debugCaCerts); } } diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/TrustKitConfigurationTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/TrustKitConfigurationTest.java index d14c9ec..ceab4ae 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/TrustKitConfigurationTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/config/TrustKitConfigurationTest.java @@ -1,14 +1,13 @@ package com.datatheorem.android.trustkit.config; -import android.content.Context; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import android.content.Context; import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Test; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - import java.io.IOException; import java.io.InputStream; import java.io.StringReader; @@ -21,12 +20,10 @@ import java.util.Date; import java.util.HashSet; import java.util.Locale; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; public class TrustKitConfigurationTest { @@ -35,27 +32,29 @@ private XmlPullParser parseXmlString(String xmlString) throws XmlPullParserExcep factory.setNamespaceAware(true); XmlPullParser xpp = factory.newPullParser(); - String test = xmlString.replace("\n","").replace(" ",""); + String test = xmlString.replace("\n", "").replace(" ", ""); xpp.setInput(new StringReader(test)); return xpp; } @Test - public void testBadHostnameValidation() throws XmlPullParserException, IOException, CertificateException { + public void testBadHostnameValidation() + throws XmlPullParserException, IOException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String xml = "" + - "\n" + - "\n" + - " \n" + - " www.datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + - " \n" + - " \n" + - ""; - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy(context, - parseXmlString(xml)); + String xml = + "" + + "\n" + + "\n" + + " \n" + + " www.datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + + " \n" + + " \n" + + ""; + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); // Ensure that something that isn't a domain (such as a URL) gets rejected boolean wasBadDomainRejected = false; @@ -68,25 +67,26 @@ public void testBadHostnameValidation() throws XmlPullParserException, IOExcepti } @Test - public void testDefaultValues() throws XmlPullParserException, IOException, ParseException, - CertificateException { + public void testDefaultValues() + throws XmlPullParserException, IOException, ParseException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String xml = "" + - "\n" + - "\n" + - " \n" + - " www.datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + - " \n" + - " \n" + - " https://some.reportdomain.com/\n" + - " \n" + - " \n" + - ""; - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy(context, - parseXmlString(xml)); + String xml = + "" + + "\n" + + "\n" + + " \n" + + " www.datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + + " \n" + + " \n" + + " https://some.reportdomain.com/\n" + + " \n" + + " \n" + + ""; + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); // Validate the domain's configuration DomainPinningPolicy domainConfig = config.getPolicyForHostname("www.datatheorem.com"); @@ -97,37 +97,44 @@ public void testDefaultValues() throws XmlPullParserException, IOException, Pars assertFalse(domainConfig.shouldIncludeSubdomains()); assertFalse(domainConfig.shouldEnforcePinning()); - HashSet expectedUri = new HashSet() {{ - add(new java.net.URL("https://some.reportdomain.com/")); - // The default report URI should be there too - add(new java.net.URL("https://overmind.datatheorem.com/trustkit/report")); - }}; + HashSet expectedUri = + new HashSet() { + { + add(new java.net.URL("https://some.reportdomain.com/")); + // The default report URI should be there too + add(new java.net.URL("https://overmind.datatheorem.com/trustkit/report")); + } + }; assertEquals(expectedUri, domainConfig.getReportUris()); - HashSet expectedPins = new HashSet() {{ - add(new PublicKeyPin("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")); - add(new PublicKeyPin("grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")); - }}; + HashSet expectedPins = + new HashSet() { + { + add(new PublicKeyPin("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")); + add(new PublicKeyPin("grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")); + } + }; assertEquals(expectedPins, domainConfig.getPublicKeyPins()); } @Test - public void testIncludeSubdomainsAndNoTrustkitTag() throws XmlPullParserException, IOException, - ParseException, CertificateException { + public void testIncludeSubdomainsAndNoTrustkitTag() + throws XmlPullParserException, IOException, ParseException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String xml = "" + - "\n" + - "\n" + - " \n" + - " datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + - " \n" + - " \n" + - ""; - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy(context, - parseXmlString(xml)); + String xml = + "" + + "\n" + + "\n" + + " \n" + + " datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + + " \n" + + " \n" + + ""; + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); // Ensure a valid subdomain gets the policy DomainPinningPolicy domainConfig = config.getPolicyForHostname("subdomain.datatheorem.com"); @@ -146,48 +153,49 @@ public void testIncludeSubdomainsAndNoTrustkitTag() throws XmlPullParserExceptio } @Test - public void testEnforcePinning() throws XmlPullParserException, IOException, - ParseException, CertificateException { + public void testEnforcePinning() + throws XmlPullParserException, IOException, ParseException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String xml = "" + - "\n" + - "\n" + - " \n" + - " www.datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + - " \n" + - " \n" + - " \n" + - " \n" + - ""; - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy(context, - parseXmlString(xml)); + String xml = + "" + + "\n" + + "\n" + + " \n" + + " www.datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); DomainPinningPolicy domainConfig = config.getPolicyForHostname("www.datatheorem.com"); assertNotNull(domainConfig); assertTrue(domainConfig.shouldEnforcePinning()); } - @Test - public void testExpirationDate() throws XmlPullParserException, IOException, - ParseException, CertificateException { + public void testExpirationDate() + throws XmlPullParserException, IOException, ParseException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String xml = "" + - "\n" + - "\n" + - " \n" + - " www.datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + - " \n" + - " \n" + - ""; - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy(context, - parseXmlString(xml)); + String xml = + "" + + "\n" + + "\n" + + " \n" + + " www.datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + + " \n" + + " \n" + + ""; + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd", Locale.US); Date expectedDate = parser.parse("2018-01-01"); @@ -197,24 +205,25 @@ public void testExpirationDate() throws XmlPullParserException, IOException, } @Test - public void testDisableDefaultReportUri() throws XmlPullParserException, IOException, - ParseException, CertificateException { + public void testDisableDefaultReportUri() + throws XmlPullParserException, IOException, ParseException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String xml = "" + - "\n" + - "\n" + - " \n" + - " www.datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + - " \n" + - " \n" + - " \n" + - " \n" + - ""; - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy(context, - parseXmlString(xml)); + String xml = + "" + + "\n" + + "\n" + + " \n" + + " www.datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + + " \n" + + " \n" + + " \n" + + " \n" + + ""; + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); // Ensure the list of report URIs is empty DomainPinningPolicy domainConfig = config.getPolicyForHostname("www.datatheorem.com"); @@ -223,30 +232,32 @@ public void testDisableDefaultReportUri() throws XmlPullParserException, IOExcep } @Test - public void testDebugOverrides() throws XmlPullParserException, IOException, - ParseException, CertificateException { + public void testDebugOverrides() + throws XmlPullParserException, IOException, ParseException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String xml = "" + - "\n" + - "\n" + - " \n" + - " www.datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - // We ignore src=sytem or user - " \n" + - " \n" + - " \n" + - ""; - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy(context, - parseXmlString(xml)); + String xml = + "" + + "\n" + + "\n" + + " \n" + + " www.datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + + // We ignore src=sytem or user + " \n" + + " \n" + + " \n" + + ""; + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); // Validate the debug overrides configuration int goodCertResId = @@ -262,59 +273,68 @@ public void testDebugOverrides() throws XmlPullParserException, IOException, CertificateFactory.getInstance("X.509").generateCertificate(caCertStream); assertTrue(config.shouldOverridePins()); - HashSet expectedCertificates = new HashSet() {{ - add(goodCert); - add(caCert); - }}; + HashSet expectedCertificates = + new HashSet() { + { + add(goodCert); + add(caCert); + } + }; assertEquals(expectedCertificates, config.getDebugCaCertificates()); } @Test - public void testNestedDomainConfig() throws XmlPullParserException, IOException, - ParseException, CertificateException { + public void testNestedDomainConfig() + throws XmlPullParserException, IOException, ParseException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String xml = "" + - "\n" + - "\n" + - " \n" + - // A more specific domain-config for a subdomain is nested here - " \n" + - " other.datatheorem.com\n" + - " \n" + - " CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=\n" + - " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\n" + - " \n" + - " \n" + - " \n" + - // A more specific domain-config for an unrelated domain is nested here - " \n" + - " unrelated.domain.com\n" + - " \n" + - " https://some.reportdomain.com/\n" + - " \n" + - " \n" + - " \n" + - ""; - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy(context, - parseXmlString(xml)); + String xml = + "" + + "\n" + + "\n" + + " \n" + + + // A more specific domain-config for a subdomain is nested here + " \n" + + " other.datatheorem.com\n" + + " \n" + + " CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=\n" + + " DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\n" + + " \n" + + " \n" + + " \n" + + + // A more specific domain-config for an unrelated domain is nested here + " \n" + + " unrelated.domain.com\n" + + " \n" + + " https://some.reportdomain.com/\n" + + " \n" + + " \n" + + " \n" + + ""; + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); // Validate the configuration of the parent domain-config DomainPinningPolicy domainConfig = config.getPolicyForHostname("datatheorem.com"); assertNotNull(domainConfig); assertEquals(new HashSet<>(), domainConfig.getReportUris()); - HashSet expectedPins = new HashSet() {{ - add(new PublicKeyPin("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")); - add(new PublicKeyPin("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")); - }}; + HashSet expectedPins = + new HashSet() { + { + add(new PublicKeyPin("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")); + add(new PublicKeyPin("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")); + } + }; assertEquals(expectedPins, domainConfig.getPublicKeyPins()); // Validate the configuration of the parent domain-config for a subdomain @@ -326,17 +346,23 @@ public void testNestedDomainConfig() throws XmlPullParserException, IOException, // Validate the configuration of a nested domain-config for a subdomain domainConfig = config.getPolicyForHostname("other.datatheorem.com"); - HashSet expectedOtherPins = new HashSet() {{ - add(new PublicKeyPin("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=")); - add(new PublicKeyPin("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=")); - }}; + HashSet expectedOtherPins = + new HashSet() { + { + add(new PublicKeyPin("CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=")); + add(new PublicKeyPin("DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=")); + } + }; assertNotNull(domainConfig); assertEquals(expectedOtherPins, domainConfig.getPublicKeyPins()); - HashSet expectedUri = new HashSet() {{ - // The default report URI should be there - add(new java.net.URL("https://overmind.datatheorem.com/trustkit/report")); - }}; + HashSet expectedUri = + new HashSet() { + { + // The default report URI should be there + add(new java.net.URL("https://overmind.datatheorem.com/trustkit/report")); + } + }; assertEquals(expectedUri, domainConfig.getReportUris()); // Validate the configuration of a nested domain-config for an unrelated domain @@ -344,40 +370,44 @@ public void testNestedDomainConfig() throws XmlPullParserException, IOException, assertNotNull(domainConfig); assertEquals(expectedPins, domainConfig.getPublicKeyPins()); - HashSet expectedUnrelatedUri = new HashSet() {{ - // The default report URI should be there - add(new java.net.URL("https://some.reportdomain.com/")); - }}; + HashSet expectedUnrelatedUri = + new HashSet() { + { + // The default report URI should be there + add(new java.net.URL("https://some.reportdomain.com/")); + } + }; assertEquals(expectedUnrelatedUri, domainConfig.getReportUris()); } @Test - public void testIgnoreDomainWithNoPins( - ) throws XmlPullParserException, IOException, CertificateException { + public void testIgnoreDomainWithNoPins() + throws XmlPullParserException, IOException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); // Given a valid network security config - String xml = "" + - "\n" + - "\n" + - " \n" + - " www.datatheorem.com\n" + - " \n" + - " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + - " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + - " \n" + - " \n" + - - // That has a domain-config entry with no pin-set - " \n" + - " localhost\n" + - " 10.0.2.2\n" + - " \n" + - ""; + String xml = + "" + + "\n" + + "\n" + + " \n" + + " www.datatheorem.com\n" + + " \n" + + " AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n" + + " grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=\n" + + " \n" + + " \n" + + + + // That has a domain-config entry with no pin-set + " \n" + + " localhost\n" + + " 10.0.2.2\n" + + " \n" + + ""; // When parsing the config - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy( - context, parseXmlString(xml) - ); + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); // It succeeds DomainPinningPolicy datathDomainConfig = config.getPolicyForHostname("www.datatheorem.com"); @@ -389,23 +419,23 @@ context, parseXmlString(xml) } @Test - public void testAllowsEmptyPinningConfig( - ) throws XmlPullParserException, IOException, CertificateException { + public void testAllowsEmptyPinningConfig() + throws XmlPullParserException, IOException, CertificateException { Context context = InstrumentationRegistry.getInstrumentation().getContext(); // Given a valid network security config that has no entries related to pinning - String xml = "" + - "\n" + - "\n" + - " \n" + - " localhost\n" + - " 10.0.2.2\n" + - " \n" + - ""; + String xml = + "" + + "\n" + + "\n" + + " \n" + + " localhost\n" + + " 10.0.2.2\n" + + " \n" + + ""; // When parsing the config - TrustKitConfiguration config = TrustKitConfiguration.fromXmlPolicy( - context, parseXmlString(xml) - ); + TrustKitConfiguration config = + TrustKitConfiguration.fromXmlPolicy(context, parseXmlString(xml)); // It succeeds and no domains have any pinning config DomainPinningPolicy datathDomainConfig = config.getPolicyForHostname("www.datatheorem.com"); diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/pinning/SSLSocketFactoryTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/pinning/SSLSocketFactoryTest.java index 5d790ac..b71b67e 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/pinning/SSLSocketFactoryTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/pinning/SSLSocketFactoryTest.java @@ -1,10 +1,16 @@ package com.datatheorem.android.trustkit.pinning; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + import android.content.Context; import android.os.Build; - import androidx.test.platform.app.InstrumentationRegistry; - import com.datatheorem.android.trustkit.CertificateUtils; import com.datatheorem.android.trustkit.TestableTrustKit; import com.datatheorem.android.trustkit.config.DomainPinningPolicy; @@ -12,13 +18,6 @@ import com.google.android.gms.common.GooglePlayServicesNotAvailableException; import com.google.android.gms.common.GooglePlayServicesRepairableException; import com.google.android.gms.security.ProviderInstaller; - -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - import java.io.IOException; import java.net.Socket; import java.security.cert.Certificate; @@ -26,77 +25,71 @@ import java.security.cert.X509Certificate; import java.util.HashSet; import java.util.List; - import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSocketFactory; - -import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; - +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; /** * Tests TrustKit's SSLSocketFactory. - *

- * The general testing strategy used here is to connect to live websites. This provides a variety of - * valid certificate chains that can then have different pins applied to each. This requires no + * + *

The general testing strategy used here is to connect to live websites. This provides a variety + * of valid certificate chains that can then have different pins applied to each. This requires no * special mock servers or mock CA setup, but it is dependent on the domains being live and having * valid certificate chains. */ @SuppressWarnings("unchecked") public class SSLSocketFactoryTest { - @Mock - private BackgroundReporter mockReporter; + @Mock private BackgroundReporter mockReporter; // The root CA for cacert.org; useful to test connections with a custom CA private final String caCertDotOrgRootPem = - "MIIHbDCCBVSgAwIBAgIDAsGhMA0GCSqGSIb3DQEBDQUAMFQxFDASBgNVBAoTC0NB\n" + - "Y2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5vcmcxHDAaBgNV\n" + - "BAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwHhcNMTgwNDA1MTk0MjQxWhcNMjAwNDA0\n" + - "MTk0MjQxWjBbMQswCQYDVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZT\n" + - "eWRuZXkxFDASBgNVBAoTC0NBY2VydCBJbmMuMRcwFQYDVQQDEw53d3cuY2FjZXJ0\n" + - "Lm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANwriThHegmvvYFB\n" + - "2X281mJ5d+F2AEEZwaBSSSWoq75BYRJ5l5ke8QHGcx3c8CZDPlPjopyYCIy8LRhA\n" + - "75IfVhRnR5imikVG4Gsvp57vAzwrxBtiAh8IqZKSlok30IaZ062G7uPNXaxwNZGY\n" + - "c4CcAD2MRmTAxBbVan+wa+h/NTwTa/OfZwjaVdU4mDFJpegGl6tqm10+AdZW7bvP\n" + - "Hbg5GPnn8WON0UzR5avrGDkU8013ruFH/Y0G/FlqnAsFAkf20rFYDLRLXzb29Olh\n" + - "f6arkF+HOrsnanfyqjwyv5sgvZva3iXmEo0a7NhK2dGM1pO9Pd2AqkvjGARMI0ud\n" + - "WrQkDThvoGEV2BvgBqQpF8WYBhlxMr7ToG4y2Dxc+wXgXSy6zPIgZqVwq9OZ4qit\n" + - "TeXIiwWQp6nAYlJcPWuDNX2EoTi0FUKn2xCzbDr+i2ZtfZ6NYytxUq+ZwSOZ/o18\n" + - "AXnMk82YO95WUFzFbTXrYKF6Sae8caHO92ptjl2tVxLPPRzsIDBMEh2/97fp1jxO\n" + - "RjgwWMnBISwznbgIlG9/lY7/DaPHCYlAnIfsqvAasH3SRm5XedmGW4kyOD7D1Cpo\n" + - "6vTSk4gs3MyaNvGt9wYATuunqwRjJVX83L/JfrDfxZ8CCb1s+JyYgTPMpbtyvZbN\n" + - "1DHYLVfpFL5Nwtx3sZzuMteflQ7NAgMBAAGjggI+MIICOjAMBgNVHRMBAf8EAjAA\n" + - "MA4GA1UdDwEB/wQEAwIDqDA0BgNVHSUELTArBggrBgEFBQcDAgYIKwYBBQUHAwEG\n" + - "CWCGSAGG+EIEAQYKKwYBBAGCNwoDAzAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUH\n" + - "MAGGF2h0dHA6Ly9vY3NwLmNhY2VydC5vcmcvMDgGA1UdHwQxMC8wLaAroCmGJ2h0\n" + - "dHA6Ly9jcmwuY2FjZXJ0Lm9yZy9jbGFzczMtcmV2b2tlLmNybDCCAXMGA1UdEQSC\n" + - "AWowggFmgg53d3cuY2FjZXJ0Lm9yZ6AcBggrBgEFBQcIBaAQDA53d3cuY2FjZXJ0\n" + - "Lm9yZ4IRc2VjdXJlLmNhY2VydC5vcmegHwYIKwYBBQUHCAWgEwwRc2VjdXJlLmNh\n" + - "Y2VydC5vcmeCEnd3d21haWwuY2FjZXJ0Lm9yZ6AgBggrBgEFBQcIBaAUDBJ3d3dt\n" + - "YWlsLmNhY2VydC5vcmeCCmNhY2VydC5vcmegGAYIKwYBBQUHCAWgDAwKY2FjZXJ0\n" + - "Lm9yZ4IOd3d3LmNhY2VydC5uZXSgHAYIKwYBBQUHCAWgEAwOd3d3LmNhY2VydC5u\n" + - "ZXSCCmNhY2VydC5uZXSgGAYIKwYBBQUHCAWgDAwKY2FjZXJ0Lm5ldIIOd3d3LmNh\n" + - "Y2VydC5jb22gHAYIKwYBBQUHCAWgEAwOd3d3LmNhY2VydC5jb22CCmNhY2VydC5j\n" + - "b22gGAYIKwYBBQUHCAWgDAwKY2FjZXJ0LmNvbTANBgkqhkiG9w0BAQ0FAAOCAgEA\n" + - "pEFsiLHeLxNrP12BIG1QqZja9i1IrBCnWyVvlDmbUMdVHcscAQhWE5sTYkAD+1D7\n" + - "VAodoYXo23paZrDKgKoFgZMNLMQ4m93WlCLrInEfENjCxNaPWI5LmsajeZR/5T7C\n" + - "5nUqYklCY+3Bc6SBGHXIRDVnGw9AhWgI9f3hSpQhECyokbLwZ17aIGmznTeKx7lV\n" + - "DYwaBeyFjZ/AIqovRSkcPTMf1L8LT/SZXuc1urgETbBa+F4tSMGjdGJg2jayojs0\n" + - "kD2EFZVGdKYUzOH/rNoQmnTyDEudswp+nim7jgfugztl5KbKeowDFN9KpeineJUW\n" + - "lthzARWpWr2gkIH8mGmgvOsIngYGof1sJMJsxcgdrowTrSPW6W/lOWRc6nSGnjg0\n" + - "gnshQg3gDN902Kps0OBwbTCrbC4sYu3Xywk0QVxYtcDF2asnsERuSFaZuLWUf2WS\n" + - "JYRDGbMuyw6MY+Uoukbee9fJ5Yq77+N0ZeeHRXvRG+PIVyl5KbujznNo6pCGeb3d\n" + - "atDvi507zOiRJAWwHTXOEqpJ71ZjuV7XyRTvFe+qb70+t7FiohcZJZhE1hFZrIeV\n" + - "iZX2MyJJPaWx2fjx8u/FpaKo01OYNrVOCcnhXzd5jUs+99zxrUVh1CEgC8KIN2zF\n" + - "VgnYWDmu4r7D5JbJwxqccicVF9oUa+4HGHvcHdAwfRg="; - private final Certificate caCertDotOrgRoot - = CertificateUtils.certificateFromPem(caCertDotOrgRootPem); + "MIIHbDCCBVSgAwIBAgIDAsGhMA0GCSqGSIb3DQEBDQUAMFQxFDASBgNVBAoTC0NB\n" + + "Y2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5vcmcxHDAaBgNV\n" + + "BAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwHhcNMTgwNDA1MTk0MjQxWhcNMjAwNDA0\n" + + "MTk0MjQxWjBbMQswCQYDVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZT\n" + + "eWRuZXkxFDASBgNVBAoTC0NBY2VydCBJbmMuMRcwFQYDVQQDEw53d3cuY2FjZXJ0\n" + + "Lm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANwriThHegmvvYFB\n" + + "2X281mJ5d+F2AEEZwaBSSSWoq75BYRJ5l5ke8QHGcx3c8CZDPlPjopyYCIy8LRhA\n" + + "75IfVhRnR5imikVG4Gsvp57vAzwrxBtiAh8IqZKSlok30IaZ062G7uPNXaxwNZGY\n" + + "c4CcAD2MRmTAxBbVan+wa+h/NTwTa/OfZwjaVdU4mDFJpegGl6tqm10+AdZW7bvP\n" + + "Hbg5GPnn8WON0UzR5avrGDkU8013ruFH/Y0G/FlqnAsFAkf20rFYDLRLXzb29Olh\n" + + "f6arkF+HOrsnanfyqjwyv5sgvZva3iXmEo0a7NhK2dGM1pO9Pd2AqkvjGARMI0ud\n" + + "WrQkDThvoGEV2BvgBqQpF8WYBhlxMr7ToG4y2Dxc+wXgXSy6zPIgZqVwq9OZ4qit\n" + + "TeXIiwWQp6nAYlJcPWuDNX2EoTi0FUKn2xCzbDr+i2ZtfZ6NYytxUq+ZwSOZ/o18\n" + + "AXnMk82YO95WUFzFbTXrYKF6Sae8caHO92ptjl2tVxLPPRzsIDBMEh2/97fp1jxO\n" + + "RjgwWMnBISwznbgIlG9/lY7/DaPHCYlAnIfsqvAasH3SRm5XedmGW4kyOD7D1Cpo\n" + + "6vTSk4gs3MyaNvGt9wYATuunqwRjJVX83L/JfrDfxZ8CCb1s+JyYgTPMpbtyvZbN\n" + + "1DHYLVfpFL5Nwtx3sZzuMteflQ7NAgMBAAGjggI+MIICOjAMBgNVHRMBAf8EAjAA\n" + + "MA4GA1UdDwEB/wQEAwIDqDA0BgNVHSUELTArBggrBgEFBQcDAgYIKwYBBQUHAwEG\n" + + "CWCGSAGG+EIEAQYKKwYBBAGCNwoDAzAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUH\n" + + "MAGGF2h0dHA6Ly9vY3NwLmNhY2VydC5vcmcvMDgGA1UdHwQxMC8wLaAroCmGJ2h0\n" + + "dHA6Ly9jcmwuY2FjZXJ0Lm9yZy9jbGFzczMtcmV2b2tlLmNybDCCAXMGA1UdEQSC\n" + + "AWowggFmgg53d3cuY2FjZXJ0Lm9yZ6AcBggrBgEFBQcIBaAQDA53d3cuY2FjZXJ0\n" + + "Lm9yZ4IRc2VjdXJlLmNhY2VydC5vcmegHwYIKwYBBQUHCAWgEwwRc2VjdXJlLmNh\n" + + "Y2VydC5vcmeCEnd3d21haWwuY2FjZXJ0Lm9yZ6AgBggrBgEFBQcIBaAUDBJ3d3dt\n" + + "YWlsLmNhY2VydC5vcmeCCmNhY2VydC5vcmegGAYIKwYBBQUHCAWgDAwKY2FjZXJ0\n" + + "Lm9yZ4IOd3d3LmNhY2VydC5uZXSgHAYIKwYBBQUHCAWgEAwOd3d3LmNhY2VydC5u\n" + + "ZXSCCmNhY2VydC5uZXSgGAYIKwYBBQUHCAWgDAwKY2FjZXJ0Lm5ldIIOd3d3LmNh\n" + + "Y2VydC5jb22gHAYIKwYBBQUHCAWgEAwOd3d3LmNhY2VydC5jb22CCmNhY2VydC5j\n" + + "b22gGAYIKwYBBQUHCAWgDAwKY2FjZXJ0LmNvbTANBgkqhkiG9w0BAQ0FAAOCAgEA\n" + + "pEFsiLHeLxNrP12BIG1QqZja9i1IrBCnWyVvlDmbUMdVHcscAQhWE5sTYkAD+1D7\n" + + "VAodoYXo23paZrDKgKoFgZMNLMQ4m93WlCLrInEfENjCxNaPWI5LmsajeZR/5T7C\n" + + "5nUqYklCY+3Bc6SBGHXIRDVnGw9AhWgI9f3hSpQhECyokbLwZ17aIGmznTeKx7lV\n" + + "DYwaBeyFjZ/AIqovRSkcPTMf1L8LT/SZXuc1urgETbBa+F4tSMGjdGJg2jayojs0\n" + + "kD2EFZVGdKYUzOH/rNoQmnTyDEudswp+nim7jgfugztl5KbKeowDFN9KpeineJUW\n" + + "lthzARWpWr2gkIH8mGmgvOsIngYGof1sJMJsxcgdrowTrSPW6W/lOWRc6nSGnjg0\n" + + "gnshQg3gDN902Kps0OBwbTCrbC4sYu3Xywk0QVxYtcDF2asnsERuSFaZuLWUf2WS\n" + + "JYRDGbMuyw6MY+Uoukbee9fJ5Yq77+N0ZeeHRXvRG+PIVyl5KbujznNo6pCGeb3d\n" + + "atDvi507zOiRJAWwHTXOEqpJ71ZjuV7XyRTvFe+qb70+t7FiohcZJZhE1hFZrIeV\n" + + "iZX2MyJJPaWx2fjx8u/FpaKo01OYNrVOCcnhXzd5jUs+99zxrUVh1CEgC8KIN2zF\n" + + "VgnYWDmu4r7D5JbJwxqccicVF9oUa+4HGHvcHdAwfRg="; + private final Certificate caCertDotOrgRoot = + CertificateUtils.certificateFromPem(caCertDotOrgRootPem); @BeforeClass public static void runOnceBeforeClass() { @@ -104,7 +97,8 @@ public static void runOnceBeforeClass() { // in the SSLSocketFactory; otherwise some tests fail because the server requires TLS 1.2 if (Build.VERSION.SDK_INT < 20) { try { - ProviderInstaller.installIfNeeded(InstrumentationRegistry.getInstrumentation().getContext()); + ProviderInstaller.installIfNeeded( + InstrumentationRegistry.getInstrumentation().getContext()); } catch (GooglePlayServicesRepairableException e) { e.printStackTrace(); } catch (GooglePlayServicesNotAvailableException e) { @@ -119,7 +113,7 @@ public void setUp() { TestableTrustKit.reset(); } - //region Tests for when the domain is pinned + // region Tests for when the domain is pinned @Test public void testPinnedDomainExpiredChain() throws IOException { // Initialize TrustKit @@ -146,14 +140,17 @@ public void testPinnedDomainExpiredChain() throws IOException { } // Ensure the background reporter was called - verify(mockReporter).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED) - ); + verify(mockReporter) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED)); } @Test @@ -182,14 +179,17 @@ public void testPinnedDomainWrongHostnameChain() throws IOException { } // Ensure the background reporter was called - verify(mockReporter).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED) - ); + verify(mockReporter) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED)); } @Test @@ -207,14 +207,17 @@ public void testPinnedDomainSuccessAnchor() throws IOException { socket.close(); // Ensure the background reporter was NOT called - verify(mockReporter, never()).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED) - ); + verify(mockReporter, never()) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED)); } @Test @@ -232,14 +235,17 @@ public void testPinnedDomainSuccessLeaf() throws IOException { socket.close(); // Ensure the background reporter was NOT called - verify(mockReporter, never()).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED) - ); + verify(mockReporter, never()) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED)); } @Test @@ -267,14 +273,17 @@ public void testPinnedDomainInvalidPin() throws IOException { assertTrue(didReceivePinningError); // Ensure the background reporter was called - verify(mockReporter).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED) - ); + verify(mockReporter) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED)); } @Test @@ -297,14 +306,17 @@ public void testPinnedDomainInvalidPinAndPinningNotEnforced() throws IOException } // Ensure the background reporter was called - verify(mockReporter).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED) - ); + verify(mockReporter) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED)); } @Test @@ -322,14 +334,14 @@ public void testPinnedDomainInvalidPinAndPolicyExpired() throws IOException { socket.close(); // Ensure the background reporter was NOT called - verify(mockReporter, never()).pinValidationFailed( - anyString(), - anyInt(), - (List) any(), - (List) any(), - any(DomainPinningPolicy.class), - any(PinningValidationResult.class) - ); + verify(mockReporter, never()) + .pinValidationFailed( + anyString(), + anyInt(), + (List) any(), + (List) any(), + any(DomainPinningPolicy.class), + any(PinningValidationResult.class)); } @Test @@ -359,18 +371,22 @@ public void testPinnedDomainUntrustedChainAndPinningNotEnforced() throws IOExcep } // Ensure the background reporter was called - verify(mockReporter).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED) - ); + verify(mockReporter) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED)); } @Test - public void testDebugOverridesInvalidPinButOverridePins() throws IOException, CertificateException { + public void testDebugOverridesInvalidPinButOverridePins() + throws IOException, CertificateException { if (Build.VERSION.SDK_INT >= 24) { // This test will not work when using the Android N XML network policy because we can't // dynamically remove the debug-overrides tag defined in the XML policy which adds the @@ -383,20 +399,34 @@ public void testDebugOverridesInvalidPinButOverridePins() throws IOException, Ce } String serverHostname = "www.cacert.org"; - final DomainPinningPolicy domainPolicy = new DomainPinningPolicy.Builder() - .setHostname(serverHostname) - .setShouldEnforcePinning(true) - .setPublicKeyHashes(new HashSet() {{ - // Wrong pins - add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); - }}).build(); + final DomainPinningPolicy domainPolicy = + new DomainPinningPolicy.Builder() + .setHostname(serverHostname) + .setShouldEnforcePinning(true) + .setPublicKeyHashes( + new HashSet() { + { + // Wrong pins + add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); + add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); + } + }) + .build(); // Create a configuration with debug overrides enabled to add the cacert.org CA and to set // overridePins to true - TestableTrustKit.init(new HashSet() {{ add(domainPolicy); }}, + TestableTrustKit.init( + new HashSet() { + { + add(domainPolicy); + } + }, true, - new HashSet(){{ add(caCertDotOrgRoot); }}, + new HashSet() { + { + add(caCertDotOrgRoot); + } + }, InstrumentationRegistry.getInstrumentation().getContext(), mockReporter); @@ -411,14 +441,14 @@ public void testDebugOverridesInvalidPinButOverridePins() throws IOException, Ce socket.close(); // Ensure the background reporter was NOT called - verify(mockReporter, never()).pinValidationFailed( - anyString(), - anyInt(), - (List) any(), - (List) any(), - any(DomainPinningPolicy.class), - any(PinningValidationResult.class) - ); + verify(mockReporter, never()) + .pinValidationFailed( + anyString(), + anyInt(), + (List) any(), + (List) any(), + any(DomainPinningPolicy.class), + any(PinningValidationResult.class)); } @Test @@ -435,23 +465,37 @@ public void testDebugOverridesButAppNotDebuggable() throws IOException, Certific } String serverHostname = "www.cacert.org"; - final DomainPinningPolicy domainPolicy = new DomainPinningPolicy.Builder() - .setHostname(serverHostname) - .setShouldEnforcePinning(true) - .setPublicKeyHashes(new HashSet() {{ - // Wrong pins - add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); - }}).build(); + final DomainPinningPolicy domainPolicy = + new DomainPinningPolicy.Builder() + .setHostname(serverHostname) + .setShouldEnforcePinning(true) + .setPublicKeyHashes( + new HashSet() { + { + // Wrong pins + add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); + add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); + } + }) + .build(); // Create a configuration with debug overrides enabled to add the cacert.org CA but // make the App's debuggable flag disabled to mock a production App Context mockContext = InstrumentationRegistry.getInstrumentation().getContext(); int originalAppFlags = mockContext.getApplicationInfo().flags; mockContext.getApplicationInfo().flags = 0; - TestableTrustKit.init(new HashSet() {{ add(domainPolicy); }}, + TestableTrustKit.init( + new HashSet() { + { + add(domainPolicy); + } + }, true, - new HashSet(){{ add(caCertDotOrgRoot); }}, + new HashSet() { + { + add(caCertDotOrgRoot); + } + }, mockContext, mockReporter); mockContext.getApplicationInfo().flags = originalAppFlags; @@ -468,14 +512,17 @@ public void testDebugOverridesButAppNotDebuggable() throws IOException, Certific assertTrue(didReceiveHandshakeError); // Ensure the background reporter was called - verify(mockReporter).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED) - ); + verify(mockReporter) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED)); } @Test @@ -491,20 +538,34 @@ public void testDebugOverridesInvalidPin() throws IOException, CertificateExcept } String serverHostname = "www.cacert.org"; - final DomainPinningPolicy domainPolicy = new DomainPinningPolicy.Builder() - .setHostname(serverHostname) - .setShouldEnforcePinning(true) - .setPublicKeyHashes(new HashSet() {{ - // Wrong pins - add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); - }}).build(); + final DomainPinningPolicy domainPolicy = + new DomainPinningPolicy.Builder() + .setHostname(serverHostname) + .setShouldEnforcePinning(true) + .setPublicKeyHashes( + new HashSet() { + { + // Wrong pins + add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); + add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); + } + }) + .build(); // Create a configuration with debug overrides enabled to add the cacert.org CA and to set // overridePins to false, making the connection fail - TestableTrustKit.init(new HashSet() {{ add(domainPolicy); }}, + TestableTrustKit.init( + new HashSet() { + { + add(domainPolicy); + } + }, false, - new HashSet(){{ add(caCertDotOrgRoot); }}, + new HashSet() { + { + add(caCertDotOrgRoot); + } + }, InstrumentationRegistry.getInstrumentation().getContext(), mockReporter); @@ -524,31 +585,44 @@ public void testDebugOverridesInvalidPin() throws IOException, CertificateExcept assertTrue(didReceivePinningError); // Ensure the background reporter was called - verify(mockReporter).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED) - ); + verify(mockReporter) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED)); } - //endregion + // endregion - //region Tests for when the domain is NOT pinned + // region Tests for when the domain is NOT pinned @Test public void testNonPinnedDomainUntrustedRootChain() throws IOException { String serverHostname = "www.cacert.org"; - final DomainPinningPolicy domainPolicy = new DomainPinningPolicy.Builder() - .setHostname("other.domain.com") - .setShouldEnforcePinning(true) - .setPublicKeyHashes(new HashSet() {{ - // Wrong pins - add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); - }}).build(); - - TestableTrustKit.init(new HashSet() {{ add(domainPolicy); }}, + final DomainPinningPolicy domainPolicy = + new DomainPinningPolicy.Builder() + .setHostname("other.domain.com") + .setShouldEnforcePinning(true) + .setPublicKeyHashes( + new HashSet() { + { + // Wrong pins + add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); + add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); + } + }) + .build(); + + TestableTrustKit.init( + new HashSet() { + { + add(domainPolicy); + } + }, InstrumentationRegistry.getInstrumentation().getContext(), mockReporter); @@ -567,14 +641,17 @@ public void testNonPinnedDomainUntrustedRootChain() throws IOException { assertTrue(didReceiveHandshakeError); // Ensure the background reporter was NOT called as we only want reports for pinned domains - verify(mockReporter, never()).pinValidationFailed( - eq(serverHostname), - eq(0), - (List) org.mockito.Matchers.isNotNull(), - (List) org.mockito.Matchers.isNotNull(), - eq(TestableTrustKit.getInstance().getConfiguration().getPolicyForHostname(serverHostname)), - eq(PinningValidationResult.FAILED) - ); + verify(mockReporter, never()) + .pinValidationFailed( + eq(serverHostname), + eq(0), + (List) org.mockito.Matchers.isNotNull(), + (List) org.mockito.Matchers.isNotNull(), + eq( + TestableTrustKit.getInstance() + .getConfiguration() + .getPolicyForHostname(serverHostname)), + eq(PinningValidationResult.FAILED)); } @Test @@ -593,14 +670,14 @@ public void testNonPinnedDomainSuccess() throws IOException { socket.close(); // Ensure the background reporter was NOT called - verify(mockReporter, never()).pinValidationFailed( - anyString(), - anyInt(), - (List) any(), - (List) any(), - any(DomainPinningPolicy.class), - any(PinningValidationResult.class) - ); + verify(mockReporter, never()) + .pinValidationFailed( + anyString(), + anyInt(), + (List) any(), + (List) any(), + any(DomainPinningPolicy.class), + any(PinningValidationResult.class)); } @Test @@ -618,19 +695,33 @@ public void testDebugOverrides() throws IOException, CertificateException { String serverHostname = "www.cacert.org"; // Create a policy for a different domain - final DomainPinningPolicy domainPolicy = new DomainPinningPolicy.Builder() - .setHostname("other.domain.com") - .setShouldEnforcePinning(true) - .setPublicKeyHashes(new HashSet() {{ - // Wrong pins - add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); - }}).build(); + final DomainPinningPolicy domainPolicy = + new DomainPinningPolicy.Builder() + .setHostname("other.domain.com") + .setShouldEnforcePinning(true) + .setPublicKeyHashes( + new HashSet() { + { + // Wrong pins + add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); + add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); + } + }) + .build(); // Create a configuration with debug overrides enabled to add the cacert.org CA - TestableTrustKit.init(new HashSet() {{ add(domainPolicy); }}, + TestableTrustKit.init( + new HashSet() { + { + add(domainPolicy); + } + }, false, - new HashSet(){{ add(caCertDotOrgRoot); }}, + new HashSet() { + { + add(caCertDotOrgRoot); + } + }, InstrumentationRegistry.getInstrumentation().getContext(), mockReporter); @@ -644,14 +735,14 @@ public void testDebugOverrides() throws IOException, CertificateException { socket.close(); // Ensure the background reporter was NOT called - verify(mockReporter, never()).pinValidationFailed( - anyString(), - anyInt(), - (List) any(), - (List) any(), - any(DomainPinningPolicy.class), - any(PinningValidationResult.class) - ); + verify(mockReporter, never()) + .pinValidationFailed( + anyString(), + anyInt(), + (List) any(), + (List) any(), + any(DomainPinningPolicy.class), + any(PinningValidationResult.class)); } @Test @@ -665,19 +756,33 @@ public void testDebugOverridesSystemCa() throws IOException, CertificateExceptio String serverHostname = "www.google.com"; // Create a policy for a different domain - final DomainPinningPolicy domainPolicy = new DomainPinningPolicy.Builder() - .setHostname("other.domain.com") - .setShouldEnforcePinning(true) - .setPublicKeyHashes(new HashSet() {{ - // Wrong pins - add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); - }}).build(); + final DomainPinningPolicy domainPolicy = + new DomainPinningPolicy.Builder() + .setHostname("other.domain.com") + .setShouldEnforcePinning(true) + .setPublicKeyHashes( + new HashSet() { + { + // Wrong pins + add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); + add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); + } + }) + .build(); // Create a configuration with debug overrides enabled to add the cacert.org CA - TestableTrustKit.init(new HashSet() {{ add(domainPolicy); }}, + TestableTrustKit.init( + new HashSet() { + { + add(domainPolicy); + } + }, false, - new HashSet(){{ add(caCertDotOrgRoot); }}, + new HashSet() { + { + add(caCertDotOrgRoot); + } + }, InstrumentationRegistry.getInstrumentation().getContext(), mockReporter); @@ -691,14 +796,14 @@ public void testDebugOverridesSystemCa() throws IOException, CertificateExceptio socket.close(); // Ensure the background reporter was NOT called - verify(mockReporter, never()).pinValidationFailed( - anyString(), - anyInt(), - (List) any(), - (List) any(), - any(DomainPinningPolicy.class), - any(PinningValidationResult.class) - ); + verify(mockReporter, never()) + .pinValidationFailed( + anyString(), + anyInt(), + (List) any(), + (List) any(), + any(DomainPinningPolicy.class), + any(PinningValidationResult.class)); } - //endregion + // endregion } diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/pinning/TestableTrustManagerBuilder.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/pinning/TestableTrustManagerBuilder.java index 3f6f679..abea41e 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/pinning/TestableTrustManagerBuilder.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/pinning/TestableTrustManagerBuilder.java @@ -1,6 +1,5 @@ package com.datatheorem.android.trustkit.pinning; - import com.datatheorem.android.trustkit.reporting.BackgroundReporter; public class TestableTrustManagerBuilder extends TrustManagerBuilder { diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTaskTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTaskTest.java index 570cdfc..8c01c99 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTaskTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTaskTest.java @@ -1,39 +1,48 @@ package com.datatheorem.android.trustkit.reporting; -import android.os.Build; +import static com.datatheorem.android.trustkit.CertificateUtils.testCertChainPem; +import static junit.framework.Assert.assertEquals; +import android.os.Build; import androidx.test.platform.app.InstrumentationRegistry; - import com.datatheorem.android.trustkit.TestableTrustKit; import com.datatheorem.android.trustkit.config.PublicKeyPin; import com.datatheorem.android.trustkit.pinning.PinningValidationResult; import com.datatheorem.android.trustkit.utils.VendorIdentifier; - -import org.junit.Before; -import org.junit.Test; - import java.net.MalformedURLException; import java.net.URL; import java.sql.Date; import java.util.ArrayList; import java.util.HashSet; - -import static com.datatheorem.android.trustkit.CertificateUtils.testCertChainPem; -import static junit.framework.Assert.assertEquals; - +import org.junit.Before; +import org.junit.Test; public class BackgroundReporterTaskTest { - private final HashSet knownPins = new HashSet() {{ - add(new PublicKeyPin("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")); - add(new PublicKeyPin("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")); - }}; - - private final PinningFailureReport report = new PinningFailureReport("com.unit.test", "1.2", - VendorIdentifier.getOrCreate(InstrumentationRegistry.getInstrumentation().getContext()), - "www.datatheorem.com", 0, "datatheorem.com", true, true, - testCertChainPem, testCertChainPem, new Date(System.currentTimeMillis()), knownPins, - PinningValidationResult.FAILED); + private final HashSet knownPins = + new HashSet() { + { + add(new PublicKeyPin("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")); + add(new PublicKeyPin("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")); + } + }; + + private final PinningFailureReport report = + new PinningFailureReport( + "com.unit.test", + "1.2", + VendorIdentifier.getOrCreate( + InstrumentationRegistry.getInstrumentation().getContext()), + "www.datatheorem.com", + 0, + "datatheorem.com", + true, + true, + testCertChainPem, + testCertChainPem, + new Date(System.currentTimeMillis()), + knownPins, + PinningValidationResult.FAILED); @Before public void setUp() { @@ -129,5 +138,3 @@ public void testExecuteFailedNoConnection() throws MalformedURLException { assertEquals(null, lastResponseCode); } } - - diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTest.java index 1dfd7c0..efe9ca6 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTest.java @@ -1,18 +1,32 @@ package com.datatheorem.android.trustkit.reporting; +import static com.datatheorem.android.trustkit.CertificateUtils.testCertChain; +import static com.datatheorem.android.trustkit.CertificateUtils.testCertChainPem; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; -import androidx.test.platform.app.InstrumentationRegistry; import androidx.localbroadcastmanager.content.LocalBroadcastManager; - +import androidx.test.platform.app.InstrumentationRegistry; import com.datatheorem.android.trustkit.TestableTrustKit; import com.datatheorem.android.trustkit.config.DomainPinningPolicy; import com.datatheorem.android.trustkit.pinning.PinningValidationResult; import com.datatheorem.android.trustkit.utils.VendorIdentifier; - +import java.io.Serializable; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.awaitility.Awaitility; import org.json.JSONArray; import org.json.JSONException; @@ -22,24 +36,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import java.io.Serializable; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import static com.datatheorem.android.trustkit.CertificateUtils.testCertChain; -import static com.datatheorem.android.trustkit.CertificateUtils.testCertChainPem; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - - public class BackgroundReporterTest { @Before @@ -56,44 +52,69 @@ public void testPinValidationFailed() throws MalformedURLException, JSONExceptio Context context = InstrumentationRegistry.getInstrumentation().getContext(); // Initialize TrustKit String serverHostname = "mail.google.com"; - final DomainPinningPolicy domainPolicy = new DomainPinningPolicy.Builder() - .setHostname("google.com") - .setShouldIncludeSubdomains(true) - .setShouldEnforcePinning(true) - .setPublicKeyHashes(new HashSet() {{ - // Wrong pins - add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); - add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); - }}) - .setShouldDisableDefaultReportUri(true) - .setReportUris(new HashSet() {{ add("https://overmind.datatheorem.com"); }}) - .build(); - - final PinningValidationReportTestBroadcastReceiver receiver = new PinningValidationReportTestBroadcastReceiver(); + final DomainPinningPolicy domainPolicy = + new DomainPinningPolicy.Builder() + .setHostname("google.com") + .setShouldIncludeSubdomains(true) + .setShouldEnforcePinning(true) + .setPublicKeyHashes( + new HashSet() { + { + // Wrong pins + add("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="); + add("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="); + } + }) + .setShouldDisableDefaultReportUri(true) + .setReportUris( + new HashSet() { + { + add("https://overmind.datatheorem.com"); + } + }) + .build(); + + final PinningValidationReportTestBroadcastReceiver receiver = + new PinningValidationReportTestBroadcastReceiver(); LocalBroadcastManager.getInstance(context) - .registerReceiver(receiver, new IntentFilter(BackgroundReporter.REPORT_VALIDATION_EVENT)); + .registerReceiver( + receiver, new IntentFilter(BackgroundReporter.REPORT_VALIDATION_EVENT)); - TestableBackgroundReporter reporter = new TestableBackgroundReporter( context, - "com.unit.tests", - "1.2", - VendorIdentifier.getOrCreate(context)); + TestableBackgroundReporter reporter = + new TestableBackgroundReporter( + context, "com.unit.tests", "1.2", VendorIdentifier.getOrCreate(context)); TestableBackgroundReporter reporterSpy = Mockito.spy(reporter); // Call the method twice to also test the report rate limiter - reporterSpy.pinValidationFailed(serverHostname, 443, testCertChain, testCertChain, - domainPolicy, PinningValidationResult.FAILED); - reporterSpy.pinValidationFailed(serverHostname, 443, testCertChain, testCertChain, - domainPolicy, PinningValidationResult.FAILED); + reporterSpy.pinValidationFailed( + serverHostname, + 443, + testCertChain, + testCertChain, + domainPolicy, + PinningValidationResult.FAILED); + reporterSpy.pinValidationFailed( + serverHostname, + 443, + testCertChain, + testCertChain, + domainPolicy, + PinningValidationResult.FAILED); ArgumentCaptor reportSent = ArgumentCaptor.forClass(PinningFailureReport.class); // Ensure the sendReport() method was only called once, to make sure the rate limiter // blocked the second, identical report - verify(reporterSpy, times(1)).sendReport( - reportSent.capture(), - eq(new HashSet() {{ add(new URL("https://overmind.datatheorem.com")); }} ) - ); + verify(reporterSpy, times(1)) + .sendReport( + reportSent.capture(), + eq( + new HashSet() { + { + add(new URL("https://overmind.datatheorem.com")); + } + })); validateSentReport(reportSent.getValue()); @@ -114,7 +135,8 @@ private void validateSentReport(PinningFailureReport reportSent) throws JSONExce assertEquals(443, reportSentJson.getInt("port")); assertTrue(reportSentJson.getBoolean("include-subdomains")); assertTrue(reportSentJson.getBoolean("enforce-pinning")); - assertEquals(PinningValidationResult.FAILED.ordinal(), + assertEquals( + PinningValidationResult.FAILED.ordinal(), reportSentJson.getInt("validation-result")); assertEquals("google.com", reportSentJson.getString("noted-hostname")); @@ -124,16 +146,20 @@ private void validateSentReport(PinningFailureReport reportSent) throws JSONExce JSONArray validatedChain = reportSentJson.getJSONArray("validated-certificate-chain"); assertEquals(2, validatedChain.length()); - assertEquals(testCertChainPem.get(0).replace("\n", ""), + assertEquals( + testCertChainPem.get(0).replace("\n", ""), validatedChain.getString(0).replace("\n", "")); - assertEquals(testCertChainPem.get(1).replace("\n", ""), + assertEquals( + testCertChainPem.get(1).replace("\n", ""), validatedChain.getString(1).replace("\n", "")); JSONArray servedChain = reportSentJson.getJSONArray("served-certificate-chain"); assertEquals(2, servedChain.length()); - assertEquals(testCertChainPem.get(0).replace("\n", ""), + assertEquals( + testCertChainPem.get(0).replace("\n", ""), servedChain.getString(0).replace("\n", "")); - assertEquals(testCertChainPem.get(1).replace("\n", ""), + assertEquals( + testCertChainPem.get(1).replace("\n", ""), servedChain.getString(1).replace("\n", "")); JSONArray knownPins = reportSentJson.getJSONArray("known-pins"); @@ -142,14 +168,16 @@ private void validateSentReport(PinningFailureReport reportSent) throws JSONExce pinsTestable.add(knownPins.getString(i)); } assertEquals(2, knownPins.length()); - assertTrue(pinsTestable - .contains("pin-sha256=\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\"")); - assertTrue(pinsTestable - .contains("pin-sha256=\"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\"")); + assertTrue( + pinsTestable.contains( + "pin-sha256=\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\"")); + assertTrue( + pinsTestable.contains( + "pin-sha256=\"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=\"")); } - private class PinningValidationReportTestBroadcastReceiver extends BroadcastReceiver{ - public final AtomicBoolean broadcastReceived = new AtomicBoolean(false); + private class PinningValidationReportTestBroadcastReceiver extends BroadcastReceiver { + public AtomicBoolean broadcastReceived = new AtomicBoolean(false); public Serializable containedReport; @Override @@ -159,5 +187,3 @@ public void onReceive(Context context, Intent intent) { } } } - - diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/ReportRateLimiterTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/ReportRateLimiterTest.java index 09ea3e6..f97e572 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/ReportRateLimiterTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/ReportRateLimiterTest.java @@ -1,86 +1,103 @@ package com.datatheorem.android.trustkit.reporting; - import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import androidx.test.runner.AndroidJUnit4; import com.datatheorem.android.trustkit.config.PublicKeyPin; import com.datatheorem.android.trustkit.pinning.PinningValidationResult; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import org.junit.Test; -import org.junit.runner.RunWith; - public class ReportRateLimiterTest { - private final HashSet pinList = new HashSet() {{ - add(new PublicKeyPin("rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=")); - add(new PublicKeyPin("0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=")); - }}; - - private final ArrayList pemCertificateList1 = new ArrayList() {{ - add("-----BEGIN CERTIFICATE-----\n"+ - "MIIDGTCCAgGgAwIBAgIJAI1jD1qixIPLMA0GCSqGSIb3DQEBBQUAMCMxITAfBgNV\n"+ - "BAMMGGV2aWxjZXJ0LmRhdGF0aGVvcmVtLmNvbTAeFw0xNTEyMjAxMzU4NDNaFw0y\n"+ - "NTEyMTcxMzU4NDNaMCMxITAfBgNVBAMMGGV2aWxjZXJ0LmRhdGF0aGVvcmVtLmNv\n"+ - "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdltqsRJtO7Nqypkehh\n"+ - "4DSEirp9RM+hJXkBE9nRleTO+utV/snWqX/0wsUrz0wgWyPnAHybGOOXvkrWfXSt\n"+ - "c2/8PyONOeFEU/9S/lWBXGZkaPhgTvkEzPmOOhf06rBMTwXUMGNDI45gKFgkO6Br\n"+ - "bGPeSCuheQj0TKeWdwwNoJ+kczUE06IKu2tcuFRjHXci6VeHjANJzrfKro4ivIRy\n"+ - "bewOGJj1onnpKbui/EOytsmW9MPpOSEXMoVksHOKBQ9nhpL6cDODRvG+t8u7qfFt\n"+ - "mhphemK3IYNMNA4MMXpbJ+Au2hnPApZPEOit34bAwOiGi/batcS3iA+nl06dPYA9\n"+ - "nPkCAwEAAaNQME4wHQYDVR0OBBYEFANxdSXS1JSvjdNtNbYBbRlgii93MB8GA1Ud\n"+ - "IwQYMBaAFANxdSXS1JSvjdNtNbYBbRlgii93MAwGA1UdEwQFMAMBAf8wDQYJKoZI\n"+ - "hvcNAQEFBQADggEBAAM78Bt2aLUgl2Yq4KMIGDeHdWYcRB7QPQ8sp3Q1TOQQzw0i\n"+ - "AukRccl9iYNLgaSJDvlVMapD76jo3okydoWgDogWJhtZpMU/9xegIpukmu5hvF6i\n"+ - "NpqE99PFO5E8BpMkNz+2nskwu//D0as6P9F3tA/o3jC6n6fWX0gt/e9th2ZgVwNQ\n"+ - "9JTH1ZcyFbX9hdBI4xPAtzFX51AsSa8dpRdG+8DmI41Q/1ludoMZboExHldlUbQH\n"+ - "zUuHKF8/T+aNo/9FfpqDz1fFnuoF7tuwyRh73B0YDyDVTNuq7LJ4tmzpVvqIt2tn\n"+ - "RJnQoL4pLQ40SQsoUi4FYG/gxJMoQX6ROWe2nyg=\n"+ - "-----END CERTIFICATE-----"); - }}; - - private final ArrayList pemCertificateList2 = new ArrayList() {{ - add("-----BEGIN CERTIFICATE-----\n" + - "MIIE2TCCA8GgAwIBAgIQFVDTs9tHXX3ivhstjNW2zANBgkqhkiG9w0BAQUFADA8\n" + - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMuMRYwFAYDVQQDEw1U\n" + - "aGF3dGUgU1NMIENBMB4XDTE0MTAwMjAwMDAwMFoXDTE1MTEwMTIzNTk1OVowgZcx\n" + - "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQHFAlQYWxv\n" + - "IEFsdG8xGzAZBgNVBAoUEkRhdGEgVGhlb3JlbSwgSW5jLjEkMCIGA1UECxQbU2Nh\n" + - "biBhbmQgU2VjdXJlIE1vYmlsZSBBcHBzMRwwGgYDVQQDFBN3d3cuZGF0YXRoZW9y\n" + - "ZW0uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5bCuLK3XOnNs\n" + - "i8CJvHU4H5yY3d4G1qzq7EeMydKuScMM8Nqsp4CySKTbrUhi/uIc08II9yBxM+q4\n" + - "NmrEg0tgVvTqvUjmMN/MrYQrSGVLxPq5gadI7UxfWeGSo9DpvgXaw1Vvehs2jGFK\n" + - "jLzDYbzJOhv/pqpv4UCV/xfeuqmTNqqzsp+tB5Zn6gXIvIFsxfpjbeId4OWviLnC\n" + - "q957++coddvqBZd2sWkyzE2un5itXRKfnMGSBTB0cU9/9fXeGhzA+u01Xj+BfpHR\n" + - "uP/eX+rHsgc3a4hbsSWDG5278ujJ5+4To9Bn/rTZy7uALTM2oBZvsFX4567RhB1\n" + - "IYbMDE5y8QIDAQABo4IBeTCCAXUwHgYDVR0RBBcwFYITd3d3LmRhdGF0aGVvcmVt\n" + - "LmNvbTAJBgNVHRMEAjAAMHIGA1UdIARrMGkwZwYKYIZIAYb4RQEHNjBZMCYGCCsG\n" + - "AQUFBwIBFhpodHRwczovL3d3dy50aGF3dGUuY29tL2NwczAvBggrBgEFBQcCAjAj\n" + - "DCFodHRwczovL3d3dy50aGF3dGUuY29tL3JlcG9zaXRvcnkwDgYDVR0PAQH/BAQD\n" + - "AgWgMB8GA1UdIwQYMBaAFKeig7s0RUA9/NUwTxK5PqEBn/bbMCsGA1UdHwQkMCIw\n" + - "IKAeoByGGmh0dHA6Ly90Yi5zeW1jYi5jb20vdGIuY3JsMB0GA1UdJQQWMBQGCCsG\n" + - "AQUFBwMBBggrBgEFBQcDAjBXBggrBgEFBQcBAQRLMEkwHwYIKwYBBQUHMAGGE2h0\n" + - "dHA6Ly90Yi5zeW1jZC5jb20wJgYIKwYBBQUHMAKGGmh0dHA6Ly90Yi5zeW1jYi5j\n" + - "b20vdGIuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQB2qnnrsAICkV9HNuBdXe+cThHV\n" + - "8+5+LBz3zGDpC1rCyq/DIGu0vaa/gasM+MswPj+AEI4f1K1x9K9KedjilVfXH+QI\n" + - "tfRzLO8iR0TbPsC6Y1avuXhal1BuvZ9UQayHRDPUEncsf+SHbIOD2GJzXy7vVk5a\n" + - "VjkvxLtjMprWIi+P7Hbn2qj03qX9KM1DnNsB28jqg7r2rpXNUPUKsxekfrMTaJgg\n" + - "zTnCN/EQvF5eGvAjjHckr1SlogV9o/y4k0x6YmPWR/vopMEPyOj+JhflKCdg+6w3\n" + - "79ESvZUhmgT2285c1Nu5vJjtr8x51zCNIpEoVqdkCU4c1aVZGZogSWl1rAIi\n" + - "-----END CERTIFICATE-----"); - }}; - + private final HashSet pinList = + new HashSet() { + { + add(new PublicKeyPin("rFjc3wG7lTZe43zeYTvPq8k4xdDEutCmIhI5dn4oCeE=")); + add(new PublicKeyPin("0SDf3cRToyZJaMsoS17oF72VMavLxj/N7WBNasNuiR8=")); + } + }; + + private final ArrayList pemCertificateList1 = + new ArrayList() { + { + add( + "-----BEGIN CERTIFICATE-----\n" + + "MIIDGTCCAgGgAwIBAgIJAI1jD1qixIPLMA0GCSqGSIb3DQEBBQUAMCMxITAfBgNV\n" + + "BAMMGGV2aWxjZXJ0LmRhdGF0aGVvcmVtLmNvbTAeFw0xNTEyMjAxMzU4NDNaFw0y\n" + + "NTEyMTcxMzU4NDNaMCMxITAfBgNVBAMMGGV2aWxjZXJ0LmRhdGF0aGVvcmVtLmNv\n" + + "bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMdltqsRJtO7Nqypkehh\n" + + "4DSEirp9RM+hJXkBE9nRleTO+utV/snWqX/0wsUrz0wgWyPnAHybGOOXvkrWfXSt\n" + + "c2/8PyONOeFEU/9S/lWBXGZkaPhgTvkEzPmOOhf06rBMTwXUMGNDI45gKFgkO6Br\n" + + "bGPeSCuheQj0TKeWdwwNoJ+kczUE06IKu2tcuFRjHXci6VeHjANJzrfKro4ivIRy\n" + + "bewOGJj1onnpKbui/EOytsmW9MPpOSEXMoVksHOKBQ9nhpL6cDODRvG+t8u7qfFt\n" + + "mhphemK3IYNMNA4MMXpbJ+Au2hnPApZPEOit34bAwOiGi/batcS3iA+nl06dPYA9\n" + + "nPkCAwEAAaNQME4wHQYDVR0OBBYEFANxdSXS1JSvjdNtNbYBbRlgii93MB8GA1Ud\n" + + "IwQYMBaAFANxdSXS1JSvjdNtNbYBbRlgii93MAwGA1UdEwQFMAMBAf8wDQYJKoZI\n" + + "hvcNAQEFBQADggEBAAM78Bt2aLUgl2Yq4KMIGDeHdWYcRB7QPQ8sp3Q1TOQQzw0i\n" + + "AukRccl9iYNLgaSJDvlVMapD76jo3okydoWgDogWJhtZpMU/9xegIpukmu5hvF6i\n" + + "NpqE99PFO5E8BpMkNz+2nskwu//D0as6P9F3tA/o3jC6n6fWX0gt/e9th2ZgVwNQ\n" + + "9JTH1ZcyFbX9hdBI4xPAtzFX51AsSa8dpRdG+8DmI41Q/1ludoMZboExHldlUbQH\n" + + "zUuHKF8/T+aNo/9FfpqDz1fFnuoF7tuwyRh73B0YDyDVTNuq7LJ4tmzpVvqIt2tn\n" + + "RJnQoL4pLQ40SQsoUi4FYG/gxJMoQX6ROWe2nyg=\n" + + "-----END CERTIFICATE-----"); + } + }; + + private final ArrayList pemCertificateList2 = + new ArrayList() { + { + add( + "-----BEGIN CERTIFICATE-----\n" + + "MIIE2TCCA8GgAwIBAgIQFVDTs9tHXX3ivhstjNW2zANBgkqhkiG9w0BAQUFADA8\n" + + "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMVGhhd3RlLCBJbmMuMRYwFAYDVQQDEw1U\n" + + "aGF3dGUgU1NMIENBMB4XDTE0MTAwMjAwMDAwMFoXDTE1MTEwMTIzNTk1OVowgZcx\n" + + "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRIwEAYDVQQHFAlQYWxv\n" + + "IEFsdG8xGzAZBgNVBAoUEkRhdGEgVGhlb3JlbSwgSW5jLjEkMCIGA1UECxQbU2Nh\n" + + "biBhbmQgU2VjdXJlIE1vYmlsZSBBcHBzMRwwGgYDVQQDFBN3d3cuZGF0YXRoZW9y\n" + + "ZW0uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5bCuLK3XOnNs\n" + + "i8CJvHU4H5yY3d4G1qzq7EeMydKuScMM8Nqsp4CySKTbrUhi/uIc08II9yBxM+q4\n" + + "NmrEg0tgVvTqvUjmMN/MrYQrSGVLxPq5gadI7UxfWeGSo9DpvgXaw1Vvehs2jGFK\n" + + "jLzDYbzJOhv/pqpv4UCV/xfeuqmTNqqzsp+tB5Zn6gXIvIFsxfpjbeId4OWviLnC\n" + + "q957++coddvqBZd2sWkyzE2un5itXRKfnMGSBTB0cU9/9fXeGhzA+u01Xj+BfpHR\n" + + "uP/eX+rHsgc3a4hbsSWDG5278ujJ5+4To9Bn/rTZy7uALTM2oBZvsFX4567RhB1\n" + + "IYbMDE5y8QIDAQABo4IBeTCCAXUwHgYDVR0RBBcwFYITd3d3LmRhdGF0aGVvcmVt\n" + + "LmNvbTAJBgNVHRMEAjAAMHIGA1UdIARrMGkwZwYKYIZIAYb4RQEHNjBZMCYGCCsG\n" + + "AQUFBwIBFhpodHRwczovL3d3dy50aGF3dGUuY29tL2NwczAvBggrBgEFBQcCAjAj\n" + + "DCFodHRwczovL3d3dy50aGF3dGUuY29tL3JlcG9zaXRvcnkwDgYDVR0PAQH/BAQD\n" + + "AgWgMB8GA1UdIwQYMBaAFKeig7s0RUA9/NUwTxK5PqEBn/bbMCsGA1UdHwQkMCIw\n" + + "IKAeoByGGmh0dHA6Ly90Yi5zeW1jYi5jb20vdGIuY3JsMB0GA1UdJQQWMBQGCCsG\n" + + "AQUFBwMBBggrBgEFBQcDAjBXBggrBgEFBQcBAQRLMEkwHwYIKwYBBQUHMAGGE2h0\n" + + "dHA6Ly90Yi5zeW1jZC5jb20wJgYIKwYBBQUHMAKGGmh0dHA6Ly90Yi5zeW1jYi5j\n" + + "b20vdGIuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQB2qnnrsAICkV9HNuBdXe+cThHV\n" + + "8+5+LBz3zGDpC1rCyq/DIGu0vaa/gasM+MswPj+AEI4f1K1x9K9KedjilVfXH+QI\n" + + "tfRzLO8iR0TbPsC6Y1avuXhal1BuvZ9UQayHRDPUEncsf+SHbIOD2GJzXy7vVk5a\n" + + "VjkvxLtjMprWIi+P7Hbn2qj03qX9KM1DnNsB28jqg7r2rpXNUPUKsxekfrMTaJgg\n" + + "zTnCN/EQvF5eGvAjjHckr1SlogV9o/y4k0x6YmPWR/vopMEPyOj+JhflKCdg+6w3\n" + + "79ESvZUhmgT2285c1Nu5vJjtr8x51zCNIpEoVqdkCU4c1aVZGZogSWl1rAIi\n" + + "-----END CERTIFICATE-----"); + } + }; @Test public void test() { - PinningFailureReport report = new PinningFailureReport("com.test", "1.2.3", "vendorId", - "www.host.com", 443, "host.com", true, true, - pemCertificateList1, pemCertificateList1, new Date(), - pinList, PinningValidationResult.FAILED); + PinningFailureReport report = + new PinningFailureReport( + "com.test", + "1.2.3", + "vendorId", + "www.host.com", + 443, + "host.com", + true, + true, + pemCertificateList1, + pemCertificateList1, + new Date(), + pinList, + PinningValidationResult.FAILED); // Ensure the same report will not be sent twice in a row assertFalse(ReportRateLimiter.shouldRateLimit(report)); @@ -88,34 +105,65 @@ pemCertificateList1, pemCertificateList1, new Date(), // Set the last time the cache was reset to more than 24 hours ago and ensure the report // is sent again - long oneDayAgo = System.currentTimeMillis()-25*60*60*1000; + long oneDayAgo = System.currentTimeMillis() - 25 * 60 * 60 * 1000; TestableReportRateLimiter.setLastReportsCacheResetDate(new Date(oneDayAgo)); assertFalse(ReportRateLimiter.shouldRateLimit(report)); assertTrue(ReportRateLimiter.shouldRateLimit(report)); - // Ensure the same report with a different validation result will be sent - report = new PinningFailureReport("com.test", "1.2.3", "vendorId", - "www.host.com", 443, "host.com", true, true, - pemCertificateList1, pemCertificateList1, new Date(), - pinList, PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED); + report = + new PinningFailureReport( + "com.test", + "1.2.3", + "vendorId", + "www.host.com", + 443, + "host.com", + true, + true, + pemCertificateList1, + pemCertificateList1, + new Date(), + pinList, + PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED); assertFalse(ReportRateLimiter.shouldRateLimit(report)); assertTrue(ReportRateLimiter.shouldRateLimit(report)); // Ensure the same report with a different hostname will be sent - report = new PinningFailureReport("com.test", "1.2.3", "vendorId", - "www.otherhost.com", 443, "host.com", true, true, - pemCertificateList1, pemCertificateList1, new Date(), - pinList, PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED); + report = + new PinningFailureReport( + "com.test", + "1.2.3", + "vendorId", + "www.otherhost.com", + 443, + "host.com", + true, + true, + pemCertificateList1, + pemCertificateList1, + new Date(), + pinList, + PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED); assertFalse(ReportRateLimiter.shouldRateLimit(report)); assertTrue(ReportRateLimiter.shouldRateLimit(report)); - // Ensure the same report with a different certificate chain will be sent - report = new PinningFailureReport("com.test", "1.2.3", "vendorId", - "www.otherhost.com", 443, "host.com", true, true, - pemCertificateList2, pemCertificateList2, new Date(), - pinList, PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED); + report = + new PinningFailureReport( + "com.test", + "1.2.3", + "vendorId", + "www.otherhost.com", + 443, + "host.com", + true, + true, + pemCertificateList2, + pemCertificateList2, + new Date(), + pinList, + PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED); assertFalse(ReportRateLimiter.shouldRateLimit(report)); assertTrue(ReportRateLimiter.shouldRateLimit(report)); } diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/TestableBackgroundReporter.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/TestableBackgroundReporter.java index 9127462..7594dc1 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/TestableBackgroundReporter.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/TestableBackgroundReporter.java @@ -1,18 +1,15 @@ package com.datatheorem.android.trustkit.reporting; - import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; - import java.net.URL; import java.util.Set; - @RequiresApi(api = 16) public class TestableBackgroundReporter extends BackgroundReporter { - public TestableBackgroundReporter(Context context, String appPackageName, String appVersion, String appVendorId){ + public TestableBackgroundReporter( + Context context, String appPackageName, String appVersion, String appVendorId) { super(context, appPackageName, appVersion, appVendorId); } diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/TestableReportRateLimiter.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/TestableReportRateLimiter.java index 590dc5e..3ebad11 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/TestableReportRateLimiter.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/reporting/TestableReportRateLimiter.java @@ -1,6 +1,5 @@ package com.datatheorem.android.trustkit.reporting; - import java.util.Date; class TestableReportRateLimiter extends ReportRateLimiter { diff --git a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/utils/VendorIdentifierTest.java b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/utils/VendorIdentifierTest.java index 3755e09..97b4359 100644 --- a/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/utils/VendorIdentifierTest.java +++ b/trustkit/src/androidTest/java/com/datatheorem/android/trustkit/utils/VendorIdentifierTest.java @@ -1,14 +1,11 @@ package com.datatheorem.android.trustkit.utils; -import android.content.Context; - -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Test; - import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; +import android.content.Context; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; public class VendorIdentifierTest { diff --git a/trustkit/src/main/AndroidManifest.xml b/trustkit/src/main/AndroidManifest.xml index 5567873..9b65eb0 100644 --- a/trustkit/src/main/AndroidManifest.xml +++ b/trustkit/src/main/AndroidManifest.xml @@ -1 +1 @@ - + diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/TrustKit.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/TrustKit.java index ea9ed89..7a1c438 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/TrustKit.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/TrustKit.java @@ -4,18 +4,14 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.Build; -import androidx.annotation.NonNull; import android.util.Printer; - +import androidx.annotation.NonNull; import com.datatheorem.android.trustkit.config.ConfigurationException; import com.datatheorem.android.trustkit.config.TrustKitConfiguration; import com.datatheorem.android.trustkit.pinning.TrustManagerBuilder; import com.datatheorem.android.trustkit.reporting.BackgroundReporter; import com.datatheorem.android.trustkit.utils.TrustKitLog; import com.datatheorem.android.trustkit.utils.VendorIdentifier; - -import org.xmlpull.v1.XmlPullParserException; - import java.io.IOException; import java.security.KeyManagementException; import java.security.KeyStoreException; @@ -23,150 +19,130 @@ import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.Set; - import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; - +import org.xmlpull.v1.XmlPullParserException; /** * Class that provides all of the TrustKit public APIs. * - *

- * It should be used to initialize the App's SSL pinning policy and to retrieve the - * corresponding {@code SSLSocketFactory} and {@code X509TrustManager}, to be used to add SSL - * pinning validation to the App's network connections. - *

- * - *

- * TrustKit works by extending the - * - * Android N Network Security Configuration in two ways: + *

It should be used to initialize the App's SSL pinning policy and to retrieve the corresponding + * {@code SSLSocketFactory} and {@code X509TrustManager}, to be used to add SSL pinning validation + * to the App's network connections. * - *

    - *
  • It provides support for the SSL pinning functionality of the Android N Network - * Security Configuration to earlier versions of Android, down to API level 17. This - * allows Apps supporting versions of Android that earlier than N to implement SSL - * pinning in a way that is future-proof.
  • + *

    TrustKit works by extending the + * Android N Network Security Configuration in two ways: * - *

  • It adds the ability to send reports when pinning validation failed for a specific - * connection. Reports have a format that is similar to the report-uri feature of - * HTTP - * Public Key Pinning and TrustKit - * iOS.
  • - *
+ *
    + *
  • It provides support for the SSL pinning functionality of the Android N Network Security + * Configuration to earlier versions of Android, down to API level 17. This allows Apps + * supporting versions of Android that earlier than N to implement SSL pinning in a way that + * is future-proof. + *
  • It adds the ability to send reports when pinning validation failed for a specific + * connection. Reports have a format that is similar to the report-uri feature of HTTP Public Key Pinning and TrustKit iOS. + *
* - * For better compatibility, TrustKit will also run on API levels 15 and 16 but its - * functionality will be disabled. - *

+ * For better compatibility, TrustKit will also run on API levels 15 and 16 but its functionality + * will be disabled. * *

Supported Android N Network Security Settings

* - *

- * On devices before Android N, TrustKit supports the following XML tags defined in the - * - * Android N Network Security Configuration for deploying SSL pinning: - *

+ *

On devices before Android N, TrustKit supports the following XML tags defined in the Android N Network Security Configuration for deploying SSL pinning: * *

    - *
  • {@code }.
  • - *
  • {@code } and the {@code includeSubdomains} attribute.
  • - *
  • {@code } and the {@code expiration} attribute.
  • - *
  • {@code } and the {@code digest} attribute.
  • - *
  • {@code }.
  • - *
  • {@code }, but only within a {@code } tag. Hence, custom - * trust anchors for specific domains cannot be set.
  • - *
  • {@code } and the {@code overridePins} and {@code src} attributes. Only raw - * certificate files are supported for the {@code src} attribute ({@code user} and - * {@code system} values will be ignored).
  • + *
  • {@code }. + *
  • {@code } and the {@code includeSubdomains} attribute. + *
  • {@code } and the {@code expiration} attribute. + *
  • {@code } and the {@code digest} attribute. + *
  • {@code }. + *
  • {@code }, but only within a {@code } tag. Hence, custom + * trust anchors for specific domains cannot be set. + *
  • {@code } and the {@code overridePins} and {@code src} attributes. Only raw + * certificate files are supported for the {@code src} attribute ({@code user} and {@code + * system} values will be ignored). *
* - *

- * On Android N devices, the OS' implementation is used and all XML tags are supported. - *

+ *

On Android N devices, the OS' implementation is used and all XML tags are supported. * *

Additional TrustKit Settings

* - *

- * TrustKit provides additional functionality to not enforce pinning validation and to allow - * reports to be sent by the App whenever a pinning validation failure occurred. - *

+ *

TrustKit provides additional functionality to not enforce pinning validation and to allow + * reports to be sent by the App whenever a pinning validation failure occurred. * *

{@code }

* - *

- * The main tag for specifying additional TrustKit settings, to be defined within a - * {@code } entry. It supports the following attributes: - *

- * - *
    - *
  • {@code enforcePinning}: if set to {@code false}, TrustKit will not block SSL - * connections that caused a pinning validation error; default value is {@code false}. When - * a pinning failure occurs, pin failure reports will always be sent to the configured - * report URIs regardless of the value of {@code enforcePinning}. This behavior allows - * deploying pinning validation without the risk of locking out users due to a - * misconfiguration, while still receiving reports in order to assess how many users would - * be affected by pinning.
  • + *

    The main tag for specifying additional TrustKit settings, to be defined within a {@code + * } entry. It supports the following attributes: * - *

  • {@code disableDefaultReportUri}: if set to {@code true}, the default report URL for - * sending pin failure reports will be disabled; default value is {@code false}. By default, - * pin failure reports are sent to a report server hosted by Data Theorem, for detecting - * potential CA compromises and man-in-the-middle attacks, as well as providing a free - * dashboard for developers; email - * info@datatheorem.com if you'd like a dashboard - * for your App. Only pin failure reports are sent, which contain the App's package name, - * a randomly-generated ID, and the server's hostname and certificate chain that failed - * validation.
  • - *
+ *
    + *
  • {@code enforcePinning}: if set to {@code false}, TrustKit will not block SSL connections + * that caused a pinning validation error; default value is {@code false}. When a pinning + * failure occurs, pin failure reports will always be sent to the configured report URIs + * regardless of the value of {@code enforcePinning}. This behavior allows deploying pinning + * validation without the risk of locking out users due to a misconfiguration, while still + * receiving reports in order to assess how many users would be affected by pinning. + *
  • {@code disableDefaultReportUri}: if set to {@code true}, the default report URL for sending + * pin failure reports will be disabled; default value is {@code false}. By default, pin + * failure reports are sent to a report server hosted by Data Theorem, for detecting potential + * CA compromises and man-in-the-middle attacks, as well as providing a free dashboard for + * developers; email info@datatheorem.com if you'd + * like a dashboard for your App. Only pin failure reports are sent, which contain the App's + * package name, a randomly-generated ID, and the server's hostname and certificate chain that + * failed validation. + *
* *

{@code }

* - * A URL to which pin validation failures should be reported, to be defined within a - * {@code } tag. The format of the reports is similar to the one described in - * RFC 7469 for the HPKP - * specification. A sample TrustKit report is available - * - * in the project's repository - * . + * A URL to which pin validation failures should be reported, to be defined within a {@code + * } tag. The format of the reports is similar to the one described in RFC 7469 for the HPKP + * specification. A sample TrustKit report is available in the project's repository . * *

Sample TrustKit Configuration

- *

- * The following configuration will pin the www.datatheorem.com domain without enforcing pinning - * validation, and will send pinning failure reports to report.datatheorem.com. It also defines - * a debug overrides to add a debug certificate authority to the App's trust store for easier - * debugging of the App's network traffic. - *

- *
- *     {@code
- *         
- *         
- *         
- *         
- *         
- *         
- *         www.datatheorem.com
- *         
- *         k3XnEYQCK79AtL9GYnT/nyhsabas03V+bhRQYHQbpXU=
- *         2kOi4HdYYsvTR1sTIR7RHwlf2SescTrpza9ZrWy7poQ=
- *         
- *         
- *         
- *         
- *         
- *         http://report.datatheorem.com/log_report
- *         
- *         
- *         
- *         
- *         
- *         
- *         
- *         
- *         
- *     }
- * 
* + *

The following configuration will pin the www.datatheorem.com domain without enforcing pinning + * validation, and will send pinning failure reports to report.datatheorem.com. It also defines a + * debug overrides to add a debug certificate authority to the App's trust store for easier + * debugging of the App's network traffic. + * + *

{@code
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * www.datatheorem.com
+ * 
+ * k3XnEYQCK79AtL9GYnT/nyhsabas03V+bhRQYHQbpXU=
+ * 2kOi4HdYYsvTR1sTIR7RHwlf2SescTrpza9ZrWy7poQ=
+ * 
+ * 
+ * 
+ * 
+ * 
+ * http://report.datatheorem.com/log_report
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ * 
+ *
+ * }
*/ public class TrustKit { @@ -174,14 +150,14 @@ public class TrustKit { private final TrustKitConfiguration trustKitConfiguration; - protected TrustKit(@NonNull Context context, - @NonNull TrustKitConfiguration trustKitConfiguration) { + protected TrustKit( + @NonNull Context context, @NonNull TrustKitConfiguration trustKitConfiguration) { this.trustKitConfiguration = trustKitConfiguration; // Setup the debug-overrides setting if the App is debuggable // Do not use BuildConfig.DEBUG as it does not work for libraries - boolean isAppDebuggable = (0 != - (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)); + boolean isAppDebuggable = + (0 != (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)); Set debugCaCerts = null; boolean shouldOverridePins = false; if (isAppDebuggable) { @@ -207,37 +183,42 @@ protected TrustKit(@NonNull Context context, } String appVendorId = VendorIdentifier.getOrCreate(context); - BackgroundReporter reporter = new BackgroundReporter(context, appPackageName, appVersion, - appVendorId); + BackgroundReporter reporter = + new BackgroundReporter(context, appPackageName, appVersion, appVendorId); // Initialize the trust manager builder try { - TrustManagerBuilder.initializeBaselineTrustManager(debugCaCerts, - shouldOverridePins, reporter); - } catch (CertificateException | NoSuchAlgorithmException | KeyStoreException + TrustManagerBuilder.initializeBaselineTrustManager( + debugCaCerts, shouldOverridePins, reporter); + } catch (CertificateException + | NoSuchAlgorithmException + | KeyStoreException | IOException e) { throw new ConfigurationException("Could not parse certificates"); } } - /** Try to retrieve the Network Security Policy resource ID configured in the App's manifest. - * Somewhat convoluted as other means of getting the resource ID involve using private APIs. + /** + * Try to retrieve the Network Security Policy resource ID configured in the App's manifest. + * + *

Somewhat convoluted as other means of getting the resource ID involve using private APIs. * - * @param context android context + * @param context * @return The resource ID for the XML file containing the configured Network Security Policy or - * -1 if no policy was configured in the App's manifest or if we are not running on Android N. + * -1 if no policy was configured in the App's manifest or if we are not running on Android + * N. */ - static private int getNetSecConfigResourceId(@NonNull Context context) { + private static int getNetSecConfigResourceId(@NonNull Context context) { ApplicationInfo info = context.getApplicationInfo(); // Dump the content of the ApplicationInfo, which contains the resource ID on Android N class NetSecConfigResIdRetriever implements Printer { private int netSecConfigResourceId = -1; + private final String NETSEC_LINE_FORMAT = "networkSecurityConfigRes=0x"; public void println(String x) { if (netSecConfigResourceId == -1) { // Attempt at parsing "networkSecurityConfigRes=0x1234" - String NETSEC_LINE_FORMAT = "networkSecurityConfigRes=0x"; if (x.contains(NETSEC_LINE_FORMAT)) { netSecConfigResourceId = Integer.parseInt(x.substring(NETSEC_LINE_FORMAT.length()), 16); @@ -245,7 +226,9 @@ public void println(String x) { } } - private int getNetworkSecurityConfigResId() { return netSecConfigResourceId; } + private int getNetworkSecurityConfigResId() { + return netSecConfigResourceId; + } } NetSecConfigResIdRetriever retriever = new NetSecConfigResIdRetriever(); @@ -253,35 +236,38 @@ public void println(String x) { return retriever.getNetworkSecurityConfigResId(); } - /** Initialize TrustKit with the Network Security Configuration file at the default location + /** + * Initialize TrustKit with the Network Security Configuration file at the default location * res/xml/network_security_config.xml. The Network Security Configuration file must also have - * been - * added to the App's manifest. + * been added to the App's manifest. * * @param context the application's context. * @throws ConfigurationException if the policy could not be parsed or contained errors. */ @NonNull - public synchronized static TrustKit initializeWithNetworkSecurityConfiguration( + public static synchronized TrustKit initializeWithNetworkSecurityConfiguration( @NonNull Context context) { // Try to get the default network policy resource ID - int networkSecurityConfigId = context.getResources().getIdentifier( - "network_security_config", "xml", context.getPackageName()); + int networkSecurityConfigId = + context.getResources() + .getIdentifier("network_security_config", "xml", context.getPackageName()); return initializeWithNetworkSecurityConfiguration(context, networkSecurityConfigId); } - /** Initialize TrustKit with the Network Security Configuration file with the specified - * resource ID. The Network Security Configuration file must also have - * been - * added to the App's manifest. + /** + * Initialize TrustKit with the Network Security Configuration file with the specified resource + * ID. The Network Security Configuration file must also have been added to the App's manifest. * * @param context the application's context. * @param configurationResourceId the resource ID for the Network Security Configuration file to - * use. + * use. * @throws ConfigurationException if the policy could not be parsed or contained errors. */ @NonNull - public synchronized static TrustKit initializeWithNetworkSecurityConfiguration( + public static synchronized TrustKit initializeWithNetworkSecurityConfiguration( @NonNull Context context, int configurationResourceId) { if (trustKitInstance != null) { throw new IllegalStateException("TrustKit has already been initialized"); @@ -294,34 +280,37 @@ public synchronized static TrustKit initializeWithNetworkSecurityConfiguration( if (systemConfigResId == -1) { // Android did not find a policy because the supplied resource ID is wrong or the // policy file is not properly setup in the manifest, or contains bad data - throw new ConfigurationException("TrustKit was initialized with a network policy " + - "that was not properly configured for Android N - make sure it is in the " + - "App's Manifest."); - } - else if (systemConfigResId != configurationResourceId) { - throw new ConfigurationException("TrustKit was initialized with a different " + - "network policy than the one configured in the App's manifest."); + throw new ConfigurationException( + "TrustKit was initialized with a network policy " + + "that was not properly configured for Android N - make sure it is in the " + + "App's Manifest."); + } else if (systemConfigResId != configurationResourceId) { + throw new ConfigurationException( + "TrustKit was initialized with a different " + + "network policy than the one configured in the App's manifest."); } } // Then try to load the supplied policy TrustKitConfiguration trustKitConfiguration; try { - trustKitConfiguration = TrustKitConfiguration.fromXmlPolicy( - context, context.getResources().getXml(configurationResourceId) - ); + trustKitConfiguration = + TrustKitConfiguration.fromXmlPolicy( + context, context.getResources().getXml(configurationResourceId)); } catch (XmlPullParserException | IOException e) { throw new ConfigurationException("Could not parse network security policy file"); } catch (CertificateException e) { - throw new ConfigurationException("Could not find the debug certificate in the " + - "network security police file"); + throw new ConfigurationException( + "Could not find the debug certificate in the " + + "network security police file"); } trustKitInstance = new TrustKit(context, trustKitConfiguration); return trustKitInstance; } - /** Retrieve the initialized instance of TrustKit. + /** + * Retrieve the initialized instance of TrustKit. * * @throws IllegalStateException if TrustKit has not been initialized. */ @@ -333,35 +322,33 @@ public static TrustKit getInstance() { return trustKitInstance; } - /** Retrieve the current TrustKit configuration. - * - */ + /** Retrieve the current TrustKit configuration. */ @NonNull - public TrustKitConfiguration getConfiguration() { return trustKitConfiguration; } - - /** Retrieve an {@code SSLSSocketFactory} that implements SSL pinning validation based on the + public TrustKitConfiguration getConfiguration() { + return trustKitConfiguration; + } + + /** + * Retrieve an {@code SSLSSocketFactory} that implements SSL pinning validation based on the * current TrustKit configuration for the specified serverHostname. It can be used with most * network APIs (such as {@code HttpsUrlConnection}) to add SSL pinning validation to the * connections. * - *

- * The {@code SSLSocketFactory} is configured for the supplied serverHostname, and will - * enforce this domain's pinning policy even if a redirection to a different domain occurs - * during the connection. Hence validation will always fail in the case of a redirection to - * a different domain. - * However, pinning validation is only meant to be used on the App's API server(s), and - * redirections to other domains should not happen in this scenario. - *

+ *

The {@code SSLSocketFactory} is configured for the supplied serverHostname, and will + * enforce this domain's pinning policy even if a redirection to a different domain occurs + * during the connection. Hence validation will always fail in the case of a redirection to a + * different domain. However, pinning validation is only meant to be used on the App's API + * server(s), and redirections to other domains should not happen in this scenario. * * @param serverHostname the server's hostname that the {@code SSLSocketFactory} will be used to - * connect to. This hostname will be used to retrieve the pinning policy - * from the current TrustKit configuration. + * connect to. This hostname will be used to retrieve the pinning policy from the current + * TrustKit configuration. */ @NonNull public SSLSocketFactory getSSLSocketFactory(@NonNull String serverHostname) { try { SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(null, new TrustManager[]{getTrustManager(serverHostname)}, null); + sslContext.init(null, new TrustManager[] {getTrustManager(serverHostname)}, null); return sslContext.getSocketFactory(); } catch (NoSuchAlgorithmException | KeyManagementException e) { @@ -370,23 +357,20 @@ public SSLSocketFactory getSSLSocketFactory(@NonNull String serverHostname) { } } - - /** Retrieve an {@code X509TrustManager} that implements SSL pinning validation based on the + /** + * Retrieve an {@code X509TrustManager} that implements SSL pinning validation based on the * current TrustKit configuration for the supplied hostname. It can be used with some network * APIs that let developers supply a trust manager to customize SSL validation. * - *

- * The {@code X509TrustManager} is configured for the supplied serverHostname, and will - * enforce this domain's pinning policy even if a redirection to a different domain occurs - * during the connection. Hence validation will always fail in the case of a redirection to - * a different domain. - * However, pinning validation is only meant to be used on the App's API server(s), and - * redirections to other domains should not happen in this scenario. - *

+ *

The {@code X509TrustManager} is configured for the supplied serverHostname, and will + * enforce this domain's pinning policy even if a redirection to a different domain occurs + * during the connection. Hence validation will always fail in the case of a redirection to a + * different domain. However, pinning validation is only meant to be used on the App's API + * server(s), and redirections to other domains should not happen in this scenario. * * @param serverHostname the server's hostname that the {@code X509TrustManager} will be used to - * connect to. This hostname will be used to retrieve the pinning policy - * from the current TrustKit configuration. + * connect to. This hostname will be used to retrieve the pinning policy from the current + * TrustKit configuration. */ @NonNull public X509TrustManager getTrustManager(@NonNull String serverHostname) { diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/ConfigurationException.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/ConfigurationException.java index 8775b23..c48eec5 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/ConfigurationException.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/ConfigurationException.java @@ -1,7 +1,7 @@ package com.datatheorem.android.trustkit.config; public final class ConfigurationException extends RuntimeException { - public ConfigurationException(String detailMessage) { - super(detailMessage); - } -} \ No newline at end of file + public ConfigurationException(String detailMessage) { + super(detailMessage); + } +} diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/DomainPinningPolicy.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/DomainPinningPolicy.java index 1e9aae0..d35e27d 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/DomainPinningPolicy.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/DomainPinningPolicy.java @@ -9,11 +9,11 @@ import java.util.HashSet; import java.util.Set; - public final class DomainPinningPolicy { // The default URL to submit pin failure report to private static final URL DEFAULT_REPORTING_URL; + static { java.net.URL defaultUrl; try { @@ -31,13 +31,14 @@ public final class DomainPinningPolicy { private final boolean shouldEnforcePinning; @NonNull private final Set reportUris; - DomainPinningPolicy(@NonNull String hostname, - Boolean shouldIncludeSubdomains, - Set publicKeyHashStrList, - Boolean shouldEnforcePinning, - @Nullable Date expirationDate, - @Nullable Set reportUriStrList, - Boolean shouldDisableDefaultReportUri) + DomainPinningPolicy( + @NonNull String hostname, + Boolean shouldIncludeSubdomains, + Set publicKeyHashStrList, + Boolean shouldEnforcePinning, + @Nullable Date expirationDate, + @Nullable Set reportUriStrList, + Boolean shouldDisableDefaultReportUri) throws MalformedURLException { // Run some sanity checks on the configuration // Check if the hostname seems valid @@ -50,8 +51,7 @@ public final class DomainPinningPolicy { // Due to the fact some configurations could be added without any pin (e.g. localhost) // the publicKeyHashStrList would be null. // Thus we're managing these cases as an empty set of pins. - if (publicKeyHashStrList == null) - publicKeyHashStrList = new HashSet<>(); + if (publicKeyHashStrList == null) publicKeyHashStrList = new HashSet<>(); // Parse boolean settings and handle default values if (shouldEnforcePinning == null) { @@ -65,28 +65,33 @@ public final class DomainPinningPolicy { this.shouldIncludeSubdomains = shouldIncludeSubdomains; } - // Check if the configuration has a empty pin-set and still would enforce pinning // TrustKit should not work if the configuration contains both (opposite behaviors) if (publicKeyHashStrList.isEmpty() && this.shouldEnforcePinning) { - throw new ConfigurationException("An empty pin-set was supplied "+ - "for domain " + this.hostname + " with the enforcePinning set to true. " + - "An empty pin-set disables pinning and can't be use with enforcePinning set to true."); + throw new ConfigurationException( + "An empty pin-set was supplied " + + "for domain " + + this.hostname + + " with the enforcePinning set to true. " + + "An empty pin-set disables pinning and can't be use with enforcePinning set to true."); } // Check if the configuration has at least two pins (including a backup pin) // TrustKit should not work if the configuration contains only one pin // more info (https://tools.ietf.org/html/rfc7469#page-21) if (publicKeyHashStrList.size() < 2 && this.shouldEnforcePinning) { - throw new ConfigurationException("Less than two pins were supplied "+ - "for domain " + this.hostname + ". This might " + - "brick your App; please review the Getting Started guide in " + - "./docs/getting-started.md"); + throw new ConfigurationException( + "Less than two pins were supplied " + + "for domain " + + this.hostname + + ". This might " + + "brick your App; please review the Getting Started guide in " + + "./docs/getting-started.md"); } // Parse the supplied pins publicKeyPins = new HashSet<>(); - for (String pinStr : publicKeyHashStrList) { + for (String pinStr : publicKeyHashStrList) { publicKeyPins.add(new PublicKeyPin(pinStr)); } @@ -99,7 +104,7 @@ public final class DomainPinningPolicy { } // Add the default report URL - if ((shouldDisableDefaultReportUri == null) || (!shouldDisableDefaultReportUri) ) { + if ((shouldDisableDefaultReportUri == null) || (!shouldDisableDefaultReportUri)) { reportUris.add(DEFAULT_REPORTING_URL); } @@ -137,17 +142,25 @@ public Date getExpirationDate() { @NonNull @Override public String toString() { - return "DomainPinningPolicy{" + - "hostname = " + hostname + "\n" + - "knownPins = " + Arrays.toString(publicKeyPins.toArray()) + - "\n" + - "shouldEnforcePinning = " + shouldEnforcePinning + "\n" + - "reportUris = " + reportUris + "\n" + - "shouldIncludeSubdomains = " + shouldIncludeSubdomains + "\n" + - "}"; + return "DomainPinningPolicy{" + + "hostname = " + + hostname + + "\n" + + "knownPins = " + + Arrays.toString(publicKeyPins.toArray()) + + "\n" + + "shouldEnforcePinning = " + + shouldEnforcePinning + + "\n" + + "reportUris = " + + reportUris + + "\n" + + "shouldIncludeSubdomains = " + + shouldIncludeSubdomains + + "\n" + + "}"; } - public static final class Builder { // The domain must always be specified in domain-config private String hostname; @@ -190,7 +203,8 @@ public DomainPinningPolicy build() throws MalformedURLException { } if (shouldDisableDefaultReportUri == null) { - shouldDisableDefaultReportUri = parentBuilder.getShouldDisableDefaultReportUri(); + shouldDisableDefaultReportUri = + parentBuilder.getShouldDisableDefaultReportUri(); } } @@ -205,8 +219,7 @@ public DomainPinningPolicy build() throws MalformedURLException { shouldEnforcePinning, expirationDate, reportUris, - shouldDisableDefaultReportUri - ); + shouldDisableDefaultReportUri); } public Builder setParent(Builder parent) { diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/DomainValidator.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/DomainValidator.java index e5f7ec3..e63d486 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/DomainValidator.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/DomainValidator.java @@ -1,4 +1,5 @@ -// TrustKit: Taken from https://apache.googlesource.com/commons-validator/+/VALIDATOR_1_5_1/src/main/java/org/apache/commons/validator/routines/DomainValidator.java +// TrustKit: Taken from +// https://apache.googlesource.com/commons-validator/+/VALIDATOR_1_5_1/src/main/java/org/apache/commons/validator/routines/DomainValidator.java package com.datatheorem.android.trustkit.config; /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -17,47 +18,37 @@ * limitations under the License. */ - import java.io.Serializable; import java.net.IDN; import java.util.Arrays; import java.util.Locale; + /** - *

Domain name validation routines.

+ * Domain name validation routines. * - *

- * This validator provides methods for validating Internet domain names - * and top-level domains. - *

+ *

This validator provides methods for validating Internet domain names and top-level domains. * - *

Domain names are evaluated according - * to the standards RFC1034, - * section 3, and RFC1123, - * section 2.1. No accommodation is provided for the specialized needs of - * other applications; if the domain name has been URL-encoded, for example, - * validation will fail even though the equivalent plaintext version of the - * same name would have passed. - *

+ *

Domain names are evaluated according to the standards RFC1034, section 3, and RFC1123, section 2.1. No accommodation is provided + * for the specialized needs of other applications; if the domain name has been URL-encoded, for + * example, validation will fail even though the equivalent plaintext version of the same name would + * have passed. * - *

- * Validation is also provided for top-level domains (TLDs) as defined and - * maintained by the Internet Assigned Numbers Authority (IANA): - *

+ *

Validation is also provided for top-level domains (TLDs) as defined and maintained by the + * Internet Assigned Numbers Authority (IANA): * - *

    - *
  • {@link #isValidInfrastructureTld} - validates infrastructure TLDs - * (.arpa, etc.)
  • - *
  • {@link #isValidGenericTld} - validates generic TLDs - * (.com, .org, etc.)
  • - *
  • {@link #isValidCountryCodeTld} - validates country code TLDs - * (.us, .uk, .cn, etc.)
  • - *
+ *
    + *
  • {@link #isValidInfrastructureTld} - validates infrastructure TLDs (.arpa, + * etc.) + *
  • {@link #isValidGenericTld} - validates generic TLDs (.com, .org, etc.) + *
  • {@link #isValidCountryCodeTld} - validates country code TLDs (.us, .uk, .cn, + * etc.) + *
* - *

- * (NOTE: This class does not provide IP address lookup for domain names or - * methods to ensure that a given domain name matches a specific IP; see - * {@link java.net.InetAddress} for that functionality.) - *

+ *

(NOTE: This class does not provide IP address lookup for domain names or methods to + * ensure that a given domain name matches a specific IP; see {@link java.net.InetAddress} for that + * functionality.) * * @version $Revision$ * @since Validator 1.4 @@ -82,30 +73,19 @@ class DomainValidator implements Serializable { private static final String DOMAIN_NAME_REGEX = "^(?:" + DOMAIN_LABEL_REGEX + "\\.)+" + "(" + TOP_LABEL_REGEX + ")\\.?$"; private final boolean allowLocal; - /** - * Singleton instance of this validator, which - * doesn't consider local addresses as valid. - */ + /** Singleton instance of this validator, which doesn't consider local addresses as valid. */ private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator(false); - /** - * Singleton instance of this validator, which does - * consider local addresses valid. - */ + /** Singleton instance of this validator, which does consider local addresses valid. */ private static final DomainValidator DOMAIN_VALIDATOR_WITH_LOCAL = new DomainValidator(true); - /** - * RegexValidator for matching domains. - */ - private final RegexValidator domainRegex = - new RegexValidator(DOMAIN_NAME_REGEX); - /** - * RegexValidator for matching a local hostname - */ + /** RegexValidator for matching domains. */ + private final RegexValidator domainRegex = new RegexValidator(DOMAIN_NAME_REGEX); + /** RegexValidator for matching a local hostname */ // RFC1123 sec 2.1 allows hostnames to start with a digit - private final RegexValidator hostnameRegex = - new RegexValidator(DOMAIN_LABEL_REGEX); + private final RegexValidator hostnameRegex = new RegexValidator(DOMAIN_LABEL_REGEX); /** - * Returns the singleton instance of this validator. It - * will not consider local addresses as valid. + * Returns the singleton instance of this validator. It will not consider local addresses as + * valid. + * * @return the singleton instance of this validator */ public static synchronized DomainValidator getInstance() { @@ -113,14 +93,14 @@ public static synchronized DomainValidator getInstance() { return DOMAIN_VALIDATOR; } /** - * Returns the singleton instance of this validator, - * with local validation as required. + * Returns the singleton instance of this validator, with local validation as required. + * * @param allowLocal Should local addresses be considered valid? * @return the singleton instance of this validator */ public static synchronized DomainValidator getInstance(boolean allowLocal) { inUse = true; - if(allowLocal) { + if (allowLocal) { return DOMAIN_VALIDATOR_WITH_LOCAL; } return DOMAIN_VALIDATOR; @@ -130,9 +110,9 @@ private DomainValidator(boolean allowLocal) { this.allowLocal = allowLocal; } /** - * Returns true if the specified String parses - * as a valid domain name with a recognized top-level domain. - * The parsing is case-insensitive. + * Returns true if the specified String parses as a valid domain name with a + * recognized top-level domain. The parsing is case-insensitive. + * * @param domain the parameter to check for domain name syntax * @return true if the parameter is a valid domain name */ @@ -169,19 +149,18 @@ final boolean isValidDomainSyntax(String domain) { return false; } String[] groups = domainRegex.match(domain); - return (groups != null && groups.length > 0) - || hostnameRegex.isValid(domain); + return (groups != null && groups.length > 0) || hostnameRegex.isValid(domain); } /** - * Returns true if the specified String matches any - * IANA-defined top-level domain. Leading dots are ignored if present. - * The search is case-insensitive. + * Returns true if the specified String matches any IANA-defined top-level domain. + * Leading dots are ignored if present. The search is case-insensitive. + * * @param tld the parameter to check for TLD status, not null * @return true if the parameter is a TLD */ public boolean isValidTld(String tld) { tld = unicodeToASCII(tld); - if(allowLocal && isValidLocalTld(tld)) { + if (allowLocal && isValidLocalTld(tld)) { return true; } return isValidInfrastructureTld(tld) @@ -189,9 +168,9 @@ public boolean isValidTld(String tld) { || isValidCountryCodeTld(tld); } /** - * Returns true if the specified String matches any - * IANA-defined infrastructure top-level domain. Leading dots are - * ignored if present. The search is case-insensitive. + * Returns true if the specified String matches any IANA-defined infrastructure + * top-level domain. Leading dots are ignored if present. The search is case-insensitive. + * * @param iTld the parameter to check for infrastructure TLD status, not null * @return true if the parameter is an infrastructure TLD */ @@ -200,9 +179,9 @@ public boolean isValidInfrastructureTld(String iTld) { return arrayContains(INFRASTRUCTURE_TLDS, key); } /** - * Returns true if the specified String matches any - * IANA-defined generic top-level domain. Leading dots are ignored - * if present. The search is case-insensitive. + * Returns true if the specified String matches any IANA-defined generic top-level + * domain. Leading dots are ignored if present. The search is case-insensitive. + * * @param gTld the parameter to check for generic TLD status, not null * @return true if the parameter is a generic TLD */ @@ -212,9 +191,9 @@ public boolean isValidGenericTld(String gTld) { && !arrayContains(genericTLDsMinus, key); } /** - * Returns true if the specified String matches any - * IANA-defined country code top-level domain. Leading dots are - * ignored if present. The search is case-insensitive. + * Returns true if the specified String matches any IANA-defined country code + * top-level domain. Leading dots are ignored if present. The search is case-insensitive. + * * @param ccTld the parameter to check for country code TLD status, not null * @return true if the parameter is a country code TLD */ @@ -224,9 +203,10 @@ public boolean isValidCountryCodeTld(String ccTld) { && !arrayContains(countryCodeTLDsMinus, key); } /** - * Returns true if the specified String matches any - * widely used "local" domains (localhost or localdomain). Leading dots are - * ignored if present. The search is case-insensitive. + * Returns true if the specified String matches any widely used "local" domains + * (localhost or localdomain). Leading dots are ignored if present. The search is + * case-insensitive. + * * @param lTld the parameter to check for local TLD status, not null * @return true if the parameter is an local TLD */ @@ -234,6 +214,7 @@ public boolean isValidLocalTld(String lTld) { final String key = chompLeadingDot(unicodeToASCII(lTld).toLowerCase(Locale.ENGLISH)); return arrayContains(LOCAL_TLDS, key); } + private String chompLeadingDot(String str) { if (str.startsWith(".")) { return str.substring(1); @@ -252,1323 +233,1350 @@ private String chompLeadingDot(String str) { // For example (as of 2015-01-02): // .bl country-code Not assigned // .um country-code Not assigned - // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search - private static final String[] INFRASTRUCTURE_TLDS = new String[] { - "arpa", // internet infrastructure - }; - // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search - private static final String[] GENERIC_TLDS = new String[] { - // Taken from Version 2016042500, Last Updated Mon Apr 25 07:07:01 2016 UTC - "aaa", // aaa American Automobile Association, Inc. - "aarp", // aarp AARP - "abb", // abb ABB Ltd - "abbott", // abbott Abbott Laboratories, Inc. - "abbvie", // abbvie AbbVie Inc. - "abogado", // abogado Top Level Domain Holdings Limited - "abudhabi", // abudhabi Abu Dhabi Systems and Information Centre - "academy", // academy Half Oaks, LLC - "accenture", // accenture Accenture plc - "accountant", // accountant dot Accountant Limited - "accountants", // accountants Knob Town, LLC - "aco", // aco ACO Severin Ahlmann GmbH & Co. KG - "active", // active The Active Network, Inc - "actor", // actor United TLD Holdco Ltd. - "adac", // adac Allgemeiner Deutscher Automobil-Club e.V. (ADAC) - "ads", // ads Charleston Road Registry Inc. - "adult", // adult ICM Registry AD LLC - "aeg", // aeg Aktiebolaget Electrolux - "aero", // aero Societe Internationale de Telecommunications Aeronautique (SITA INC USA) - "afl", // afl Australian Football League - "agakhan", // agakhan Fondation Aga Khan (Aga Khan Foundation) - "agency", // agency Steel Falls, LLC - "aig", // aig American International Group, Inc. - "airforce", // airforce United TLD Holdco Ltd. - "airtel", // airtel Bharti Airtel Limited - "akdn", // akdn Fondation Aga Khan (Aga Khan Foundation) - "alibaba", // alibaba Alibaba Group Holding Limited - "alipay", // alipay Alibaba Group Holding Limited - "allfinanz", // allfinanz Allfinanz Deutsche Vermögensberatung Aktiengesellschaft - "ally", // ally Ally Financial Inc. - "alsace", // alsace REGION D ALSACE - "amica", // amica Amica Mutual Insurance Company - "amsterdam", // amsterdam Gemeente Amsterdam - "analytics", // analytics Campus IP LLC - "android", // android Charleston Road Registry Inc. - "anquan", // anquan QIHOO 360 TECHNOLOGY CO. LTD. - "apartments", // apartments June Maple, LLC - "app", // app Charleston Road Registry Inc. - "apple", // apple Apple Inc. - "aquarelle", // aquarelle Aquarelle.com - "aramco", // aramco Aramco Services Company - "archi", // archi STARTING DOT LIMITED - "army", // army United TLD Holdco Ltd. - "arte", // arte Association Relative à la Télévision Européenne G.E.I.E. - "asia", // asia DotAsia Organisation Ltd. - "associates", // associates Baxter Hill, LLC - "attorney", // attorney United TLD Holdco, Ltd - "auction", // auction United TLD HoldCo, Ltd. - "audi", // audi AUDI Aktiengesellschaft - "audio", // audio Uniregistry, Corp. - "author", // author Amazon Registry Services, Inc. - "auto", // auto Uniregistry, Corp. - "autos", // autos DERAutos, LLC - "avianca", // avianca Aerovias del Continente Americano S.A. Avianca - "aws", // aws Amazon Registry Services, Inc. - "axa", // axa AXA SA - "azure", // azure Microsoft Corporation - "baby", // baby Johnson & Johnson Services, Inc. - "baidu", // baidu Baidu, Inc. - "band", // band United TLD Holdco, Ltd - "bank", // bank fTLD Registry Services, LLC - "bar", // bar Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable - "barcelona", // barcelona Municipi de Barcelona - "barclaycard", // barclaycard Barclays Bank PLC - "barclays", // barclays Barclays Bank PLC - "barefoot", // barefoot Gallo Vineyards, Inc. - "bargains", // bargains Half Hallow, LLC - "bauhaus", // bauhaus Werkhaus GmbH - "bayern", // bayern Bayern Connect GmbH - "bbc", // bbc British Broadcasting Corporation - "bbva", // bbva BANCO BILBAO VIZCAYA ARGENTARIA, S.A. - "bcg", // bcg The Boston Consulting Group, Inc. - "bcn", // bcn Municipi de Barcelona - "beats", // beats Beats Electronics, LLC - "beer", // beer Top Level Domain Holdings Limited - "bentley", // bentley Bentley Motors Limited - "berlin", // berlin dotBERLIN GmbH & Co. KG - "best", // best BestTLD Pty Ltd - "bet", // bet Afilias plc - "bharti", // bharti Bharti Enterprises (Holding) Private Limited - "bible", // bible American Bible Society - "bid", // bid dot Bid Limited - "bike", // bike Grand Hollow, LLC - "bing", // bing Microsoft Corporation - "bingo", // bingo Sand Cedar, LLC - "bio", // bio STARTING DOT LIMITED - "biz", // biz Neustar, Inc. - "black", // black Afilias Limited - "blackfriday", // blackfriday Uniregistry, Corp. - "bloomberg", // bloomberg Bloomberg IP Holdings LLC - "blue", // blue Afilias Limited - "bms", // bms Bristol-Myers Squibb Company - "bmw", // bmw Bayerische Motoren Werke Aktiengesellschaft - "bnl", // bnl Banca Nazionale del Lavoro - "bnpparibas", // bnpparibas BNP Paribas - "boats", // boats DERBoats, LLC - "boehringer", // boehringer Boehringer Ingelheim International GmbH - "bom", // bom Núcleo de Informação e Coordenação do Ponto BR - NIC.br - "bond", // bond Bond University Limited - "boo", // boo Charleston Road Registry Inc. - "book", // book Amazon Registry Services, Inc. - "boots", // boots THE BOOTS COMPANY PLC - "bosch", // bosch Robert Bosch GMBH - "bostik", // bostik Bostik SA - "bot", // bot Amazon Registry Services, Inc. - "boutique", // boutique Over Galley, LLC - "bradesco", // bradesco Banco Bradesco S.A. - "bridgestone", // bridgestone Bridgestone Corporation - "broadway", // broadway Celebrate Broadway, Inc. - "broker", // broker DOTBROKER REGISTRY LTD - "brother", // brother Brother Industries, Ltd. - "brussels", // brussels DNS.be vzw - "budapest", // budapest Top Level Domain Holdings Limited - "bugatti", // bugatti Bugatti International SA - "build", // build Plan Bee LLC - "builders", // builders Atomic Madison, LLC - "business", // business Spring Cross, LLC - "buy", // buy Amazon Registry Services, INC - "buzz", // buzz DOTSTRATEGY CO. - "bzh", // bzh Association www.bzh - "cab", // cab Half Sunset, LLC - "cafe", // cafe Pioneer Canyon, LLC - "cal", // cal Charleston Road Registry Inc. - "call", // call Amazon Registry Services, Inc. - "camera", // camera Atomic Maple, LLC - "camp", // camp Delta Dynamite, LLC - "cancerresearch", // cancerresearch Australian Cancer Research Foundation - "canon", // canon Canon Inc. - "capetown", // capetown ZA Central Registry NPC trading as ZA Central Registry - "capital", // capital Delta Mill, LLC - "car", // car Cars Registry Limited - "caravan", // caravan Caravan International, Inc. - "cards", // cards Foggy Hollow, LLC - "care", // care Goose Cross, LLC - "career", // career dotCareer LLC - "careers", // careers Wild Corner, LLC - "cars", // cars Uniregistry, Corp. - "cartier", // cartier Richemont DNS Inc. - "casa", // casa Top Level Domain Holdings Limited - "cash", // cash Delta Lake, LLC - "casino", // casino Binky Sky, LLC - "cat", // cat Fundacio puntCAT - "catering", // catering New Falls. LLC - "cba", // cba COMMONWEALTH BANK OF AUSTRALIA - "cbn", // cbn The Christian Broadcasting Network, Inc. - "ceb", // ceb The Corporate Executive Board Company - "center", // center Tin Mill, LLC - "ceo", // ceo CEOTLD Pty Ltd - "cern", // cern European Organization for Nuclear Research ("CERN") - "cfa", // cfa CFA Institute - "cfd", // cfd DOTCFD REGISTRY LTD - "chanel", // chanel Chanel International B.V. - "channel", // channel Charleston Road Registry Inc. - "chase", // chase JPMorgan Chase & Co. - "chat", // chat Sand Fields, LLC - "cheap", // cheap Sand Cover, LLC - "chloe", // chloe Richemont DNS Inc. - "christmas", // christmas Uniregistry, Corp. - "chrome", // chrome Charleston Road Registry Inc. - "church", // church Holly Fileds, LLC - "cipriani", // cipriani Hotel Cipriani Srl - "circle", // circle Amazon Registry Services, Inc. - "cisco", // cisco Cisco Technology, Inc. - "citic", // citic CITIC Group Corporation - "city", // city Snow Sky, LLC - "cityeats", // cityeats Lifestyle Domain Holdings, Inc. - "claims", // claims Black Corner, LLC - "cleaning", // cleaning Fox Shadow, LLC - "click", // click Uniregistry, Corp. - "clinic", // clinic Goose Park, LLC - "clinique", // clinique The Estée Lauder Companies Inc. - "clothing", // clothing Steel Lake, LLC - "cloud", // cloud ARUBA S.p.A. - "club", // club .CLUB DOMAINS, LLC - "clubmed", // clubmed Club Méditerranée S.A. - "coach", // coach Koko Island, LLC - "codes", // codes Puff Willow, LLC - "coffee", // coffee Trixy Cover, LLC - "college", // college XYZ.COM LLC - "cologne", // cologne NetCologne Gesellschaft für Telekommunikation mbH - "com", // com VeriSign Global Registry Services - "commbank", // commbank COMMONWEALTH BANK OF AUSTRALIA - "community", // community Fox Orchard, LLC - "company", // company Silver Avenue, LLC - "compare", // compare iSelect Ltd - "computer", // computer Pine Mill, LLC - "comsec", // comsec VeriSign, Inc. - "condos", // condos Pine House, LLC - "construction", // construction Fox Dynamite, LLC - "consulting", // consulting United TLD Holdco, LTD. - "contact", // contact Top Level Spectrum, Inc. - "contractors", // contractors Magic Woods, LLC - "cooking", // cooking Top Level Domain Holdings Limited - "cool", // cool Koko Lake, LLC - "coop", // coop DotCooperation LLC - "corsica", // corsica Collectivité Territoriale de Corse - "country", // country Top Level Domain Holdings Limited - "coupon", // coupon Amazon Registry Services, Inc. - "coupons", // coupons Black Island, LLC - "courses", // courses OPEN UNIVERSITIES AUSTRALIA PTY LTD - "credit", // credit Snow Shadow, LLC - "creditcard", // creditcard Binky Frostbite, LLC - "creditunion", // creditunion CUNA Performance Resources, LLC - "cricket", // cricket dot Cricket Limited - "crown", // crown Crown Equipment Corporation - "crs", // crs Federated Co-operatives Limited - "cruises", // cruises Spring Way, LLC - "csc", // csc Alliance-One Services, Inc. - "cuisinella", // cuisinella SALM S.A.S. - "cymru", // cymru Nominet UK - "cyou", // cyou Beijing Gamease Age Digital Technology Co., Ltd. - "dabur", // dabur Dabur India Limited - "dad", // dad Charleston Road Registry Inc. - "dance", // dance United TLD Holdco Ltd. - "date", // date dot Date Limited - "dating", // dating Pine Fest, LLC - "datsun", // datsun NISSAN MOTOR CO., LTD. - "day", // day Charleston Road Registry Inc. - "dclk", // dclk Charleston Road Registry Inc. - "dealer", // dealer Dealer Dot Com, Inc. - "deals", // deals Sand Sunset, LLC - "degree", // degree United TLD Holdco, Ltd - "delivery", // delivery Steel Station, LLC - "dell", // dell Dell Inc. - "deloitte", // deloitte Deloitte Touche Tohmatsu - "delta", // delta Delta Air Lines, Inc. - "democrat", // democrat United TLD Holdco Ltd. - "dental", // dental Tin Birch, LLC - "dentist", // dentist United TLD Holdco, Ltd - "desi", // desi Desi Networks LLC - "design", // design Top Level Design, LLC - "dev", // dev Charleston Road Registry Inc. - "diamonds", // diamonds John Edge, LLC - "diet", // diet Uniregistry, Corp. - "digital", // digital Dash Park, LLC - "direct", // direct Half Trail, LLC - "directory", // directory Extra Madison, LLC - "discount", // discount Holly Hill, LLC - "dnp", // dnp Dai Nippon Printing Co., Ltd. - "docs", // docs Charleston Road Registry Inc. - "dog", // dog Koko Mill, LLC - "doha", // doha Communications Regulatory Authority (CRA) - "domains", // domains Sugar Cross, LLC -// "doosan", // doosan Doosan Corporation (retired) - "download", // download dot Support Limited - "drive", // drive Charleston Road Registry Inc. - "dubai", // dubai Dubai Smart Government Department - "durban", // durban ZA Central Registry NPC trading as ZA Central Registry - "dvag", // dvag Deutsche Vermögensberatung Aktiengesellschaft DVAG - "earth", // earth Interlink Co., Ltd. - "eat", // eat Charleston Road Registry Inc. - "edeka", // edeka EDEKA Verband kaufmännischer Genossenschaften e.V. - "edu", // edu EDUCAUSE - "education", // education Brice Way, LLC - "email", // email Spring Madison, LLC - "emerck", // emerck Merck KGaA - "energy", // energy Binky Birch, LLC - "engineer", // engineer United TLD Holdco Ltd. - "engineering", // engineering Romeo Canyon - "enterprises", // enterprises Snow Oaks, LLC - "epson", // epson Seiko Epson Corporation - "equipment", // equipment Corn Station, LLC - "erni", // erni ERNI Group Holding AG - "esq", // esq Charleston Road Registry Inc. - "estate", // estate Trixy Park, LLC - "eurovision", // eurovision European Broadcasting Union (EBU) - "eus", // eus Puntueus Fundazioa - "events", // events Pioneer Maple, LLC - "everbank", // everbank EverBank - "exchange", // exchange Spring Falls, LLC - "expert", // expert Magic Pass, LLC - "exposed", // exposed Victor Beach, LLC - "express", // express Sea Sunset, LLC - "extraspace", // extraspace Extra Space Storage LLC - "fage", // fage Fage International S.A. - "fail", // fail Atomic Pipe, LLC - "fairwinds", // fairwinds FairWinds Partners, LLC - "faith", // faith dot Faith Limited - "family", // family United TLD Holdco Ltd. - "fan", // fan Asiamix Digital Ltd - "fans", // fans Asiamix Digital Limited - "farm", // farm Just Maple, LLC - "fashion", // fashion Top Level Domain Holdings Limited - "fast", // fast Amazon Registry Services, Inc. - "feedback", // feedback Top Level Spectrum, Inc. - "ferrero", // ferrero Ferrero Trading Lux S.A. - "film", // film Motion Picture Domain Registry Pty Ltd - "final", // final Núcleo de Informação e Coordenação do Ponto BR - NIC.br - "finance", // finance Cotton Cypress, LLC - "financial", // financial Just Cover, LLC - "firestone", // firestone Bridgestone Corporation - "firmdale", // firmdale Firmdale Holdings Limited - "fish", // fish Fox Woods, LLC - "fishing", // fishing Top Level Domain Holdings Limited - "fit", // fit Minds + Machines Group Limited - "fitness", // fitness Brice Orchard, LLC - "flickr", // flickr Yahoo! Domain Services Inc. - "flights", // flights Fox Station, LLC - "florist", // florist Half Cypress, LLC - "flowers", // flowers Uniregistry, Corp. - "flsmidth", // flsmidth FLSmidth A/S - "fly", // fly Charleston Road Registry Inc. - "foo", // foo Charleston Road Registry Inc. - "football", // football Foggy Farms, LLC - "ford", // ford Ford Motor Company - "forex", // forex DOTFOREX REGISTRY LTD - "forsale", // forsale United TLD Holdco, LLC - "forum", // forum Fegistry, LLC - "foundation", // foundation John Dale, LLC - "fox", // fox FOX Registry, LLC - "fresenius", // fresenius Fresenius Immobilien-Verwaltungs-GmbH - "frl", // frl FRLregistry B.V. - "frogans", // frogans OP3FT - "frontier", // frontier Frontier Communications Corporation - "ftr", // ftr Frontier Communications Corporation - "fund", // fund John Castle, LLC - "furniture", // furniture Lone Fields, LLC - "futbol", // futbol United TLD Holdco, Ltd. - "fyi", // fyi Silver Tigers, LLC - "gal", // gal Asociación puntoGAL - "gallery", // gallery Sugar House, LLC - "gallo", // gallo Gallo Vineyards, Inc. - "gallup", // gallup Gallup, Inc. - "game", // game Uniregistry, Corp. - "garden", // garden Top Level Domain Holdings Limited - "gbiz", // gbiz Charleston Road Registry Inc. - "gdn", // gdn Joint Stock Company "Navigation-information systems" - "gea", // gea GEA Group Aktiengesellschaft - "gent", // gent COMBELL GROUP NV/SA - "genting", // genting Resorts World Inc. Pte. Ltd. - "ggee", // ggee GMO Internet, Inc. - "gift", // gift Uniregistry, Corp. - "gifts", // gifts Goose Sky, LLC - "gives", // gives United TLD Holdco Ltd. - "giving", // giving Giving Limited - "glass", // glass Black Cover, LLC - "gle", // gle Charleston Road Registry Inc. - "global", // global Dot Global Domain Registry Limited - "globo", // globo Globo Comunicação e Participações S.A - "gmail", // gmail Charleston Road Registry Inc. - "gmbh", // gmbh Extra Dynamite, LLC - "gmo", // gmo GMO Internet, Inc. - "gmx", // gmx 1&1 Mail & Media GmbH - "gold", // gold June Edge, LLC - "goldpoint", // goldpoint YODOBASHI CAMERA CO.,LTD. - "golf", // golf Lone Falls, LLC - "goo", // goo NTT Resonant Inc. - "goog", // goog Charleston Road Registry Inc. - "google", // google Charleston Road Registry Inc. - "gop", // gop Republican State Leadership Committee, Inc. - "got", // got Amazon Registry Services, Inc. - "gov", // gov General Services Administration Attn: QTDC, 2E08 (.gov Domain Registration) - "grainger", // grainger Grainger Registry Services, LLC - "graphics", // graphics Over Madison, LLC - "gratis", // gratis Pioneer Tigers, LLC - "green", // green Afilias Limited - "gripe", // gripe Corn Sunset, LLC - "group", // group Romeo Town, LLC - "gucci", // gucci Guccio Gucci S.p.a. - "guge", // guge Charleston Road Registry Inc. - "guide", // guide Snow Moon, LLC - "guitars", // guitars Uniregistry, Corp. - "guru", // guru Pioneer Cypress, LLC - "hamburg", // hamburg Hamburg Top-Level-Domain GmbH - "hangout", // hangout Charleston Road Registry Inc. - "haus", // haus United TLD Holdco, LTD. - "hdfcbank", // hdfcbank HDFC Bank Limited - "health", // health DotHealth, LLC - "healthcare", // healthcare Silver Glen, LLC - "help", // help Uniregistry, Corp. - "helsinki", // helsinki City of Helsinki - "here", // here Charleston Road Registry Inc. - "hermes", // hermes Hermes International - "hiphop", // hiphop Uniregistry, Corp. - "hitachi", // hitachi Hitachi, Ltd. - "hiv", // hiv dotHIV gemeinnuetziger e.V. - "hockey", // hockey Half Willow, LLC - "holdings", // holdings John Madison, LLC - "holiday", // holiday Goose Woods, LLC - "homedepot", // homedepot Homer TLC, Inc. - "homes", // homes DERHomes, LLC - "honda", // honda Honda Motor Co., Ltd. - "horse", // horse Top Level Domain Holdings Limited - "host", // host DotHost Inc. - "hosting", // hosting Uniregistry, Corp. - "hoteles", // hoteles Travel Reservations SRL - "hotmail", // hotmail Microsoft Corporation - "house", // house Sugar Park, LLC - "how", // how Charleston Road Registry Inc. - "hsbc", // hsbc HSBC Holdings PLC - "htc", // htc HTC corporation - "hyundai", // hyundai Hyundai Motor Company - "ibm", // ibm International Business Machines Corporation - "icbc", // icbc Industrial and Commercial Bank of China Limited - "ice", // ice IntercontinentalExchange, Inc. - "icu", // icu One.com A/S - "ifm", // ifm ifm electronic gmbh - "iinet", // iinet Connect West Pty. Ltd. - "imamat", // imamat Fondation Aga Khan (Aga Khan Foundation) - "immo", // immo Auburn Bloom, LLC - "immobilien", // immobilien United TLD Holdco Ltd. - "industries", // industries Outer House, LLC - "infiniti", // infiniti NISSAN MOTOR CO., LTD. - "info", // info Afilias Limited - "ing", // ing Charleston Road Registry Inc. - "ink", // ink Top Level Design, LLC - "institute", // institute Outer Maple, LLC - "insurance", // insurance fTLD Registry Services LLC - "insure", // insure Pioneer Willow, LLC - "int", // int Internet Assigned Numbers Authority - "international", // international Wild Way, LLC - "investments", // investments Holly Glen, LLC - "ipiranga", // ipiranga Ipiranga Produtos de Petroleo S.A. - "irish", // irish Dot-Irish LLC - "iselect", // iselect iSelect Ltd - "ismaili", // ismaili Fondation Aga Khan (Aga Khan Foundation) - "ist", // ist Istanbul Metropolitan Municipality - "istanbul", // istanbul Istanbul Metropolitan Municipality / Medya A.S. - "itau", // itau Itau Unibanco Holding S.A. - "iwc", // iwc Richemont DNS Inc. - "jaguar", // jaguar Jaguar Land Rover Ltd - "java", // java Oracle Corporation - "jcb", // jcb JCB Co., Ltd. - "jcp", // jcp JCP Media, Inc. - "jetzt", // jetzt New TLD Company AB - "jewelry", // jewelry Wild Bloom, LLC - "jlc", // jlc Richemont DNS Inc. - "jll", // jll Jones Lang LaSalle Incorporated - "jmp", // jmp Matrix IP LLC - "jnj", // jnj Johnson & Johnson Services, Inc. - "jobs", // jobs Employ Media LLC - "joburg", // joburg ZA Central Registry NPC trading as ZA Central Registry - "jot", // jot Amazon Registry Services, Inc. - "joy", // joy Amazon Registry Services, Inc. - "jpmorgan", // jpmorgan JPMorgan Chase & Co. - "jprs", // jprs Japan Registry Services Co., Ltd. - "juegos", // juegos Uniregistry, Corp. - "kaufen", // kaufen United TLD Holdco Ltd. - "kddi", // kddi KDDI CORPORATION - "kerryhotels", // kerryhotels Kerry Trading Co. Limited - "kerrylogistics", // kerrylogistics Kerry Trading Co. Limited - "kerryproperties", // kerryproperties Kerry Trading Co. Limited - "kfh", // kfh Kuwait Finance House - "kia", // kia KIA MOTORS CORPORATION - "kim", // kim Afilias Limited - "kinder", // kinder Ferrero Trading Lux S.A. - "kitchen", // kitchen Just Goodbye, LLC - "kiwi", // kiwi DOT KIWI LIMITED - "koeln", // koeln NetCologne Gesellschaft für Telekommunikation mbH - "komatsu", // komatsu Komatsu Ltd. - "kpmg", // kpmg KPMG International Cooperative (KPMG International Genossenschaft) - "kpn", // kpn Koninklijke KPN N.V. - "krd", // krd KRG Department of Information Technology - "kred", // kred KredTLD Pty Ltd - "kuokgroup", // kuokgroup Kerry Trading Co. Limited - "kyoto", // kyoto Academic Institution: Kyoto Jyoho Gakuen - "lacaixa", // lacaixa CAIXA D'ESTALVIS I PENSIONS DE BARCELONA - "lamborghini", // lamborghini Automobili Lamborghini S.p.A. - "lamer", // lamer The Estée Lauder Companies Inc. - "lancaster", // lancaster LANCASTER - "land", // land Pine Moon, LLC - "landrover", // landrover Jaguar Land Rover Ltd - "lanxess", // lanxess LANXESS Corporation - "lasalle", // lasalle Jones Lang LaSalle Incorporated - "lat", // lat ECOM-LAC Federación de Latinoamérica y el Caribe para Internet y el Comercio Electrónico - "latrobe", // latrobe La Trobe University - "law", // law Minds + Machines Group Limited - "lawyer", // lawyer United TLD Holdco, Ltd - "lds", // lds IRI Domain Management, LLC - "lease", // lease Victor Trail, LLC - "leclerc", // leclerc A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc - "legal", // legal Blue Falls, LLC - "lexus", // lexus TOYOTA MOTOR CORPORATION - "lgbt", // lgbt Afilias Limited - "liaison", // liaison Liaison Technologies, Incorporated - "lidl", // lidl Schwarz Domains und Services GmbH & Co. KG - "life", // life Trixy Oaks, LLC - "lifeinsurance", // lifeinsurance American Council of Life Insurers - "lifestyle", // lifestyle Lifestyle Domain Holdings, Inc. - "lighting", // lighting John McCook, LLC - "like", // like Amazon Registry Services, Inc. - "limited", // limited Big Fest, LLC - "limo", // limo Hidden Frostbite, LLC - "lincoln", // lincoln Ford Motor Company - "linde", // linde Linde Aktiengesellschaft - "link", // link Uniregistry, Corp. - "live", // live United TLD Holdco Ltd. - "living", // living Lifestyle Domain Holdings, Inc. - "lixil", // lixil LIXIL Group Corporation - "loan", // loan dot Loan Limited - "loans", // loans June Woods, LLC - "locus", // locus Locus Analytics LLC - "lol", // lol Uniregistry, Corp. - "london", // london Dot London Domains Limited - "lotte", // lotte Lotte Holdings Co., Ltd. - "lotto", // lotto Afilias Limited - "love", // love Merchant Law Group LLP - "ltd", // ltd Over Corner, LLC - "ltda", // ltda InterNetX Corp. - "lupin", // lupin LUPIN LIMITED - "luxe", // luxe Top Level Domain Holdings Limited - "luxury", // luxury Luxury Partners LLC - "madrid", // madrid Comunidad de Madrid - "maif", // maif Mutuelle Assurance Instituteur France (MAIF) - "maison", // maison Victor Frostbite, LLC - "makeup", // makeup L'Oréal - "man", // man MAN SE - "management", // management John Goodbye, LLC - "mango", // mango PUNTO FA S.L. - "market", // market Unitied TLD Holdco, Ltd - "marketing", // marketing Fern Pass, LLC - "markets", // markets DOTMARKETS REGISTRY LTD - "marriott", // marriott Marriott Worldwide Corporation - "mba", // mba Lone Hollow, LLC - "med", // med Medistry LLC - "media", // media Grand Glen, LLC - "meet", // meet Afilias Limited - "melbourne", // melbourne The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation - "meme", // meme Charleston Road Registry Inc. - "memorial", // memorial Dog Beach, LLC - "men", // men Exclusive Registry Limited - "menu", // menu Wedding TLD2, LLC - "meo", // meo PT Comunicacoes S.A. - "miami", // miami Top Level Domain Holdings Limited - "microsoft", // microsoft Microsoft Corporation - "mil", // mil DoD Network Information Center - "mini", // mini Bayerische Motoren Werke Aktiengesellschaft - "mls", // mls The Canadian Real Estate Association - "mma", // mma MMA IARD - "mobi", // mobi Afilias Technologies Limited dba dotMobi - "mobily", // mobily GreenTech Consultancy Company W.L.L. - "moda", // moda United TLD Holdco Ltd. - "moe", // moe Interlink Co., Ltd. - "moi", // moi Amazon Registry Services, Inc. - "mom", // mom Uniregistry, Corp. - "monash", // monash Monash University - "money", // money Outer McCook, LLC - "montblanc", // montblanc Richemont DNS Inc. - "mormon", // mormon IRI Domain Management, LLC ("Applicant") - "mortgage", // mortgage United TLD Holdco, Ltd - "moscow", // moscow Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) - "motorcycles", // motorcycles DERMotorcycles, LLC - "mov", // mov Charleston Road Registry Inc. - "movie", // movie New Frostbite, LLC - "movistar", // movistar Telefónica S.A. - "mtn", // mtn MTN Dubai Limited - "mtpc", // mtpc Mitsubishi Tanabe Pharma Corporation - "mtr", // mtr MTR Corporation Limited - "museum", // museum Museum Domain Management Association - "mutual", // mutual Northwestern Mutual MU TLD Registry, LLC - "mutuelle", // mutuelle Fédération Nationale de la Mutualité Française - "nadex", // nadex Nadex Domains, Inc - "nagoya", // nagoya GMO Registry, Inc. - "name", // name VeriSign Information Services, Inc. - "natura", // natura NATURA COSMÉTICOS S.A. - "navy", // navy United TLD Holdco Ltd. - "nec", // nec NEC Corporation - "net", // net VeriSign Global Registry Services - "netbank", // netbank COMMONWEALTH BANK OF AUSTRALIA - "network", // network Trixy Manor, LLC - "neustar", // neustar NeuStar, Inc. - "new", // new Charleston Road Registry Inc. - "news", // news United TLD Holdco Ltd. - "nexus", // nexus Charleston Road Registry Inc. - "ngo", // ngo Public Interest Registry - "nhk", // nhk Japan Broadcasting Corporation (NHK) - "nico", // nico DWANGO Co., Ltd. - "nikon", // nikon NIKON CORPORATION - "ninja", // ninja United TLD Holdco Ltd. - "nissan", // nissan NISSAN MOTOR CO., LTD. - "nissay", // nissay Nippon Life Insurance Company - "nokia", // nokia Nokia Corporation - "northwesternmutual", // northwesternmutual Northwestern Mutual Registry, LLC - "norton", // norton Symantec Corporation - "nowruz", // nowruz Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. - "nra", // nra NRA Holdings Company, INC. - "nrw", // nrw Minds + Machines GmbH - "ntt", // ntt NIPPON TELEGRAPH AND TELEPHONE CORPORATION - "nyc", // nyc The City of New York by and through the New York City Department of Information Technology & Telecommunications - "obi", // obi OBI Group Holding SE & Co. KGaA - "office", // office Microsoft Corporation - "okinawa", // okinawa BusinessRalliart inc. - "omega", // omega The Swatch Group Ltd - "one", // one One.com A/S - "ong", // ong Public Interest Registry - "onl", // onl I-REGISTRY Ltd., Niederlassung Deutschland - "online", // online DotOnline Inc. - "ooo", // ooo INFIBEAM INCORPORATION LIMITED - "oracle", // oracle Oracle Corporation - "orange", // orange Orange Brand Services Limited - "org", // org Public Interest Registry (PIR) - "organic", // organic Afilias Limited - "origins", // origins The Estée Lauder Companies Inc. - "osaka", // osaka Interlink Co., Ltd. - "otsuka", // otsuka Otsuka Holdings Co., Ltd. - "ovh", // ovh OVH SAS - "page", // page Charleston Road Registry Inc. - "pamperedchef", // pamperedchef The Pampered Chef, Ltd. - "panerai", // panerai Richemont DNS Inc. - "paris", // paris City of Paris - "pars", // pars Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. - "partners", // partners Magic Glen, LLC - "parts", // parts Sea Goodbye, LLC - "party", // party Blue Sky Registry Limited - "passagens", // passagens Travel Reservations SRL - "pet", // pet Afilias plc - "pharmacy", // pharmacy National Association of Boards of Pharmacy - "philips", // philips Koninklijke Philips N.V. - "photo", // photo Uniregistry, Corp. - "photography", // photography Sugar Glen, LLC - "photos", // photos Sea Corner, LLC - "physio", // physio PhysBiz Pty Ltd - "piaget", // piaget Richemont DNS Inc. - "pics", // pics Uniregistry, Corp. - "pictet", // pictet Pictet Europe S.A. - "pictures", // pictures Foggy Sky, LLC - "pid", // pid Top Level Spectrum, Inc. - "pin", // pin Amazon Registry Services, Inc. - "ping", // ping Ping Registry Provider, Inc. - "pink", // pink Afilias Limited - "pizza", // pizza Foggy Moon, LLC - "place", // place Snow Galley, LLC - "play", // play Charleston Road Registry Inc. - "playstation", // playstation Sony Computer Entertainment Inc. - "plumbing", // plumbing Spring Tigers, LLC - "plus", // plus Sugar Mill, LLC - "pohl", // pohl Deutsche Vermögensberatung Aktiengesellschaft DVAG - "poker", // poker Afilias Domains No. 5 Limited - "porn", // porn ICM Registry PN LLC - "post", // post Universal Postal Union - "praxi", // praxi Praxi S.p.A. - "press", // press DotPress Inc. - "pro", // pro Registry Services Corporation dba RegistryPro - "prod", // prod Charleston Road Registry Inc. - "productions", // productions Magic Birch, LLC - "prof", // prof Charleston Road Registry Inc. - "progressive", // progressive Progressive Casualty Insurance Company - "promo", // promo Afilias plc - "properties", // properties Big Pass, LLC - "property", // property Uniregistry, Corp. - "protection", // protection XYZ.COM LLC - "pub", // pub United TLD Holdco Ltd. - "pwc", // pwc PricewaterhouseCoopers LLP - "qpon", // qpon dotCOOL, Inc. - "quebec", // quebec PointQuébec Inc - "quest", // quest Quest ION Limited - "racing", // racing Premier Registry Limited - "read", // read Amazon Registry Services, Inc. - "realtor", // realtor Real Estate Domains LLC - "realty", // realty Fegistry, LLC - "recipes", // recipes Grand Island, LLC - "red", // red Afilias Limited - "redstone", // redstone Redstone Haute Couture Co., Ltd. - "redumbrella", // redumbrella Travelers TLD, LLC - "rehab", // rehab United TLD Holdco Ltd. - "reise", // reise Foggy Way, LLC - "reisen", // reisen New Cypress, LLC - "reit", // reit National Association of Real Estate Investment Trusts, Inc. - "ren", // ren Beijing Qianxiang Wangjing Technology Development Co., Ltd. - "rent", // rent XYZ.COM LLC - "rentals", // rentals Big Hollow,LLC - "repair", // repair Lone Sunset, LLC - "report", // report Binky Glen, LLC - "republican", // republican United TLD Holdco Ltd. - "rest", // rest Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable - "restaurant", // restaurant Snow Avenue, LLC - "review", // review dot Review Limited - "reviews", // reviews United TLD Holdco, Ltd. - "rexroth", // rexroth Robert Bosch GMBH - "rich", // rich I-REGISTRY Ltd., Niederlassung Deutschland - "ricoh", // ricoh Ricoh Company, Ltd. - "rio", // rio Empresa Municipal de Informática SA - IPLANRIO - "rip", // rip United TLD Holdco Ltd. - "rocher", // rocher Ferrero Trading Lux S.A. - "rocks", // rocks United TLD Holdco, LTD. - "rodeo", // rodeo Top Level Domain Holdings Limited - "room", // room Amazon Registry Services, Inc. - "rsvp", // rsvp Charleston Road Registry Inc. - "ruhr", // ruhr regiodot GmbH & Co. KG - "run", // run Snow Park, LLC - "rwe", // rwe RWE AG - "ryukyu", // ryukyu BusinessRalliart inc. - "saarland", // saarland dotSaarland GmbH - "safe", // safe Amazon Registry Services, Inc. - "safety", // safety Safety Registry Services, LLC. - "sakura", // sakura SAKURA Internet Inc. - "sale", // sale United TLD Holdco, Ltd - "salon", // salon Outer Orchard, LLC - "samsung", // samsung SAMSUNG SDS CO., LTD - "sandvik", // sandvik Sandvik AB - "sandvikcoromant", // sandvikcoromant Sandvik AB - "sanofi", // sanofi Sanofi - "sap", // sap SAP AG - "sapo", // sapo PT Comunicacoes S.A. - "sarl", // sarl Delta Orchard, LLC - "sas", // sas Research IP LLC - "saxo", // saxo Saxo Bank A/S - "sbi", // sbi STATE BANK OF INDIA - "sbs", // sbs SPECIAL BROADCASTING SERVICE CORPORATION - "sca", // sca SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ) - "scb", // scb The Siam Commercial Bank Public Company Limited ("SCB") - "schaeffler", // schaeffler Schaeffler Technologies AG & Co. KG - "schmidt", // schmidt SALM S.A.S. - "scholarships", // scholarships Scholarships.com, LLC - "school", // school Little Galley, LLC - "schule", // schule Outer Moon, LLC - "schwarz", // schwarz Schwarz Domains und Services GmbH & Co. KG - "science", // science dot Science Limited - "scor", // scor SCOR SE - "scot", // scot Dot Scot Registry Limited - "seat", // seat SEAT, S.A. (Sociedad Unipersonal) - "security", // security XYZ.COM LLC - "seek", // seek Seek Limited - "select", // select iSelect Ltd - "sener", // sener Sener Ingeniería y Sistemas, S.A. - "services", // services Fox Castle, LLC - "seven", // seven Seven West Media Ltd - "sew", // sew SEW-EURODRIVE GmbH & Co KG - "sex", // sex ICM Registry SX LLC - "sexy", // sexy Uniregistry, Corp. - "sfr", // sfr Societe Francaise du Radiotelephone - SFR - "sharp", // sharp Sharp Corporation - "shaw", // shaw Shaw Cablesystems G.P. - "shell", // shell Shell Information Technology International Inc - "shia", // shia Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. - "shiksha", // shiksha Afilias Limited - "shoes", // shoes Binky Galley, LLC - "shouji", // shouji QIHOO 360 TECHNOLOGY CO. LTD. - "show", // show Snow Beach, LLC - "shriram", // shriram Shriram Capital Ltd. - "sina", // sina Sina Corporation - "singles", // singles Fern Madison, LLC - "site", // site DotSite Inc. - "ski", // ski STARTING DOT LIMITED - "skin", // skin L'Oréal - "sky", // sky Sky International AG - "skype", // skype Microsoft Corporation - "smile", // smile Amazon Registry Services, Inc. - "sncf", // sncf SNCF (Société Nationale des Chemins de fer Francais) - "soccer", // soccer Foggy Shadow, LLC - "social", // social United TLD Holdco Ltd. - "softbank", // softbank SoftBank Group Corp. - "software", // software United TLD Holdco, Ltd - "sohu", // sohu Sohu.com Limited - "solar", // solar Ruby Town, LLC - "solutions", // solutions Silver Cover, LLC - "song", // song Amazon Registry Services, Inc. - "sony", // sony Sony Corporation - "soy", // soy Charleston Road Registry Inc. - "space", // space DotSpace Inc. - "spiegel", // spiegel SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG - "spot", // spot Amazon Registry Services, Inc. - "spreadbetting", // spreadbetting DOTSPREADBETTING REGISTRY LTD - "srl", // srl InterNetX Corp. - "stada", // stada STADA Arzneimittel AG - "star", // star Star India Private Limited - "starhub", // starhub StarHub Limited - "statebank", // statebank STATE BANK OF INDIA - "statefarm", // statefarm State Farm Mutual Automobile Insurance Company - "statoil", // statoil Statoil ASA - "stc", // stc Saudi Telecom Company - "stcgroup", // stcgroup Saudi Telecom Company - "stockholm", // stockholm Stockholms kommun - "storage", // storage Self Storage Company LLC - "store", // store DotStore Inc. - "stream", // stream dot Stream Limited - "studio", // studio United TLD Holdco Ltd. - "study", // study OPEN UNIVERSITIES AUSTRALIA PTY LTD - "style", // style Binky Moon, LLC - "sucks", // sucks Vox Populi Registry Ltd. - "supplies", // supplies Atomic Fields, LLC - "supply", // supply Half Falls, LLC - "support", // support Grand Orchard, LLC - "surf", // surf Top Level Domain Holdings Limited - "surgery", // surgery Tin Avenue, LLC - "suzuki", // suzuki SUZUKI MOTOR CORPORATION - "swatch", // swatch The Swatch Group Ltd - "swiss", // swiss Swiss Confederation - "sydney", // sydney State of New South Wales, Department of Premier and Cabinet - "symantec", // symantec Symantec Corporation - "systems", // systems Dash Cypress, LLC - "tab", // tab Tabcorp Holdings Limited - "taipei", // taipei Taipei City Government - "talk", // talk Amazon Registry Services, Inc. - "taobao", // taobao Alibaba Group Holding Limited - "tatamotors", // tatamotors Tata Motors Ltd - "tatar", // tatar Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic" - "tattoo", // tattoo Uniregistry, Corp. - "tax", // tax Storm Orchard, LLC - "taxi", // taxi Pine Falls, LLC - "tci", // tci Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. - "team", // team Atomic Lake, LLC - "tech", // tech Dot Tech LLC - "technology", // technology Auburn Falls, LLC - "tel", // tel Telnic Ltd. - "telecity", // telecity TelecityGroup International Limited - "telefonica", // telefonica Telefónica S.A. - "temasek", // temasek Temasek Holdings (Private) Limited - "tennis", // tennis Cotton Bloom, LLC - "teva", // teva Teva Pharmaceutical Industries Limited - "thd", // thd Homer TLC, Inc. - "theater", // theater Blue Tigers, LLC - "theatre", // theatre XYZ.COM LLC - "tickets", // tickets Accent Media Limited - "tienda", // tienda Victor Manor, LLC - "tiffany", // tiffany Tiffany and Company - "tips", // tips Corn Willow, LLC - "tires", // tires Dog Edge, LLC - "tirol", // tirol punkt Tirol GmbH - "tmall", // tmall Alibaba Group Holding Limited - "today", // today Pearl Woods, LLC - "tokyo", // tokyo GMO Registry, Inc. - "tools", // tools Pioneer North, LLC - "top", // top Jiangsu Bangning Science & Technology Co.,Ltd. - "toray", // toray Toray Industries, Inc. - "toshiba", // toshiba TOSHIBA Corporation - "total", // total Total SA - "tours", // tours Sugar Station, LLC - "town", // town Koko Moon, LLC - "toyota", // toyota TOYOTA MOTOR CORPORATION - "toys", // toys Pioneer Orchard, LLC - "trade", // trade Elite Registry Limited - "trading", // trading DOTTRADING REGISTRY LTD - "training", // training Wild Willow, LLC - "travel", // travel Tralliance Registry Management Company, LLC. - "travelers", // travelers Travelers TLD, LLC - "travelersinsurance", // travelersinsurance Travelers TLD, LLC - "trust", // trust Artemis Internet Inc - "trv", // trv Travelers TLD, LLC - "tube", // tube Latin American Telecom LLC - "tui", // tui TUI AG - "tunes", // tunes Amazon Registry Services, Inc. - "tushu", // tushu Amazon Registry Services, Inc. - "tvs", // tvs T V SUNDRAM IYENGAR & SONS PRIVATE LIMITED - "ubs", // ubs UBS AG - "unicom", // unicom China United Network Communications Corporation Limited - "university", // university Little Station, LLC - "uno", // uno Dot Latin LLC - "uol", // uol UBN INTERNET LTDA. - "vacations", // vacations Atomic Tigers, LLC - "vana", // vana Lifestyle Domain Holdings, Inc. - "vegas", // vegas Dot Vegas, Inc. - "ventures", // ventures Binky Lake, LLC - "verisign", // verisign VeriSign, Inc. - "versicherung", // versicherung dotversicherung-registry GmbH - "vet", // vet United TLD Holdco, Ltd - "viajes", // viajes Black Madison, LLC - "video", // video United TLD Holdco, Ltd - "vig", // vig VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe - "viking", // viking Viking River Cruises (Bermuda) Ltd. - "villas", // villas New Sky, LLC - "vin", // vin Holly Shadow, LLC - "vip", // vip Minds + Machines Group Limited - "virgin", // virgin Virgin Enterprises Limited - "vision", // vision Koko Station, LLC - "vista", // vista Vistaprint Limited - "vistaprint", // vistaprint Vistaprint Limited - "viva", // viva Saudi Telecom Company - "vlaanderen", // vlaanderen DNS.be vzw - "vodka", // vodka Top Level Domain Holdings Limited - "volkswagen", // volkswagen Volkswagen Group of America Inc. - "vote", // vote Monolith Registry LLC - "voting", // voting Valuetainment Corp. - "voto", // voto Monolith Registry LLC - "voyage", // voyage Ruby House, LLC - "vuelos", // vuelos Travel Reservations SRL - "wales", // wales Nominet UK - "walter", // walter Sandvik AB - "wang", // wang Zodiac Registry Limited - "wanggou", // wanggou Amazon Registry Services, Inc. - "watch", // watch Sand Shadow, LLC - "watches", // watches Richemont DNS Inc. - "weather", // weather The Weather Channel, LLC - "weatherchannel", // weatherchannel The Weather Channel, LLC - "webcam", // webcam dot Webcam Limited - "weber", // weber Saint-Gobain Weber SA - "website", // website DotWebsite Inc. - "wed", // wed Atgron, Inc. - "wedding", // wedding Top Level Domain Holdings Limited - "weibo", // weibo Sina Corporation - "weir", // weir Weir Group IP Limited - "whoswho", // whoswho Who's Who Registry - "wien", // wien punkt.wien GmbH - "wiki", // wiki Top Level Design, LLC - "williamhill", // williamhill William Hill Organization Limited - "win", // win First Registry Limited - "windows", // windows Microsoft Corporation - "wine", // wine June Station, LLC - "wme", // wme William Morris Endeavor Entertainment, LLC - "wolterskluwer", // wolterskluwer Wolters Kluwer N.V. - "work", // work Top Level Domain Holdings Limited - "works", // works Little Dynamite, LLC - "world", // world Bitter Fields, LLC - "wtc", // wtc World Trade Centers Association, Inc. - "wtf", // wtf Hidden Way, LLC - "xbox", // xbox Microsoft Corporation - "xerox", // xerox Xerox DNHC LLC - "xihuan", // xihuan QIHOO 360 TECHNOLOGY CO. LTD. - "xin", // xin Elegant Leader Limited - "xn--11b4c3d", // कॉम VeriSign Sarl - "xn--1ck2e1b", // セール Amazon Registry Services, Inc. - "xn--1qqw23a", // 佛山 Guangzhou YU Wei Information Technology Co., Ltd. - "xn--30rr7y", // 慈善 Excellent First Limited - "xn--3bst00m", // 集团 Eagle Horizon Limited - "xn--3ds443g", // 在线 TLD REGISTRY LIMITED - "xn--3pxu8k", // 点看 VeriSign Sarl - "xn--42c2d9a", // คอม VeriSign Sarl - "xn--45q11c", // 八卦 Zodiac Scorpio Limited - "xn--4gbrim", // موقع Suhub Electronic Establishment - "xn--55qw42g", // 公益 China Organizational Name Administration Center - "xn--55qx5d", // 公司 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center) - "xn--5tzm5g", // 网站 Global Website TLD Asia Limited - "xn--6frz82g", // 移动 Afilias Limited - "xn--6qq986b3xl", // 我爱你 Tycoon Treasure Limited - "xn--80adxhks", // москва Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID) - "xn--80asehdb", // онлайн CORE Association - "xn--80aswg", // сайт CORE Association - "xn--8y0a063a", // 联通 China United Network Communications Corporation Limited - "xn--9dbq2a", // קום VeriSign Sarl - "xn--9et52u", // 时尚 RISE VICTORY LIMITED - "xn--9krt00a", // 微博 Sina Corporation - "xn--b4w605ferd", // 淡马锡 Temasek Holdings (Private) Limited - "xn--bck1b9a5dre4c", // ファッション Amazon Registry Services, Inc. - "xn--c1avg", // орг Public Interest Registry - "xn--c2br7g", // नेट VeriSign Sarl - "xn--cck2b3b", // ストア Amazon Registry Services, Inc. - "xn--cg4bki", // 삼성 SAMSUNG SDS CO., LTD - "xn--czr694b", // 商标 HU YI GLOBAL INFORMATION RESOURCES(HOLDING) COMPANY.HONGKONG LIMITED - "xn--czrs0t", // 商店 Wild Island, LLC - "xn--czru2d", // 商城 Zodiac Aquarius Limited - "xn--d1acj3b", // дети The Foundation for Network Initiatives “The Smart Internet” - "xn--eckvdtc9d", // ポイント Amazon Registry Services, Inc. - "xn--efvy88h", // 新闻 Xinhua News Agency Guangdong Branch 新华通讯社广东分社 - "xn--estv75g", // 工行 Industrial and Commercial Bank of China Limited - "xn--fct429k", // 家電 Amazon Registry Services, Inc. - "xn--fhbei", // كوم VeriSign Sarl - "xn--fiq228c5hs", // 中文网 TLD REGISTRY LIMITED - "xn--fiq64b", // 中信 CITIC Group Corporation - "xn--fjq720a", // 娱乐 Will Bloom, LLC - "xn--flw351e", // 谷歌 Charleston Road Registry Inc. - "xn--g2xx48c", // 购物 Minds + Machines Group Limited - "xn--gckr3f0f", // クラウド Amazon Registry Services, Inc. - "xn--hxt814e", // 网店 Zodiac Libra Limited - "xn--i1b6b1a6a2e", // संगठन Public Interest Registry - "xn--imr513n", // 餐厅 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG LIMITED - "xn--io0a7i", // 网络 Computer Network Information Center of Chinese Academy of Sciences (China Internet Network Information Center) - "xn--j1aef", // ком VeriSign Sarl - "xn--jlq61u9w7b", // 诺基亚 Nokia Corporation - "xn--jvr189m", // 食品 Amazon Registry Services, Inc. - "xn--kcrx77d1x4a", // 飞利浦 Koninklijke Philips N.V. - "xn--kpu716f", // 手表 Richemont DNS Inc. - "xn--kput3i", // 手机 Beijing RITT-Net Technology Development Co., Ltd - "xn--mgba3a3ejt", // ارامكو Aramco Services Company - "xn--mgbab2bd", // بازار CORE Association - "xn--mgbb9fbpob", // موبايلي GreenTech Consultancy Company W.L.L. - "xn--mgbca7dzdo", // ابوظبي Abu Dhabi Systems and Information Centre - "xn--mgbt3dhd", // همراه Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. - "xn--mk1bu44c", // 닷컴 VeriSign Sarl - "xn--mxtq1m", // 政府 Net-Chinese Co., Ltd. - "xn--ngbc5azd", // شبكة International Domain Registry Pty. Ltd. - "xn--ngbe9e0a", // بيتك Kuwait Finance House - "xn--nqv7f", // 机构 Public Interest Registry - "xn--nqv7fs00ema", // 组织机构 Public Interest Registry - "xn--nyqy26a", // 健康 Stable Tone Limited - "xn--p1acf", // рус Rusnames Limited - "xn--pbt977c", // 珠宝 Richemont DNS Inc. - "xn--pssy2u", // 大拿 VeriSign Sarl - "xn--q9jyb4c", // みんな Charleston Road Registry Inc. - "xn--qcka1pmc", // グーグル Charleston Road Registry Inc. - "xn--rhqv96g", // 世界 Stable Tone Limited - "xn--rovu88b", // 書籍 Amazon EU S.à r.l. - "xn--ses554g", // 网址 KNET Co., Ltd - "xn--t60b56a", // 닷넷 VeriSign Sarl - "xn--tckwe", // コム VeriSign Sarl - "xn--unup4y", // 游戏 Spring Fields, LLC - "xn--vermgensberater-ctb", // VERMöGENSBERATER Deutsche Vermögensberatung Aktiengesellschaft DVAG - "xn--vermgensberatung-pwb", // VERMöGENSBERATUNG Deutsche Vermögensberatung Aktiengesellschaft DVAG - "xn--vhquv", // 企业 Dash McCook, LLC - "xn--vuq861b", // 信息 Beijing Tele-info Network Technology Co., Ltd. - "xn--w4r85el8fhu5dnra", // 嘉里大酒店 Kerry Trading Co. Limited - "xn--xhq521b", // 广东 Guangzhou YU Wei Information Technology Co., Ltd. - "xn--zfr164b", // 政务 China Organizational Name Administration Center - "xperia", // xperia Sony Mobile Communications AB - "xxx", // xxx ICM Registry LLC - "xyz", // xyz XYZ.COM LLC - "yachts", // yachts DERYachts, LLC - "yahoo", // yahoo Yahoo! Domain Services Inc. - "yamaxun", // yamaxun Amazon Registry Services, Inc. - "yandex", // yandex YANDEX, LLC - "yodobashi", // yodobashi YODOBASHI CAMERA CO.,LTD. - "yoga", // yoga Top Level Domain Holdings Limited - "yokohama", // yokohama GMO Registry, Inc. - "you", // you Amazon Registry Services, Inc. - "youtube", // youtube Charleston Road Registry Inc. - "yun", // yun QIHOO 360 TECHNOLOGY CO. LTD. - "zara", // zara Industria de Diseño Textil, S.A. (INDITEX, S.A.) - "zero", // zero Amazon Registry Services, Inc. - "zip", // zip Charleston Road Registry Inc. - "zone", // zone Outer Falls, LLC - "zuerich", // zuerich Kanton Zürich (Canton of Zurich) - }; - // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search - private static final String[] COUNTRY_CODE_TLDS = new String[] { - "ac", // Ascension Island - "ad", // Andorra - "ae", // United Arab Emirates - "af", // Afghanistan - "ag", // Antigua and Barbuda - "ai", // Anguilla - "al", // Albania - "am", // Armenia -// "an", // Netherlands Antilles (retired) - "ao", // Angola - "aq", // Antarctica - "ar", // Argentina - "as", // American Samoa - "at", // Austria - "au", // Australia (includes Ashmore and Cartier Islands and Coral Sea Islands) - "aw", // Aruba - "ax", // Åland - "az", // Azerbaijan - "ba", // Bosnia and Herzegovina - "bb", // Barbados - "bd", // Bangladesh - "be", // Belgium - "bf", // Burkina Faso - "bg", // Bulgaria - "bh", // Bahrain - "bi", // Burundi - "bj", // Benin - "bm", // Bermuda - "bn", // Brunei Darussalam - "bo", // Bolivia - "br", // Brazil - "bs", // Bahamas - "bt", // Bhutan - "bv", // Bouvet Island - "bw", // Botswana - "by", // Belarus - "bz", // Belize - "ca", // Canada - "cc", // Cocos (Keeling) Islands - "cd", // Democratic Republic of the Congo (formerly Zaire) - "cf", // Central African Republic - "cg", // Republic of the Congo - "ch", // Switzerland - "ci", // Côte d'Ivoire - "ck", // Cook Islands - "cl", // Chile - "cm", // Cameroon - "cn", // China, mainland - "co", // Colombia - "cr", // Costa Rica - "cu", // Cuba - "cv", // Cape Verde - "cw", // Curaçao - "cx", // Christmas Island - "cy", // Cyprus - "cz", // Czech Republic - "de", // Germany - "dj", // Djibouti - "dk", // Denmark - "dm", // Dominica - "do", // Dominican Republic - "dz", // Algeria - "ec", // Ecuador - "ee", // Estonia - "eg", // Egypt - "er", // Eritrea - "es", // Spain - "et", // Ethiopia - "eu", // European Union - "fi", // Finland - "fj", // Fiji - "fk", // Falkland Islands - "fm", // Federated States of Micronesia - "fo", // Faroe Islands - "fr", // France - "ga", // Gabon - "gb", // Great Britain (United Kingdom) - "gd", // Grenada - "ge", // Georgia - "gf", // French Guiana - "gg", // Guernsey - "gh", // Ghana - "gi", // Gibraltar - "gl", // Greenland - "gm", // The Gambia - "gn", // Guinea - "gp", // Guadeloupe - "gq", // Equatorial Guinea - "gr", // Greece - "gs", // South Georgia and the South Sandwich Islands - "gt", // Guatemala - "gu", // Guam - "gw", // Guinea-Bissau - "gy", // Guyana - "hk", // Hong Kong - "hm", // Heard Island and McDonald Islands - "hn", // Honduras - "hr", // Croatia (Hrvatska) - "ht", // Haiti - "hu", // Hungary - "id", // Indonesia - "ie", // Ireland (Éire) - "il", // Israel - "im", // Isle of Man - "in", // India - "io", // British Indian Ocean Territory - "iq", // Iraq - "ir", // Iran - "is", // Iceland - "it", // Italy - "je", // Jersey - "jm", // Jamaica - "jo", // Jordan - "jp", // Japan - "ke", // Kenya - "kg", // Kyrgyzstan - "kh", // Cambodia (Khmer) - "ki", // Kiribati - "km", // Comoros - "kn", // Saint Kitts and Nevis - "kp", // North Korea - "kr", // South Korea - "kw", // Kuwait - "ky", // Cayman Islands - "kz", // Kazakhstan - "la", // Laos (currently being marketed as the official domain for Los Angeles) - "lb", // Lebanon - "lc", // Saint Lucia - "li", // Liechtenstein - "lk", // Sri Lanka - "lr", // Liberia - "ls", // Lesotho - "lt", // Lithuania - "lu", // Luxembourg - "lv", // Latvia - "ly", // Libya - "ma", // Morocco - "mc", // Monaco - "md", // Moldova - "me", // Montenegro - "mg", // Madagascar - "mh", // Marshall Islands - "mk", // Republic of Macedonia - "ml", // Mali - "mm", // Myanmar - "mn", // Mongolia - "mo", // Macau - "mp", // Northern Mariana Islands - "mq", // Martinique - "mr", // Mauritania - "ms", // Montserrat - "mt", // Malta - "mu", // Mauritius - "mv", // Maldives - "mw", // Malawi - "mx", // Mexico - "my", // Malaysia - "mz", // Mozambique - "na", // Namibia - "nc", // New Caledonia - "ne", // Niger - "nf", // Norfolk Island - "ng", // Nigeria - "ni", // Nicaragua - "nl", // Netherlands - "no", // Norway - "np", // Nepal - "nr", // Nauru - "nu", // Niue - "nz", // New Zealand - "om", // Oman - "pa", // Panama - "pe", // Peru - "pf", // French Polynesia With Clipperton Island - "pg", // Papua New Guinea - "ph", // Philippines - "pk", // Pakistan - "pl", // Poland - "pm", // Saint-Pierre and Miquelon - "pn", // Pitcairn Islands - "pr", // Puerto Rico - "ps", // Palestinian territories (PA-controlled West Bank and Gaza Strip) - "pt", // Portugal - "pw", // Palau - "py", // Paraguay - "qa", // Qatar - "re", // Réunion - "ro", // Romania - "rs", // Serbia - "ru", // Russia - "rw", // Rwanda - "sa", // Saudi Arabia - "sb", // Solomon Islands - "sc", // Seychelles - "sd", // Sudan - "se", // Sweden - "sg", // Singapore - "sh", // Saint Helena - "si", // Slovenia - "sj", // Svalbard and Jan Mayen Islands Not in use (Norwegian dependencies; see .no) - "sk", // Slovakia - "sl", // Sierra Leone - "sm", // San Marino - "sn", // Senegal - "so", // Somalia - "sr", // Suriname - "st", // São Tomé and Príncipe - "su", // Soviet Union (deprecated) - "sv", // El Salvador - "sx", // Sint Maarten - "sy", // Syria - "sz", // Swaziland - "tc", // Turks and Caicos Islands - "td", // Chad - "tf", // French Southern and Antarctic Lands - "tg", // Togo - "th", // Thailand - "tj", // Tajikistan - "tk", // Tokelau - "tl", // East Timor (deprecated old code) - "tm", // Turkmenistan - "tn", // Tunisia - "to", // Tonga -// "tp", // East Timor (Retired) - "tr", // Turkey - "tt", // Trinidad and Tobago - "tv", // Tuvalu - "tw", // Taiwan, Republic of China - "tz", // Tanzania - "ua", // Ukraine - "ug", // Uganda - "uk", // United Kingdom - "us", // United States of America - "uy", // Uruguay - "uz", // Uzbekistan - "va", // Vatican City State - "vc", // Saint Vincent and the Grenadines - "ve", // Venezuela - "vg", // British Virgin Islands - "vi", // U.S. Virgin Islands - "vn", // Vietnam - "vu", // Vanuatu - "wf", // Wallis and Futuna - "ws", // Samoa (formerly Western Samoa) - "xn--3e0b707e", // 한국 KISA (Korea Internet & Security Agency) - "xn--45brj9c", // ভারত National Internet Exchange of India - "xn--80ao21a", // қаз Association of IT Companies of Kazakhstan - "xn--90a3ac", // срб Serbian National Internet Domain Registry (RNIDS) - "xn--90ais", // ??? Reliable Software Inc. - "xn--clchc0ea0b2g2a9gcd", // சிங்கப்பூர் Singapore Network Information Centre (SGNIC) Pte Ltd - "xn--d1alf", // мкд Macedonian Academic Research Network Skopje - "xn--e1a4c", // ею EURid vzw/asbl - "xn--fiqs8s", // 中国 China Internet Network Information Center - "xn--fiqz9s", // 中國 China Internet Network Information Center - "xn--fpcrj9c3d", // భారత్ National Internet Exchange of India - "xn--fzc2c9e2c", // ලංකා LK Domain Registry - "xn--gecrj9c", // ભારત National Internet Exchange of India - "xn--h2brj9c", // भारत National Internet Exchange of India - "xn--j1amh", // укр Ukrainian Network Information Centre (UANIC), Inc. - "xn--j6w193g", // 香港 Hong Kong Internet Registration Corporation Ltd. - "xn--kprw13d", // 台湾 Taiwan Network Information Center (TWNIC) - "xn--kpry57d", // 台灣 Taiwan Network Information Center (TWNIC) - "xn--l1acc", // мон Datacom Co.,Ltd - "xn--lgbbat1ad8j", // الجزائر CERIST - "xn--mgb9awbf", // عمان Telecommunications Regulatory Authority (TRA) - "xn--mgba3a4f16a", // ایران Institute for Research in Fundamental Sciences (IPM) - "xn--mgbaam7a8h", // امارات Telecommunications Regulatory Authority (TRA) - "xn--mgbayh7gpa", // الاردن National Information Technology Center (NITC) - "xn--mgbbh1a71e", // بھارت National Internet Exchange of India - "xn--mgbc0a9azcg", // المغرب Agence Nationale de Réglementation des Télécommunications (ANRT) - "xn--mgberp4a5d4ar", // السعودية Communications and Information Technology Commission - "xn--mgbpl2fh", // ????? Sudan Internet Society - "xn--mgbtx2b", // عراق Communications and Media Commission (CMC) - "xn--mgbx4cd0ab", // مليسيا MYNIC Berhad - "xn--mix891f", // 澳門 Bureau of Telecommunications Regulation (DSRT) - "xn--node", // გე Information Technologies Development Center (ITDC) - "xn--o3cw4h", // ไทย Thai Network Information Center Foundation - "xn--ogbpf8fl", // سورية National Agency for Network Services (NANS) - "xn--p1ai", // рф Coordination Center for TLD RU - "xn--pgbs0dh", // تونس Agence Tunisienne d'Internet - "xn--qxam", // ελ ICS-FORTH GR - "xn--s9brj9c", // ਭਾਰਤ National Internet Exchange of India - "xn--wgbh1c", // مصر National Telecommunication Regulatory Authority - NTRA - "xn--wgbl6a", // قطر Communications Regulatory Authority - "xn--xkc2al3hye2a", // இலங்கை LK Domain Registry - "xn--xkc2dl3a5ee0h", // இந்தியா National Internet Exchange of India - "xn--y9a3aq", // ??? Internet Society - "xn--yfro4i67o", // 新加坡 Singapore Network Information Centre (SGNIC) Pte Ltd - "xn--ygbi2ammx", // فلسطين Ministry of Telecom & Information Technology (MTIT) - "ye", // Yemen - "yt", // Mayotte - "za", // South Africa - "zm", // Zambia - "zw", // Zimbabwe - }; - // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search - private static final String[] LOCAL_TLDS = new String[] { - "localdomain", // Also widely used as localhost.localdomain - "localhost", // RFC2606 defined - }; + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary + // search + private static final String[] INFRASTRUCTURE_TLDS = + new String[] { + "arpa", // internet infrastructure + }; + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary + // search + private static final String[] GENERIC_TLDS = + new String[] { + // Taken from Version 2016042500, Last Updated Mon Apr 25 07:07:01 2016 UTC + "aaa", // aaa American Automobile Association, Inc. + "aarp", // aarp AARP + "abb", // abb ABB Ltd + "abbott", // abbott Abbott Laboratories, Inc. + "abbvie", // abbvie AbbVie Inc. + "abogado", // abogado Top Level Domain Holdings Limited + "abudhabi", // abudhabi Abu Dhabi Systems and Information Centre + "academy", // academy Half Oaks, LLC + "accenture", // accenture Accenture plc + "accountant", // accountant dot Accountant Limited + "accountants", // accountants Knob Town, LLC + "aco", // aco ACO Severin Ahlmann GmbH & Co. KG + "active", // active The Active Network, Inc + "actor", // actor United TLD Holdco Ltd. + "adac", // adac Allgemeiner Deutscher Automobil-Club e.V. (ADAC) + "ads", // ads Charleston Road Registry Inc. + "adult", // adult ICM Registry AD LLC + "aeg", // aeg Aktiebolaget Electrolux + "aero", // aero Societe Internationale de Telecommunications Aeronautique (SITA INC + // USA) + "afl", // afl Australian Football League + "agakhan", // agakhan Fondation Aga Khan (Aga Khan Foundation) + "agency", // agency Steel Falls, LLC + "aig", // aig American International Group, Inc. + "airforce", // airforce United TLD Holdco Ltd. + "airtel", // airtel Bharti Airtel Limited + "akdn", // akdn Fondation Aga Khan (Aga Khan Foundation) + "alibaba", // alibaba Alibaba Group Holding Limited + "alipay", // alipay Alibaba Group Holding Limited + "allfinanz", // allfinanz Allfinanz Deutsche Vermögensberatung Aktiengesellschaft + "ally", // ally Ally Financial Inc. + "alsace", // alsace REGION D ALSACE + "amica", // amica Amica Mutual Insurance Company + "amsterdam", // amsterdam Gemeente Amsterdam + "analytics", // analytics Campus IP LLC + "android", // android Charleston Road Registry Inc. + "anquan", // anquan QIHOO 360 TECHNOLOGY CO. LTD. + "apartments", // apartments June Maple, LLC + "app", // app Charleston Road Registry Inc. + "apple", // apple Apple Inc. + "aquarelle", // aquarelle Aquarelle.com + "aramco", // aramco Aramco Services Company + "archi", // archi STARTING DOT LIMITED + "army", // army United TLD Holdco Ltd. + "arte", // arte Association Relative à la Télévision Européenne G.E.I.E. + "asia", // asia DotAsia Organisation Ltd. + "associates", // associates Baxter Hill, LLC + "attorney", // attorney United TLD Holdco, Ltd + "auction", // auction United TLD HoldCo, Ltd. + "audi", // audi AUDI Aktiengesellschaft + "audio", // audio Uniregistry, Corp. + "author", // author Amazon Registry Services, Inc. + "auto", // auto Uniregistry, Corp. + "autos", // autos DERAutos, LLC + "avianca", // avianca Aerovias del Continente Americano S.A. Avianca + "aws", // aws Amazon Registry Services, Inc. + "axa", // axa AXA SA + "azure", // azure Microsoft Corporation + "baby", // baby Johnson & Johnson Services, Inc. + "baidu", // baidu Baidu, Inc. + "band", // band United TLD Holdco, Ltd + "bank", // bank fTLD Registry Services, LLC + "bar", // bar Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable + "barcelona", // barcelona Municipi de Barcelona + "barclaycard", // barclaycard Barclays Bank PLC + "barclays", // barclays Barclays Bank PLC + "barefoot", // barefoot Gallo Vineyards, Inc. + "bargains", // bargains Half Hallow, LLC + "bauhaus", // bauhaus Werkhaus GmbH + "bayern", // bayern Bayern Connect GmbH + "bbc", // bbc British Broadcasting Corporation + "bbva", // bbva BANCO BILBAO VIZCAYA ARGENTARIA, S.A. + "bcg", // bcg The Boston Consulting Group, Inc. + "bcn", // bcn Municipi de Barcelona + "beats", // beats Beats Electronics, LLC + "beer", // beer Top Level Domain Holdings Limited + "bentley", // bentley Bentley Motors Limited + "berlin", // berlin dotBERLIN GmbH & Co. KG + "best", // best BestTLD Pty Ltd + "bet", // bet Afilias plc + "bharti", // bharti Bharti Enterprises (Holding) Private Limited + "bible", // bible American Bible Society + "bid", // bid dot Bid Limited + "bike", // bike Grand Hollow, LLC + "bing", // bing Microsoft Corporation + "bingo", // bingo Sand Cedar, LLC + "bio", // bio STARTING DOT LIMITED + "biz", // biz Neustar, Inc. + "black", // black Afilias Limited + "blackfriday", // blackfriday Uniregistry, Corp. + "bloomberg", // bloomberg Bloomberg IP Holdings LLC + "blue", // blue Afilias Limited + "bms", // bms Bristol-Myers Squibb Company + "bmw", // bmw Bayerische Motoren Werke Aktiengesellschaft + "bnl", // bnl Banca Nazionale del Lavoro + "bnpparibas", // bnpparibas BNP Paribas + "boats", // boats DERBoats, LLC + "boehringer", // boehringer Boehringer Ingelheim International GmbH + "bom", // bom Núcleo de Informação e Coordenação do Ponto BR - NIC.br + "bond", // bond Bond University Limited + "boo", // boo Charleston Road Registry Inc. + "book", // book Amazon Registry Services, Inc. + "boots", // boots THE BOOTS COMPANY PLC + "bosch", // bosch Robert Bosch GMBH + "bostik", // bostik Bostik SA + "bot", // bot Amazon Registry Services, Inc. + "boutique", // boutique Over Galley, LLC + "bradesco", // bradesco Banco Bradesco S.A. + "bridgestone", // bridgestone Bridgestone Corporation + "broadway", // broadway Celebrate Broadway, Inc. + "broker", // broker DOTBROKER REGISTRY LTD + "brother", // brother Brother Industries, Ltd. + "brussels", // brussels DNS.be vzw + "budapest", // budapest Top Level Domain Holdings Limited + "bugatti", // bugatti Bugatti International SA + "build", // build Plan Bee LLC + "builders", // builders Atomic Madison, LLC + "business", // business Spring Cross, LLC + "buy", // buy Amazon Registry Services, INC + "buzz", // buzz DOTSTRATEGY CO. + "bzh", // bzh Association www.bzh + "cab", // cab Half Sunset, LLC + "cafe", // cafe Pioneer Canyon, LLC + "cal", // cal Charleston Road Registry Inc. + "call", // call Amazon Registry Services, Inc. + "camera", // camera Atomic Maple, LLC + "camp", // camp Delta Dynamite, LLC + "cancerresearch", // cancerresearch Australian Cancer Research Foundation + "canon", // canon Canon Inc. + "capetown", // capetown ZA Central Registry NPC trading as ZA Central Registry + "capital", // capital Delta Mill, LLC + "car", // car Cars Registry Limited + "caravan", // caravan Caravan International, Inc. + "cards", // cards Foggy Hollow, LLC + "care", // care Goose Cross, LLC + "career", // career dotCareer LLC + "careers", // careers Wild Corner, LLC + "cars", // cars Uniregistry, Corp. + "cartier", // cartier Richemont DNS Inc. + "casa", // casa Top Level Domain Holdings Limited + "cash", // cash Delta Lake, LLC + "casino", // casino Binky Sky, LLC + "cat", // cat Fundacio puntCAT + "catering", // catering New Falls. LLC + "cba", // cba COMMONWEALTH BANK OF AUSTRALIA + "cbn", // cbn The Christian Broadcasting Network, Inc. + "ceb", // ceb The Corporate Executive Board Company + "center", // center Tin Mill, LLC + "ceo", // ceo CEOTLD Pty Ltd + "cern", // cern European Organization for Nuclear Research ("CERN") + "cfa", // cfa CFA Institute + "cfd", // cfd DOTCFD REGISTRY LTD + "chanel", // chanel Chanel International B.V. + "channel", // channel Charleston Road Registry Inc. + "chase", // chase JPMorgan Chase & Co. + "chat", // chat Sand Fields, LLC + "cheap", // cheap Sand Cover, LLC + "chloe", // chloe Richemont DNS Inc. + "christmas", // christmas Uniregistry, Corp. + "chrome", // chrome Charleston Road Registry Inc. + "church", // church Holly Fileds, LLC + "cipriani", // cipriani Hotel Cipriani Srl + "circle", // circle Amazon Registry Services, Inc. + "cisco", // cisco Cisco Technology, Inc. + "citic", // citic CITIC Group Corporation + "city", // city Snow Sky, LLC + "cityeats", // cityeats Lifestyle Domain Holdings, Inc. + "claims", // claims Black Corner, LLC + "cleaning", // cleaning Fox Shadow, LLC + "click", // click Uniregistry, Corp. + "clinic", // clinic Goose Park, LLC + "clinique", // clinique The Estée Lauder Companies Inc. + "clothing", // clothing Steel Lake, LLC + "cloud", // cloud ARUBA S.p.A. + "club", // club .CLUB DOMAINS, LLC + "clubmed", // clubmed Club Méditerranée S.A. + "coach", // coach Koko Island, LLC + "codes", // codes Puff Willow, LLC + "coffee", // coffee Trixy Cover, LLC + "college", // college XYZ.COM LLC + "cologne", // cologne NetCologne Gesellschaft für Telekommunikation mbH + "com", // com VeriSign Global Registry Services + "commbank", // commbank COMMONWEALTH BANK OF AUSTRALIA + "community", // community Fox Orchard, LLC + "company", // company Silver Avenue, LLC + "compare", // compare iSelect Ltd + "computer", // computer Pine Mill, LLC + "comsec", // comsec VeriSign, Inc. + "condos", // condos Pine House, LLC + "construction", // construction Fox Dynamite, LLC + "consulting", // consulting United TLD Holdco, LTD. + "contact", // contact Top Level Spectrum, Inc. + "contractors", // contractors Magic Woods, LLC + "cooking", // cooking Top Level Domain Holdings Limited + "cool", // cool Koko Lake, LLC + "coop", // coop DotCooperation LLC + "corsica", // corsica Collectivité Territoriale de Corse + "country", // country Top Level Domain Holdings Limited + "coupon", // coupon Amazon Registry Services, Inc. + "coupons", // coupons Black Island, LLC + "courses", // courses OPEN UNIVERSITIES AUSTRALIA PTY LTD + "credit", // credit Snow Shadow, LLC + "creditcard", // creditcard Binky Frostbite, LLC + "creditunion", // creditunion CUNA Performance Resources, LLC + "cricket", // cricket dot Cricket Limited + "crown", // crown Crown Equipment Corporation + "crs", // crs Federated Co-operatives Limited + "cruises", // cruises Spring Way, LLC + "csc", // csc Alliance-One Services, Inc. + "cuisinella", // cuisinella SALM S.A.S. + "cymru", // cymru Nominet UK + "cyou", // cyou Beijing Gamease Age Digital Technology Co., Ltd. + "dabur", // dabur Dabur India Limited + "dad", // dad Charleston Road Registry Inc. + "dance", // dance United TLD Holdco Ltd. + "date", // date dot Date Limited + "dating", // dating Pine Fest, LLC + "datsun", // datsun NISSAN MOTOR CO., LTD. + "day", // day Charleston Road Registry Inc. + "dclk", // dclk Charleston Road Registry Inc. + "dealer", // dealer Dealer Dot Com, Inc. + "deals", // deals Sand Sunset, LLC + "degree", // degree United TLD Holdco, Ltd + "delivery", // delivery Steel Station, LLC + "dell", // dell Dell Inc. + "deloitte", // deloitte Deloitte Touche Tohmatsu + "delta", // delta Delta Air Lines, Inc. + "democrat", // democrat United TLD Holdco Ltd. + "dental", // dental Tin Birch, LLC + "dentist", // dentist United TLD Holdco, Ltd + "desi", // desi Desi Networks LLC + "design", // design Top Level Design, LLC + "dev", // dev Charleston Road Registry Inc. + "diamonds", // diamonds John Edge, LLC + "diet", // diet Uniregistry, Corp. + "digital", // digital Dash Park, LLC + "direct", // direct Half Trail, LLC + "directory", // directory Extra Madison, LLC + "discount", // discount Holly Hill, LLC + "dnp", // dnp Dai Nippon Printing Co., Ltd. + "docs", // docs Charleston Road Registry Inc. + "dog", // dog Koko Mill, LLC + "doha", // doha Communications Regulatory Authority (CRA) + "domains", // domains Sugar Cross, LLC + // "doosan", // doosan Doosan Corporation (retired) + "download", // download dot Support Limited + "drive", // drive Charleston Road Registry Inc. + "dubai", // dubai Dubai Smart Government Department + "durban", // durban ZA Central Registry NPC trading as ZA Central Registry + "dvag", // dvag Deutsche Vermögensberatung Aktiengesellschaft DVAG + "earth", // earth Interlink Co., Ltd. + "eat", // eat Charleston Road Registry Inc. + "edeka", // edeka EDEKA Verband kaufmännischer Genossenschaften e.V. + "edu", // edu EDUCAUSE + "education", // education Brice Way, LLC + "email", // email Spring Madison, LLC + "emerck", // emerck Merck KGaA + "energy", // energy Binky Birch, LLC + "engineer", // engineer United TLD Holdco Ltd. + "engineering", // engineering Romeo Canyon + "enterprises", // enterprises Snow Oaks, LLC + "epson", // epson Seiko Epson Corporation + "equipment", // equipment Corn Station, LLC + "erni", // erni ERNI Group Holding AG + "esq", // esq Charleston Road Registry Inc. + "estate", // estate Trixy Park, LLC + "eurovision", // eurovision European Broadcasting Union (EBU) + "eus", // eus Puntueus Fundazioa + "events", // events Pioneer Maple, LLC + "everbank", // everbank EverBank + "exchange", // exchange Spring Falls, LLC + "expert", // expert Magic Pass, LLC + "exposed", // exposed Victor Beach, LLC + "express", // express Sea Sunset, LLC + "extraspace", // extraspace Extra Space Storage LLC + "fage", // fage Fage International S.A. + "fail", // fail Atomic Pipe, LLC + "fairwinds", // fairwinds FairWinds Partners, LLC + "faith", // faith dot Faith Limited + "family", // family United TLD Holdco Ltd. + "fan", // fan Asiamix Digital Ltd + "fans", // fans Asiamix Digital Limited + "farm", // farm Just Maple, LLC + "fashion", // fashion Top Level Domain Holdings Limited + "fast", // fast Amazon Registry Services, Inc. + "feedback", // feedback Top Level Spectrum, Inc. + "ferrero", // ferrero Ferrero Trading Lux S.A. + "film", // film Motion Picture Domain Registry Pty Ltd + "final", // final Núcleo de Informação e Coordenação do Ponto BR - NIC.br + "finance", // finance Cotton Cypress, LLC + "financial", // financial Just Cover, LLC + "firestone", // firestone Bridgestone Corporation + "firmdale", // firmdale Firmdale Holdings Limited + "fish", // fish Fox Woods, LLC + "fishing", // fishing Top Level Domain Holdings Limited + "fit", // fit Minds + Machines Group Limited + "fitness", // fitness Brice Orchard, LLC + "flickr", // flickr Yahoo! Domain Services Inc. + "flights", // flights Fox Station, LLC + "florist", // florist Half Cypress, LLC + "flowers", // flowers Uniregistry, Corp. + "flsmidth", // flsmidth FLSmidth A/S + "fly", // fly Charleston Road Registry Inc. + "foo", // foo Charleston Road Registry Inc. + "football", // football Foggy Farms, LLC + "ford", // ford Ford Motor Company + "forex", // forex DOTFOREX REGISTRY LTD + "forsale", // forsale United TLD Holdco, LLC + "forum", // forum Fegistry, LLC + "foundation", // foundation John Dale, LLC + "fox", // fox FOX Registry, LLC + "fresenius", // fresenius Fresenius Immobilien-Verwaltungs-GmbH + "frl", // frl FRLregistry B.V. + "frogans", // frogans OP3FT + "frontier", // frontier Frontier Communications Corporation + "ftr", // ftr Frontier Communications Corporation + "fund", // fund John Castle, LLC + "furniture", // furniture Lone Fields, LLC + "futbol", // futbol United TLD Holdco, Ltd. + "fyi", // fyi Silver Tigers, LLC + "gal", // gal Asociación puntoGAL + "gallery", // gallery Sugar House, LLC + "gallo", // gallo Gallo Vineyards, Inc. + "gallup", // gallup Gallup, Inc. + "game", // game Uniregistry, Corp. + "garden", // garden Top Level Domain Holdings Limited + "gbiz", // gbiz Charleston Road Registry Inc. + "gdn", // gdn Joint Stock Company "Navigation-information systems" + "gea", // gea GEA Group Aktiengesellschaft + "gent", // gent COMBELL GROUP NV/SA + "genting", // genting Resorts World Inc. Pte. Ltd. + "ggee", // ggee GMO Internet, Inc. + "gift", // gift Uniregistry, Corp. + "gifts", // gifts Goose Sky, LLC + "gives", // gives United TLD Holdco Ltd. + "giving", // giving Giving Limited + "glass", // glass Black Cover, LLC + "gle", // gle Charleston Road Registry Inc. + "global", // global Dot Global Domain Registry Limited + "globo", // globo Globo Comunicação e Participações S.A + "gmail", // gmail Charleston Road Registry Inc. + "gmbh", // gmbh Extra Dynamite, LLC + "gmo", // gmo GMO Internet, Inc. + "gmx", // gmx 1&1 Mail & Media GmbH + "gold", // gold June Edge, LLC + "goldpoint", // goldpoint YODOBASHI CAMERA CO.,LTD. + "golf", // golf Lone Falls, LLC + "goo", // goo NTT Resonant Inc. + "goog", // goog Charleston Road Registry Inc. + "google", // google Charleston Road Registry Inc. + "gop", // gop Republican State Leadership Committee, Inc. + "got", // got Amazon Registry Services, Inc. + "gov", // gov General Services Administration Attn: QTDC, 2E08 (.gov Domain + // Registration) + "grainger", // grainger Grainger Registry Services, LLC + "graphics", // graphics Over Madison, LLC + "gratis", // gratis Pioneer Tigers, LLC + "green", // green Afilias Limited + "gripe", // gripe Corn Sunset, LLC + "group", // group Romeo Town, LLC + "gucci", // gucci Guccio Gucci S.p.a. + "guge", // guge Charleston Road Registry Inc. + "guide", // guide Snow Moon, LLC + "guitars", // guitars Uniregistry, Corp. + "guru", // guru Pioneer Cypress, LLC + "hamburg", // hamburg Hamburg Top-Level-Domain GmbH + "hangout", // hangout Charleston Road Registry Inc. + "haus", // haus United TLD Holdco, LTD. + "hdfcbank", // hdfcbank HDFC Bank Limited + "health", // health DotHealth, LLC + "healthcare", // healthcare Silver Glen, LLC + "help", // help Uniregistry, Corp. + "helsinki", // helsinki City of Helsinki + "here", // here Charleston Road Registry Inc. + "hermes", // hermes Hermes International + "hiphop", // hiphop Uniregistry, Corp. + "hitachi", // hitachi Hitachi, Ltd. + "hiv", // hiv dotHIV gemeinnuetziger e.V. + "hockey", // hockey Half Willow, LLC + "holdings", // holdings John Madison, LLC + "holiday", // holiday Goose Woods, LLC + "homedepot", // homedepot Homer TLC, Inc. + "homes", // homes DERHomes, LLC + "honda", // honda Honda Motor Co., Ltd. + "horse", // horse Top Level Domain Holdings Limited + "host", // host DotHost Inc. + "hosting", // hosting Uniregistry, Corp. + "hoteles", // hoteles Travel Reservations SRL + "hotmail", // hotmail Microsoft Corporation + "house", // house Sugar Park, LLC + "how", // how Charleston Road Registry Inc. + "hsbc", // hsbc HSBC Holdings PLC + "htc", // htc HTC corporation + "hyundai", // hyundai Hyundai Motor Company + "ibm", // ibm International Business Machines Corporation + "icbc", // icbc Industrial and Commercial Bank of China Limited + "ice", // ice IntercontinentalExchange, Inc. + "icu", // icu One.com A/S + "ifm", // ifm ifm electronic gmbh + "iinet", // iinet Connect West Pty. Ltd. + "imamat", // imamat Fondation Aga Khan (Aga Khan Foundation) + "immo", // immo Auburn Bloom, LLC + "immobilien", // immobilien United TLD Holdco Ltd. + "industries", // industries Outer House, LLC + "infiniti", // infiniti NISSAN MOTOR CO., LTD. + "info", // info Afilias Limited + "ing", // ing Charleston Road Registry Inc. + "ink", // ink Top Level Design, LLC + "institute", // institute Outer Maple, LLC + "insurance", // insurance fTLD Registry Services LLC + "insure", // insure Pioneer Willow, LLC + "int", // int Internet Assigned Numbers Authority + "international", // international Wild Way, LLC + "investments", // investments Holly Glen, LLC + "ipiranga", // ipiranga Ipiranga Produtos de Petroleo S.A. + "irish", // irish Dot-Irish LLC + "iselect", // iselect iSelect Ltd + "ismaili", // ismaili Fondation Aga Khan (Aga Khan Foundation) + "ist", // ist Istanbul Metropolitan Municipality + "istanbul", // istanbul Istanbul Metropolitan Municipality / Medya A.S. + "itau", // itau Itau Unibanco Holding S.A. + "iwc", // iwc Richemont DNS Inc. + "jaguar", // jaguar Jaguar Land Rover Ltd + "java", // java Oracle Corporation + "jcb", // jcb JCB Co., Ltd. + "jcp", // jcp JCP Media, Inc. + "jetzt", // jetzt New TLD Company AB + "jewelry", // jewelry Wild Bloom, LLC + "jlc", // jlc Richemont DNS Inc. + "jll", // jll Jones Lang LaSalle Incorporated + "jmp", // jmp Matrix IP LLC + "jnj", // jnj Johnson & Johnson Services, Inc. + "jobs", // jobs Employ Media LLC + "joburg", // joburg ZA Central Registry NPC trading as ZA Central Registry + "jot", // jot Amazon Registry Services, Inc. + "joy", // joy Amazon Registry Services, Inc. + "jpmorgan", // jpmorgan JPMorgan Chase & Co. + "jprs", // jprs Japan Registry Services Co., Ltd. + "juegos", // juegos Uniregistry, Corp. + "kaufen", // kaufen United TLD Holdco Ltd. + "kddi", // kddi KDDI CORPORATION + "kerryhotels", // kerryhotels Kerry Trading Co. Limited + "kerrylogistics", // kerrylogistics Kerry Trading Co. Limited + "kerryproperties", // kerryproperties Kerry Trading Co. Limited + "kfh", // kfh Kuwait Finance House + "kia", // kia KIA MOTORS CORPORATION + "kim", // kim Afilias Limited + "kinder", // kinder Ferrero Trading Lux S.A. + "kitchen", // kitchen Just Goodbye, LLC + "kiwi", // kiwi DOT KIWI LIMITED + "koeln", // koeln NetCologne Gesellschaft für Telekommunikation mbH + "komatsu", // komatsu Komatsu Ltd. + "kpmg", // kpmg KPMG International Cooperative (KPMG International Genossenschaft) + "kpn", // kpn Koninklijke KPN N.V. + "krd", // krd KRG Department of Information Technology + "kred", // kred KredTLD Pty Ltd + "kuokgroup", // kuokgroup Kerry Trading Co. Limited + "kyoto", // kyoto Academic Institution: Kyoto Jyoho Gakuen + "lacaixa", // lacaixa CAIXA D'ESTALVIS I PENSIONS DE BARCELONA + "lamborghini", // lamborghini Automobili Lamborghini S.p.A. + "lamer", // lamer The Estée Lauder Companies Inc. + "lancaster", // lancaster LANCASTER + "land", // land Pine Moon, LLC + "landrover", // landrover Jaguar Land Rover Ltd + "lanxess", // lanxess LANXESS Corporation + "lasalle", // lasalle Jones Lang LaSalle Incorporated + "lat", // lat ECOM-LAC Federación de Latinoamérica y el Caribe para Internet y el + // Comercio Electrónico + "latrobe", // latrobe La Trobe University + "law", // law Minds + Machines Group Limited + "lawyer", // lawyer United TLD Holdco, Ltd + "lds", // lds IRI Domain Management, LLC + "lease", // lease Victor Trail, LLC + "leclerc", // leclerc A.C.D. LEC Association des Centres Distributeurs Edouard + // Leclerc + "legal", // legal Blue Falls, LLC + "lexus", // lexus TOYOTA MOTOR CORPORATION + "lgbt", // lgbt Afilias Limited + "liaison", // liaison Liaison Technologies, Incorporated + "lidl", // lidl Schwarz Domains und Services GmbH & Co. KG + "life", // life Trixy Oaks, LLC + "lifeinsurance", // lifeinsurance American Council of Life Insurers + "lifestyle", // lifestyle Lifestyle Domain Holdings, Inc. + "lighting", // lighting John McCook, LLC + "like", // like Amazon Registry Services, Inc. + "limited", // limited Big Fest, LLC + "limo", // limo Hidden Frostbite, LLC + "lincoln", // lincoln Ford Motor Company + "linde", // linde Linde Aktiengesellschaft + "link", // link Uniregistry, Corp. + "live", // live United TLD Holdco Ltd. + "living", // living Lifestyle Domain Holdings, Inc. + "lixil", // lixil LIXIL Group Corporation + "loan", // loan dot Loan Limited + "loans", // loans June Woods, LLC + "locus", // locus Locus Analytics LLC + "lol", // lol Uniregistry, Corp. + "london", // london Dot London Domains Limited + "lotte", // lotte Lotte Holdings Co., Ltd. + "lotto", // lotto Afilias Limited + "love", // love Merchant Law Group LLP + "ltd", // ltd Over Corner, LLC + "ltda", // ltda InterNetX Corp. + "lupin", // lupin LUPIN LIMITED + "luxe", // luxe Top Level Domain Holdings Limited + "luxury", // luxury Luxury Partners LLC + "madrid", // madrid Comunidad de Madrid + "maif", // maif Mutuelle Assurance Instituteur France (MAIF) + "maison", // maison Victor Frostbite, LLC + "makeup", // makeup L'Oréal + "man", // man MAN SE + "management", // management John Goodbye, LLC + "mango", // mango PUNTO FA S.L. + "market", // market Unitied TLD Holdco, Ltd + "marketing", // marketing Fern Pass, LLC + "markets", // markets DOTMARKETS REGISTRY LTD + "marriott", // marriott Marriott Worldwide Corporation + "mba", // mba Lone Hollow, LLC + "med", // med Medistry LLC + "media", // media Grand Glen, LLC + "meet", // meet Afilias Limited + "melbourne", // melbourne The Crown in right of the State of Victoria, represented + // by its Department of State Development, Business and Innovation + "meme", // meme Charleston Road Registry Inc. + "memorial", // memorial Dog Beach, LLC + "men", // men Exclusive Registry Limited + "menu", // menu Wedding TLD2, LLC + "meo", // meo PT Comunicacoes S.A. + "miami", // miami Top Level Domain Holdings Limited + "microsoft", // microsoft Microsoft Corporation + "mil", // mil DoD Network Information Center + "mini", // mini Bayerische Motoren Werke Aktiengesellschaft + "mls", // mls The Canadian Real Estate Association + "mma", // mma MMA IARD + "mobi", // mobi Afilias Technologies Limited dba dotMobi + "mobily", // mobily GreenTech Consultancy Company W.L.L. + "moda", // moda United TLD Holdco Ltd. + "moe", // moe Interlink Co., Ltd. + "moi", // moi Amazon Registry Services, Inc. + "mom", // mom Uniregistry, Corp. + "monash", // monash Monash University + "money", // money Outer McCook, LLC + "montblanc", // montblanc Richemont DNS Inc. + "mormon", // mormon IRI Domain Management, LLC ("Applicant") + "mortgage", // mortgage United TLD Holdco, Ltd + "moscow", // moscow Foundation for Assistance for Internet Technologies and + // Infrastructure Development (FAITID) + "motorcycles", // motorcycles DERMotorcycles, LLC + "mov", // mov Charleston Road Registry Inc. + "movie", // movie New Frostbite, LLC + "movistar", // movistar Telefónica S.A. + "mtn", // mtn MTN Dubai Limited + "mtpc", // mtpc Mitsubishi Tanabe Pharma Corporation + "mtr", // mtr MTR Corporation Limited + "museum", // museum Museum Domain Management Association + "mutual", // mutual Northwestern Mutual MU TLD Registry, LLC + "mutuelle", // mutuelle Fédération Nationale de la Mutualité Française + "nadex", // nadex Nadex Domains, Inc + "nagoya", // nagoya GMO Registry, Inc. + "name", // name VeriSign Information Services, Inc. + "natura", // natura NATURA COSMÉTICOS S.A. + "navy", // navy United TLD Holdco Ltd. + "nec", // nec NEC Corporation + "net", // net VeriSign Global Registry Services + "netbank", // netbank COMMONWEALTH BANK OF AUSTRALIA + "network", // network Trixy Manor, LLC + "neustar", // neustar NeuStar, Inc. + "new", // new Charleston Road Registry Inc. + "news", // news United TLD Holdco Ltd. + "nexus", // nexus Charleston Road Registry Inc. + "ngo", // ngo Public Interest Registry + "nhk", // nhk Japan Broadcasting Corporation (NHK) + "nico", // nico DWANGO Co., Ltd. + "nikon", // nikon NIKON CORPORATION + "ninja", // ninja United TLD Holdco Ltd. + "nissan", // nissan NISSAN MOTOR CO., LTD. + "nissay", // nissay Nippon Life Insurance Company + "nokia", // nokia Nokia Corporation + "northwesternmutual", // northwesternmutual Northwestern Mutual Registry, LLC + "norton", // norton Symantec Corporation + "nowruz", // nowruz Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "nra", // nra NRA Holdings Company, INC. + "nrw", // nrw Minds + Machines GmbH + "ntt", // ntt NIPPON TELEGRAPH AND TELEPHONE CORPORATION + "nyc", // nyc The City of New York by and through the New York City Department of + // Information Technology & Telecommunications + "obi", // obi OBI Group Holding SE & Co. KGaA + "office", // office Microsoft Corporation + "okinawa", // okinawa BusinessRalliart inc. + "omega", // omega The Swatch Group Ltd + "one", // one One.com A/S + "ong", // ong Public Interest Registry + "onl", // onl I-REGISTRY Ltd., Niederlassung Deutschland + "online", // online DotOnline Inc. + "ooo", // ooo INFIBEAM INCORPORATION LIMITED + "oracle", // oracle Oracle Corporation + "orange", // orange Orange Brand Services Limited + "org", // org Public Interest Registry (PIR) + "organic", // organic Afilias Limited + "origins", // origins The Estée Lauder Companies Inc. + "osaka", // osaka Interlink Co., Ltd. + "otsuka", // otsuka Otsuka Holdings Co., Ltd. + "ovh", // ovh OVH SAS + "page", // page Charleston Road Registry Inc. + "pamperedchef", // pamperedchef The Pampered Chef, Ltd. + "panerai", // panerai Richemont DNS Inc. + "paris", // paris City of Paris + "pars", // pars Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "partners", // partners Magic Glen, LLC + "parts", // parts Sea Goodbye, LLC + "party", // party Blue Sky Registry Limited + "passagens", // passagens Travel Reservations SRL + "pet", // pet Afilias plc + "pharmacy", // pharmacy National Association of Boards of Pharmacy + "philips", // philips Koninklijke Philips N.V. + "photo", // photo Uniregistry, Corp. + "photography", // photography Sugar Glen, LLC + "photos", // photos Sea Corner, LLC + "physio", // physio PhysBiz Pty Ltd + "piaget", // piaget Richemont DNS Inc. + "pics", // pics Uniregistry, Corp. + "pictet", // pictet Pictet Europe S.A. + "pictures", // pictures Foggy Sky, LLC + "pid", // pid Top Level Spectrum, Inc. + "pin", // pin Amazon Registry Services, Inc. + "ping", // ping Ping Registry Provider, Inc. + "pink", // pink Afilias Limited + "pizza", // pizza Foggy Moon, LLC + "place", // place Snow Galley, LLC + "play", // play Charleston Road Registry Inc. + "playstation", // playstation Sony Computer Entertainment Inc. + "plumbing", // plumbing Spring Tigers, LLC + "plus", // plus Sugar Mill, LLC + "pohl", // pohl Deutsche Vermögensberatung Aktiengesellschaft DVAG + "poker", // poker Afilias Domains No. 5 Limited + "porn", // porn ICM Registry PN LLC + "post", // post Universal Postal Union + "praxi", // praxi Praxi S.p.A. + "press", // press DotPress Inc. + "pro", // pro Registry Services Corporation dba RegistryPro + "prod", // prod Charleston Road Registry Inc. + "productions", // productions Magic Birch, LLC + "prof", // prof Charleston Road Registry Inc. + "progressive", // progressive Progressive Casualty Insurance Company + "promo", // promo Afilias plc + "properties", // properties Big Pass, LLC + "property", // property Uniregistry, Corp. + "protection", // protection XYZ.COM LLC + "pub", // pub United TLD Holdco Ltd. + "pwc", // pwc PricewaterhouseCoopers LLP + "qpon", // qpon dotCOOL, Inc. + "quebec", // quebec PointQuébec Inc + "quest", // quest Quest ION Limited + "racing", // racing Premier Registry Limited + "read", // read Amazon Registry Services, Inc. + "realtor", // realtor Real Estate Domains LLC + "realty", // realty Fegistry, LLC + "recipes", // recipes Grand Island, LLC + "red", // red Afilias Limited + "redstone", // redstone Redstone Haute Couture Co., Ltd. + "redumbrella", // redumbrella Travelers TLD, LLC + "rehab", // rehab United TLD Holdco Ltd. + "reise", // reise Foggy Way, LLC + "reisen", // reisen New Cypress, LLC + "reit", // reit National Association of Real Estate Investment Trusts, Inc. + "ren", // ren Beijing Qianxiang Wangjing Technology Development Co., Ltd. + "rent", // rent XYZ.COM LLC + "rentals", // rentals Big Hollow,LLC + "repair", // repair Lone Sunset, LLC + "report", // report Binky Glen, LLC + "republican", // republican United TLD Holdco Ltd. + "rest", // rest Punto 2012 Sociedad Anonima Promotora de Inversion de Capital + // Variable + "restaurant", // restaurant Snow Avenue, LLC + "review", // review dot Review Limited + "reviews", // reviews United TLD Holdco, Ltd. + "rexroth", // rexroth Robert Bosch GMBH + "rich", // rich I-REGISTRY Ltd., Niederlassung Deutschland + "ricoh", // ricoh Ricoh Company, Ltd. + "rio", // rio Empresa Municipal de Informática SA - IPLANRIO + "rip", // rip United TLD Holdco Ltd. + "rocher", // rocher Ferrero Trading Lux S.A. + "rocks", // rocks United TLD Holdco, LTD. + "rodeo", // rodeo Top Level Domain Holdings Limited + "room", // room Amazon Registry Services, Inc. + "rsvp", // rsvp Charleston Road Registry Inc. + "ruhr", // ruhr regiodot GmbH & Co. KG + "run", // run Snow Park, LLC + "rwe", // rwe RWE AG + "ryukyu", // ryukyu BusinessRalliart inc. + "saarland", // saarland dotSaarland GmbH + "safe", // safe Amazon Registry Services, Inc. + "safety", // safety Safety Registry Services, LLC. + "sakura", // sakura SAKURA Internet Inc. + "sale", // sale United TLD Holdco, Ltd + "salon", // salon Outer Orchard, LLC + "samsung", // samsung SAMSUNG SDS CO., LTD + "sandvik", // sandvik Sandvik AB + "sandvikcoromant", // sandvikcoromant Sandvik AB + "sanofi", // sanofi Sanofi + "sap", // sap SAP AG + "sapo", // sapo PT Comunicacoes S.A. + "sarl", // sarl Delta Orchard, LLC + "sas", // sas Research IP LLC + "saxo", // saxo Saxo Bank A/S + "sbi", // sbi STATE BANK OF INDIA + "sbs", // sbs SPECIAL BROADCASTING SERVICE CORPORATION + "sca", // sca SVENSKA CELLULOSA AKTIEBOLAGET SCA (publ) + "scb", // scb The Siam Commercial Bank Public Company Limited ("SCB") + "schaeffler", // schaeffler Schaeffler Technologies AG & Co. KG + "schmidt", // schmidt SALM S.A.S. + "scholarships", // scholarships Scholarships.com, LLC + "school", // school Little Galley, LLC + "schule", // schule Outer Moon, LLC + "schwarz", // schwarz Schwarz Domains und Services GmbH & Co. KG + "science", // science dot Science Limited + "scor", // scor SCOR SE + "scot", // scot Dot Scot Registry Limited + "seat", // seat SEAT, S.A. (Sociedad Unipersonal) + "security", // security XYZ.COM LLC + "seek", // seek Seek Limited + "select", // select iSelect Ltd + "sener", // sener Sener Ingeniería y Sistemas, S.A. + "services", // services Fox Castle, LLC + "seven", // seven Seven West Media Ltd + "sew", // sew SEW-EURODRIVE GmbH & Co KG + "sex", // sex ICM Registry SX LLC + "sexy", // sexy Uniregistry, Corp. + "sfr", // sfr Societe Francaise du Radiotelephone - SFR + "sharp", // sharp Sharp Corporation + "shaw", // shaw Shaw Cablesystems G.P. + "shell", // shell Shell Information Technology International Inc + "shia", // shia Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "shiksha", // shiksha Afilias Limited + "shoes", // shoes Binky Galley, LLC + "shouji", // shouji QIHOO 360 TECHNOLOGY CO. LTD. + "show", // show Snow Beach, LLC + "shriram", // shriram Shriram Capital Ltd. + "sina", // sina Sina Corporation + "singles", // singles Fern Madison, LLC + "site", // site DotSite Inc. + "ski", // ski STARTING DOT LIMITED + "skin", // skin L'Oréal + "sky", // sky Sky International AG + "skype", // skype Microsoft Corporation + "smile", // smile Amazon Registry Services, Inc. + "sncf", // sncf SNCF (Société Nationale des Chemins de fer Francais) + "soccer", // soccer Foggy Shadow, LLC + "social", // social United TLD Holdco Ltd. + "softbank", // softbank SoftBank Group Corp. + "software", // software United TLD Holdco, Ltd + "sohu", // sohu Sohu.com Limited + "solar", // solar Ruby Town, LLC + "solutions", // solutions Silver Cover, LLC + "song", // song Amazon Registry Services, Inc. + "sony", // sony Sony Corporation + "soy", // soy Charleston Road Registry Inc. + "space", // space DotSpace Inc. + "spiegel", // spiegel SPIEGEL-Verlag Rudolf Augstein GmbH & Co. KG + "spot", // spot Amazon Registry Services, Inc. + "spreadbetting", // spreadbetting DOTSPREADBETTING REGISTRY LTD + "srl", // srl InterNetX Corp. + "stada", // stada STADA Arzneimittel AG + "star", // star Star India Private Limited + "starhub", // starhub StarHub Limited + "statebank", // statebank STATE BANK OF INDIA + "statefarm", // statefarm State Farm Mutual Automobile Insurance Company + "statoil", // statoil Statoil ASA + "stc", // stc Saudi Telecom Company + "stcgroup", // stcgroup Saudi Telecom Company + "stockholm", // stockholm Stockholms kommun + "storage", // storage Self Storage Company LLC + "store", // store DotStore Inc. + "stream", // stream dot Stream Limited + "studio", // studio United TLD Holdco Ltd. + "study", // study OPEN UNIVERSITIES AUSTRALIA PTY LTD + "style", // style Binky Moon, LLC + "sucks", // sucks Vox Populi Registry Ltd. + "supplies", // supplies Atomic Fields, LLC + "supply", // supply Half Falls, LLC + "support", // support Grand Orchard, LLC + "surf", // surf Top Level Domain Holdings Limited + "surgery", // surgery Tin Avenue, LLC + "suzuki", // suzuki SUZUKI MOTOR CORPORATION + "swatch", // swatch The Swatch Group Ltd + "swiss", // swiss Swiss Confederation + "sydney", // sydney State of New South Wales, Department of Premier and Cabinet + "symantec", // symantec Symantec Corporation + "systems", // systems Dash Cypress, LLC + "tab", // tab Tabcorp Holdings Limited + "taipei", // taipei Taipei City Government + "talk", // talk Amazon Registry Services, Inc. + "taobao", // taobao Alibaba Group Holding Limited + "tatamotors", // tatamotors Tata Motors Ltd + "tatar", // tatar Limited Liability Company "Coordination Center of Regional + // Domain of Tatarstan Republic" + "tattoo", // tattoo Uniregistry, Corp. + "tax", // tax Storm Orchard, LLC + "taxi", // taxi Pine Falls, LLC + "tci", // tci Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "team", // team Atomic Lake, LLC + "tech", // tech Dot Tech LLC + "technology", // technology Auburn Falls, LLC + "tel", // tel Telnic Ltd. + "telecity", // telecity TelecityGroup International Limited + "telefonica", // telefonica Telefónica S.A. + "temasek", // temasek Temasek Holdings (Private) Limited + "tennis", // tennis Cotton Bloom, LLC + "teva", // teva Teva Pharmaceutical Industries Limited + "thd", // thd Homer TLC, Inc. + "theater", // theater Blue Tigers, LLC + "theatre", // theatre XYZ.COM LLC + "tickets", // tickets Accent Media Limited + "tienda", // tienda Victor Manor, LLC + "tiffany", // tiffany Tiffany and Company + "tips", // tips Corn Willow, LLC + "tires", // tires Dog Edge, LLC + "tirol", // tirol punkt Tirol GmbH + "tmall", // tmall Alibaba Group Holding Limited + "today", // today Pearl Woods, LLC + "tokyo", // tokyo GMO Registry, Inc. + "tools", // tools Pioneer North, LLC + "top", // top Jiangsu Bangning Science & Technology Co.,Ltd. + "toray", // toray Toray Industries, Inc. + "toshiba", // toshiba TOSHIBA Corporation + "total", // total Total SA + "tours", // tours Sugar Station, LLC + "town", // town Koko Moon, LLC + "toyota", // toyota TOYOTA MOTOR CORPORATION + "toys", // toys Pioneer Orchard, LLC + "trade", // trade Elite Registry Limited + "trading", // trading DOTTRADING REGISTRY LTD + "training", // training Wild Willow, LLC + "travel", // travel Tralliance Registry Management Company, LLC. + "travelers", // travelers Travelers TLD, LLC + "travelersinsurance", // travelersinsurance Travelers TLD, LLC + "trust", // trust Artemis Internet Inc + "trv", // trv Travelers TLD, LLC + "tube", // tube Latin American Telecom LLC + "tui", // tui TUI AG + "tunes", // tunes Amazon Registry Services, Inc. + "tushu", // tushu Amazon Registry Services, Inc. + "tvs", // tvs T V SUNDRAM IYENGAR & SONS PRIVATE LIMITED + "ubs", // ubs UBS AG + "unicom", // unicom China United Network Communications Corporation Limited + "university", // university Little Station, LLC + "uno", // uno Dot Latin LLC + "uol", // uol UBN INTERNET LTDA. + "vacations", // vacations Atomic Tigers, LLC + "vana", // vana Lifestyle Domain Holdings, Inc. + "vegas", // vegas Dot Vegas, Inc. + "ventures", // ventures Binky Lake, LLC + "verisign", // verisign VeriSign, Inc. + "versicherung", // versicherung dotversicherung-registry GmbH + "vet", // vet United TLD Holdco, Ltd + "viajes", // viajes Black Madison, LLC + "video", // video United TLD Holdco, Ltd + "vig", // vig VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe + "viking", // viking Viking River Cruises (Bermuda) Ltd. + "villas", // villas New Sky, LLC + "vin", // vin Holly Shadow, LLC + "vip", // vip Minds + Machines Group Limited + "virgin", // virgin Virgin Enterprises Limited + "vision", // vision Koko Station, LLC + "vista", // vista Vistaprint Limited + "vistaprint", // vistaprint Vistaprint Limited + "viva", // viva Saudi Telecom Company + "vlaanderen", // vlaanderen DNS.be vzw + "vodka", // vodka Top Level Domain Holdings Limited + "volkswagen", // volkswagen Volkswagen Group of America Inc. + "vote", // vote Monolith Registry LLC + "voting", // voting Valuetainment Corp. + "voto", // voto Monolith Registry LLC + "voyage", // voyage Ruby House, LLC + "vuelos", // vuelos Travel Reservations SRL + "wales", // wales Nominet UK + "walter", // walter Sandvik AB + "wang", // wang Zodiac Registry Limited + "wanggou", // wanggou Amazon Registry Services, Inc. + "watch", // watch Sand Shadow, LLC + "watches", // watches Richemont DNS Inc. + "weather", // weather The Weather Channel, LLC + "weatherchannel", // weatherchannel The Weather Channel, LLC + "webcam", // webcam dot Webcam Limited + "weber", // weber Saint-Gobain Weber SA + "website", // website DotWebsite Inc. + "wed", // wed Atgron, Inc. + "wedding", // wedding Top Level Domain Holdings Limited + "weibo", // weibo Sina Corporation + "weir", // weir Weir Group IP Limited + "whoswho", // whoswho Who's Who Registry + "wien", // wien punkt.wien GmbH + "wiki", // wiki Top Level Design, LLC + "williamhill", // williamhill William Hill Organization Limited + "win", // win First Registry Limited + "windows", // windows Microsoft Corporation + "wine", // wine June Station, LLC + "wme", // wme William Morris Endeavor Entertainment, LLC + "wolterskluwer", // wolterskluwer Wolters Kluwer N.V. + "work", // work Top Level Domain Holdings Limited + "works", // works Little Dynamite, LLC + "world", // world Bitter Fields, LLC + "wtc", // wtc World Trade Centers Association, Inc. + "wtf", // wtf Hidden Way, LLC + "xbox", // xbox Microsoft Corporation + "xerox", // xerox Xerox DNHC LLC + "xihuan", // xihuan QIHOO 360 TECHNOLOGY CO. LTD. + "xin", // xin Elegant Leader Limited + "xn--11b4c3d", // कॉम VeriSign Sarl + "xn--1ck2e1b", // セール Amazon Registry Services, Inc. + "xn--1qqw23a", // 佛山 Guangzhou YU Wei Information Technology Co., Ltd. + "xn--30rr7y", // 慈善 Excellent First Limited + "xn--3bst00m", // 集团 Eagle Horizon Limited + "xn--3ds443g", // 在线 TLD REGISTRY LIMITED + "xn--3pxu8k", // 点看 VeriSign Sarl + "xn--42c2d9a", // คอม VeriSign Sarl + "xn--45q11c", // 八卦 Zodiac Scorpio Limited + "xn--4gbrim", // موقع Suhub Electronic Establishment + "xn--55qw42g", // 公益 China Organizational Name Administration Center + "xn--55qx5d", // 公司 Computer Network Information Center of Chinese Academy of + // Sciences (China Internet Network Information Center) + "xn--5tzm5g", // 网站 Global Website TLD Asia Limited + "xn--6frz82g", // 移动 Afilias Limited + "xn--6qq986b3xl", // 我爱你 Tycoon Treasure Limited + "xn--80adxhks", // москва Foundation for Assistance for Internet Technologies and + // Infrastructure Development (FAITID) + "xn--80asehdb", // онлайн CORE Association + "xn--80aswg", // сайт CORE Association + "xn--8y0a063a", // 联通 China United Network Communications Corporation Limited + "xn--9dbq2a", // קום VeriSign Sarl + "xn--9et52u", // 时尚 RISE VICTORY LIMITED + "xn--9krt00a", // 微博 Sina Corporation + "xn--b4w605ferd", // 淡马锡 Temasek Holdings (Private) Limited + "xn--bck1b9a5dre4c", // ファッション Amazon Registry Services, Inc. + "xn--c1avg", // орг Public Interest Registry + "xn--c2br7g", // नेट VeriSign Sarl + "xn--cck2b3b", // ストア Amazon Registry Services, Inc. + "xn--cg4bki", // 삼성 SAMSUNG SDS CO., LTD + "xn--czr694b", // 商标 HU YI GLOBAL INFORMATION RESOURCES(HOLDING) COMPANY.HONGKONG + // LIMITED + "xn--czrs0t", // 商店 Wild Island, LLC + "xn--czru2d", // 商城 Zodiac Aquarius Limited + "xn--d1acj3b", // дети The Foundation for Network Initiatives “The Smart Internet” + "xn--eckvdtc9d", // ポイント Amazon Registry Services, Inc. + "xn--efvy88h", // 新闻 Xinhua News Agency Guangdong Branch 新华通讯社广东分社 + "xn--estv75g", // 工行 Industrial and Commercial Bank of China Limited + "xn--fct429k", // 家電 Amazon Registry Services, Inc. + "xn--fhbei", // كوم VeriSign Sarl + "xn--fiq228c5hs", // 中文网 TLD REGISTRY LIMITED + "xn--fiq64b", // 中信 CITIC Group Corporation + "xn--fjq720a", // 娱乐 Will Bloom, LLC + "xn--flw351e", // 谷歌 Charleston Road Registry Inc. + "xn--g2xx48c", // 购物 Minds + Machines Group Limited + "xn--gckr3f0f", // クラウド Amazon Registry Services, Inc. + "xn--hxt814e", // 网店 Zodiac Libra Limited + "xn--i1b6b1a6a2e", // संगठन Public Interest Registry + "xn--imr513n", // 餐厅 HU YI GLOBAL INFORMATION RESOURCES (HOLDING) COMPANY. HONGKONG + // LIMITED + "xn--io0a7i", // 网络 Computer Network Information Center of Chinese Academy of + // Sciences (China Internet Network Information Center) + "xn--j1aef", // ком VeriSign Sarl + "xn--jlq61u9w7b", // 诺基亚 Nokia Corporation + "xn--jvr189m", // 食品 Amazon Registry Services, Inc. + "xn--kcrx77d1x4a", // 飞利浦 Koninklijke Philips N.V. + "xn--kpu716f", // 手表 Richemont DNS Inc. + "xn--kput3i", // 手机 Beijing RITT-Net Technology Development Co., Ltd + "xn--mgba3a3ejt", // ارامكو Aramco Services Company + "xn--mgbab2bd", // بازار CORE Association + "xn--mgbb9fbpob", // موبايلي GreenTech Consultancy Company W.L.L. + "xn--mgbca7dzdo", // ابوظبي Abu Dhabi Systems and Information Centre + "xn--mgbt3dhd", // همراه Asia Green IT System Bilgisayar San. ve Tic. Ltd. Sti. + "xn--mk1bu44c", // 닷컴 VeriSign Sarl + "xn--mxtq1m", // 政府 Net-Chinese Co., Ltd. + "xn--ngbc5azd", // شبكة International Domain Registry Pty. Ltd. + "xn--ngbe9e0a", // بيتك Kuwait Finance House + "xn--nqv7f", // 机构 Public Interest Registry + "xn--nqv7fs00ema", // 组织机构 Public Interest Registry + "xn--nyqy26a", // 健康 Stable Tone Limited + "xn--p1acf", // рус Rusnames Limited + "xn--pbt977c", // 珠宝 Richemont DNS Inc. + "xn--pssy2u", // 大拿 VeriSign Sarl + "xn--q9jyb4c", // みんな Charleston Road Registry Inc. + "xn--qcka1pmc", // グーグル Charleston Road Registry Inc. + "xn--rhqv96g", // 世界 Stable Tone Limited + "xn--rovu88b", // 書籍 Amazon EU S.à r.l. + "xn--ses554g", // 网址 KNET Co., Ltd + "xn--t60b56a", // 닷넷 VeriSign Sarl + "xn--tckwe", // コム VeriSign Sarl + "xn--unup4y", // 游戏 Spring Fields, LLC + "xn--vermgensberater-ctb", // VERMöGENSBERATER Deutsche Vermögensberatung + // Aktiengesellschaft DVAG + "xn--vermgensberatung-pwb", // VERMöGENSBERATUNG Deutsche Vermögensberatung + // Aktiengesellschaft DVAG + "xn--vhquv", // 企业 Dash McCook, LLC + "xn--vuq861b", // 信息 Beijing Tele-info Network Technology Co., Ltd. + "xn--w4r85el8fhu5dnra", // 嘉里大酒店 Kerry Trading Co. Limited + "xn--xhq521b", // 广东 Guangzhou YU Wei Information Technology Co., Ltd. + "xn--zfr164b", // 政务 China Organizational Name Administration Center + "xperia", // xperia Sony Mobile Communications AB + "xxx", // xxx ICM Registry LLC + "xyz", // xyz XYZ.COM LLC + "yachts", // yachts DERYachts, LLC + "yahoo", // yahoo Yahoo! Domain Services Inc. + "yamaxun", // yamaxun Amazon Registry Services, Inc. + "yandex", // yandex YANDEX, LLC + "yodobashi", // yodobashi YODOBASHI CAMERA CO.,LTD. + "yoga", // yoga Top Level Domain Holdings Limited + "yokohama", // yokohama GMO Registry, Inc. + "you", // you Amazon Registry Services, Inc. + "youtube", // youtube Charleston Road Registry Inc. + "yun", // yun QIHOO 360 TECHNOLOGY CO. LTD. + "zara", // zara Industria de Diseño Textil, S.A. (INDITEX, S.A.) + "zero", // zero Amazon Registry Services, Inc. + "zip", // zip Charleston Road Registry Inc. + "zone", // zone Outer Falls, LLC + "zuerich", // zuerich Kanton Zürich (Canton of Zurich) + }; + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary + // search + private static final String[] COUNTRY_CODE_TLDS = + new String[] { + "ac", // Ascension Island + "ad", // Andorra + "ae", // United Arab Emirates + "af", // Afghanistan + "ag", // Antigua and Barbuda + "ai", // Anguilla + "al", // Albania + "am", // Armenia + // "an", // Netherlands Antilles (retired) + "ao", // Angola + "aq", // Antarctica + "ar", // Argentina + "as", // American Samoa + "at", // Austria + "au", // Australia (includes Ashmore and Cartier Islands and Coral Sea Islands) + "aw", // Aruba + "ax", // Åland + "az", // Azerbaijan + "ba", // Bosnia and Herzegovina + "bb", // Barbados + "bd", // Bangladesh + "be", // Belgium + "bf", // Burkina Faso + "bg", // Bulgaria + "bh", // Bahrain + "bi", // Burundi + "bj", // Benin + "bm", // Bermuda + "bn", // Brunei Darussalam + "bo", // Bolivia + "br", // Brazil + "bs", // Bahamas + "bt", // Bhutan + "bv", // Bouvet Island + "bw", // Botswana + "by", // Belarus + "bz", // Belize + "ca", // Canada + "cc", // Cocos (Keeling) Islands + "cd", // Democratic Republic of the Congo (formerly Zaire) + "cf", // Central African Republic + "cg", // Republic of the Congo + "ch", // Switzerland + "ci", // Côte d'Ivoire + "ck", // Cook Islands + "cl", // Chile + "cm", // Cameroon + "cn", // China, mainland + "co", // Colombia + "cr", // Costa Rica + "cu", // Cuba + "cv", // Cape Verde + "cw", // Curaçao + "cx", // Christmas Island + "cy", // Cyprus + "cz", // Czech Republic + "de", // Germany + "dj", // Djibouti + "dk", // Denmark + "dm", // Dominica + "do", // Dominican Republic + "dz", // Algeria + "ec", // Ecuador + "ee", // Estonia + "eg", // Egypt + "er", // Eritrea + "es", // Spain + "et", // Ethiopia + "eu", // European Union + "fi", // Finland + "fj", // Fiji + "fk", // Falkland Islands + "fm", // Federated States of Micronesia + "fo", // Faroe Islands + "fr", // France + "ga", // Gabon + "gb", // Great Britain (United Kingdom) + "gd", // Grenada + "ge", // Georgia + "gf", // French Guiana + "gg", // Guernsey + "gh", // Ghana + "gi", // Gibraltar + "gl", // Greenland + "gm", // The Gambia + "gn", // Guinea + "gp", // Guadeloupe + "gq", // Equatorial Guinea + "gr", // Greece + "gs", // South Georgia and the South Sandwich Islands + "gt", // Guatemala + "gu", // Guam + "gw", // Guinea-Bissau + "gy", // Guyana + "hk", // Hong Kong + "hm", // Heard Island and McDonald Islands + "hn", // Honduras + "hr", // Croatia (Hrvatska) + "ht", // Haiti + "hu", // Hungary + "id", // Indonesia + "ie", // Ireland (Éire) + "il", // Israel + "im", // Isle of Man + "in", // India + "io", // British Indian Ocean Territory + "iq", // Iraq + "ir", // Iran + "is", // Iceland + "it", // Italy + "je", // Jersey + "jm", // Jamaica + "jo", // Jordan + "jp", // Japan + "ke", // Kenya + "kg", // Kyrgyzstan + "kh", // Cambodia (Khmer) + "ki", // Kiribati + "km", // Comoros + "kn", // Saint Kitts and Nevis + "kp", // North Korea + "kr", // South Korea + "kw", // Kuwait + "ky", // Cayman Islands + "kz", // Kazakhstan + "la", // Laos (currently being marketed as the official domain for Los Angeles) + "lb", // Lebanon + "lc", // Saint Lucia + "li", // Liechtenstein + "lk", // Sri Lanka + "lr", // Liberia + "ls", // Lesotho + "lt", // Lithuania + "lu", // Luxembourg + "lv", // Latvia + "ly", // Libya + "ma", // Morocco + "mc", // Monaco + "md", // Moldova + "me", // Montenegro + "mg", // Madagascar + "mh", // Marshall Islands + "mk", // Republic of Macedonia + "ml", // Mali + "mm", // Myanmar + "mn", // Mongolia + "mo", // Macau + "mp", // Northern Mariana Islands + "mq", // Martinique + "mr", // Mauritania + "ms", // Montserrat + "mt", // Malta + "mu", // Mauritius + "mv", // Maldives + "mw", // Malawi + "mx", // Mexico + "my", // Malaysia + "mz", // Mozambique + "na", // Namibia + "nc", // New Caledonia + "ne", // Niger + "nf", // Norfolk Island + "ng", // Nigeria + "ni", // Nicaragua + "nl", // Netherlands + "no", // Norway + "np", // Nepal + "nr", // Nauru + "nu", // Niue + "nz", // New Zealand + "om", // Oman + "pa", // Panama + "pe", // Peru + "pf", // French Polynesia With Clipperton Island + "pg", // Papua New Guinea + "ph", // Philippines + "pk", // Pakistan + "pl", // Poland + "pm", // Saint-Pierre and Miquelon + "pn", // Pitcairn Islands + "pr", // Puerto Rico + "ps", // Palestinian territories (PA-controlled West Bank and Gaza Strip) + "pt", // Portugal + "pw", // Palau + "py", // Paraguay + "qa", // Qatar + "re", // Réunion + "ro", // Romania + "rs", // Serbia + "ru", // Russia + "rw", // Rwanda + "sa", // Saudi Arabia + "sb", // Solomon Islands + "sc", // Seychelles + "sd", // Sudan + "se", // Sweden + "sg", // Singapore + "sh", // Saint Helena + "si", // Slovenia + "sj", // Svalbard and Jan Mayen Islands Not in use (Norwegian dependencies; see .no) + "sk", // Slovakia + "sl", // Sierra Leone + "sm", // San Marino + "sn", // Senegal + "so", // Somalia + "sr", // Suriname + "st", // São Tomé and Príncipe + "su", // Soviet Union (deprecated) + "sv", // El Salvador + "sx", // Sint Maarten + "sy", // Syria + "sz", // Swaziland + "tc", // Turks and Caicos Islands + "td", // Chad + "tf", // French Southern and Antarctic Lands + "tg", // Togo + "th", // Thailand + "tj", // Tajikistan + "tk", // Tokelau + "tl", // East Timor (deprecated old code) + "tm", // Turkmenistan + "tn", // Tunisia + "to", // Tonga + // "tp", // East Timor (Retired) + "tr", // Turkey + "tt", // Trinidad and Tobago + "tv", // Tuvalu + "tw", // Taiwan, Republic of China + "tz", // Tanzania + "ua", // Ukraine + "ug", // Uganda + "uk", // United Kingdom + "us", // United States of America + "uy", // Uruguay + "uz", // Uzbekistan + "va", // Vatican City State + "vc", // Saint Vincent and the Grenadines + "ve", // Venezuela + "vg", // British Virgin Islands + "vi", // U.S. Virgin Islands + "vn", // Vietnam + "vu", // Vanuatu + "wf", // Wallis and Futuna + "ws", // Samoa (formerly Western Samoa) + "xn--3e0b707e", // 한국 KISA (Korea Internet & Security Agency) + "xn--45brj9c", // ভারত National Internet Exchange of India + "xn--80ao21a", // қаз Association of IT Companies of Kazakhstan + "xn--90a3ac", // срб Serbian National Internet Domain Registry (RNIDS) + "xn--90ais", // ??? Reliable Software Inc. + "xn--clchc0ea0b2g2a9gcd", // சிங்கப்பூர் Singapore Network Information Centre + // (SGNIC) Pte Ltd + "xn--d1alf", // мкд Macedonian Academic Research Network Skopje + "xn--e1a4c", // ею EURid vzw/asbl + "xn--fiqs8s", // 中国 China Internet Network Information Center + "xn--fiqz9s", // 中國 China Internet Network Information Center + "xn--fpcrj9c3d", // భారత్ National Internet Exchange of India + "xn--fzc2c9e2c", // ලංකා LK Domain Registry + "xn--gecrj9c", // ભારત National Internet Exchange of India + "xn--h2brj9c", // भारत National Internet Exchange of India + "xn--j1amh", // укр Ukrainian Network Information Centre (UANIC), Inc. + "xn--j6w193g", // 香港 Hong Kong Internet Registration Corporation Ltd. + "xn--kprw13d", // 台湾 Taiwan Network Information Center (TWNIC) + "xn--kpry57d", // 台灣 Taiwan Network Information Center (TWNIC) + "xn--l1acc", // мон Datacom Co.,Ltd + "xn--lgbbat1ad8j", // الجزائر CERIST + "xn--mgb9awbf", // عمان Telecommunications Regulatory Authority (TRA) + "xn--mgba3a4f16a", // ایران Institute for Research in Fundamental Sciences (IPM) + "xn--mgbaam7a8h", // امارات Telecommunications Regulatory Authority (TRA) + "xn--mgbayh7gpa", // الاردن National Information Technology Center (NITC) + "xn--mgbbh1a71e", // بھارت National Internet Exchange of India + "xn--mgbc0a9azcg", // المغرب Agence Nationale de Réglementation des + // Télécommunications (ANRT) + "xn--mgberp4a5d4ar", // السعودية Communications and Information Technology + // Commission + "xn--mgbpl2fh", // ????? Sudan Internet Society + "xn--mgbtx2b", // عراق Communications and Media Commission (CMC) + "xn--mgbx4cd0ab", // مليسيا MYNIC Berhad + "xn--mix891f", // 澳門 Bureau of Telecommunications Regulation (DSRT) + "xn--node", // გე Information Technologies Development Center (ITDC) + "xn--o3cw4h", // ไทย Thai Network Information Center Foundation + "xn--ogbpf8fl", // سورية National Agency for Network Services (NANS) + "xn--p1ai", // рф Coordination Center for TLD RU + "xn--pgbs0dh", // تونس Agence Tunisienne d'Internet + "xn--qxam", // ελ ICS-FORTH GR + "xn--s9brj9c", // ਭਾਰਤ National Internet Exchange of India + "xn--wgbh1c", // مصر National Telecommunication Regulatory Authority - NTRA + "xn--wgbl6a", // قطر Communications Regulatory Authority + "xn--xkc2al3hye2a", // இலங்கை LK Domain Registry + "xn--xkc2dl3a5ee0h", // இந்தியா National Internet Exchange of India + "xn--y9a3aq", // ??? Internet Society + "xn--yfro4i67o", // 新加坡 Singapore Network Information Centre (SGNIC) Pte Ltd + "xn--ygbi2ammx", // فلسطين Ministry of Telecom & Information Technology (MTIT) + "ye", // Yemen + "yt", // Mayotte + "za", // South Africa + "zm", // Zambia + "zw", // Zimbabwe + }; + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary + // search + private static final String[] LOCAL_TLDS = + new String[] { + "localdomain", // Also widely used as localhost.localdomain + "localhost", // RFC2606 defined + }; // Additional arrays to supplement or override the built in ones. // The PLUS arrays are valid keys, the MINUS arrays are invalid keys /* * This field is used to detect whether the getInstance has been called. * After this, the method updateTLDOverride is not allowed to be called. * This field does not need to be volatile since it is only accessed from - * synchronized methods. + * synchronized methods. */ private static boolean inUse = false; /* @@ -1576,28 +1584,43 @@ private String chompLeadingDot(String str) { * They can only be updated by the updateTLDOverride method, and any readers must get an instance * using the getInstance methods which are all (now) synchronised. */ - // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary + // search private static volatile String[] countryCodeTLDsPlus = EMPTY_STRING_ARRAY; - // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary + // search private static volatile String[] genericTLDsPlus = EMPTY_STRING_ARRAY; - // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary + // search private static volatile String[] countryCodeTLDsMinus = EMPTY_STRING_ARRAY; - // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary search + // WARNING: this array MUST be sorted, otherwise it cannot be searched reliably using binary + // search private static volatile String[] genericTLDsMinus = EMPTY_STRING_ARRAY; /** - * enum used by {@link DomainValidator#updateTLDOverride(ArrayType, String[])} - * to determine which override array to update / fetch + * enum used by {@link DomainValidator#updateTLDOverride(ArrayType, String[])} to determine + * which override array to update / fetch + * * @since 1.5.0 * @since 1.5.1 made public and added read-only array references */ public enum ArrayType { - /** Update (or get a copy of) the GENERIC_TLDS_PLUS table containing additonal generic TLDs */ + /** + * Update (or get a copy of) the GENERIC_TLDS_PLUS table containing additonal generic TLDs + */ GENERIC_PLUS, - /** Update (or get a copy of) the GENERIC_TLDS_MINUS table containing deleted generic TLDs */ + /** + * Update (or get a copy of) the GENERIC_TLDS_MINUS table containing deleted generic TLDs + */ GENERIC_MINUS, - /** Update (or get a copy of) the COUNTRY_CODE_TLDS_PLUS table containing additonal country code TLDs */ + /** + * Update (or get a copy of) the COUNTRY_CODE_TLDS_PLUS table containing additonal country + * code TLDs + */ COUNTRY_CODE_PLUS, - /** Update (or get a copy of) the COUNTRY_CODE_TLDS_MINUS table containing deleted country code TLDs */ + /** + * Update (or get a copy of) the COUNTRY_CODE_TLDS_MINUS table containing deleted country + * code TLDs + */ COUNTRY_CODE_MINUS, /** Get a copy of the generic TLDS table */ GENERIC_RO, @@ -1606,8 +1629,7 @@ public enum ArrayType { /** Get a copy of the infrastructure table */ INFRASTRUCTURE_RO, /** Get a copy of the local table */ - LOCAL_RO - ; + LOCAL_RO; }; // For use by unit test code only static synchronized void clearTLDOverrides() { @@ -1618,39 +1640,41 @@ static synchronized void clearTLDOverrides() { genericTLDsMinus = EMPTY_STRING_ARRAY; } /** - * Update one of the TLD override arrays. - * This must only be done at program startup, before any instances are accessed using getInstance. - *

- * For example: - *

- * {@code DomainValidator.updateTLDOverride(ArrayType.GENERIC_PLUS, new String[]{"apache"})} - *

- * To clear an override array, provide an empty array. + * Update one of the TLD override arrays. This must only be done at program startup, before any + * instances are accessed using getInstance. + * + *

For example: + * + *

{@code DomainValidator.updateTLDOverride(ArrayType.GENERIC_PLUS, new String[]{"apache"})} + * + *

To clear an override array, provide an empty array. + * + * @param table the table to update, see {@link DomainValidator.ArrayType} Must be one of the + * following + *

    + *
  • COUNTRY_CODE_MINUS + *
  • COUNTRY_CODE_PLUS + *
  • GENERIC_MINUS + *
  • GENERIC_PLUS + *
* - * @param table the table to update, see {@link DomainValidator.ArrayType} - * Must be one of the following - *
    - *
  • COUNTRY_CODE_MINUS
  • - *
  • COUNTRY_CODE_PLUS
  • - *
  • GENERIC_MINUS
  • - *
  • GENERIC_PLUS
  • - *
* @param tlds the array of TLDs, must not be null * @throws IllegalStateException if the method is called after getInstance * @throws IllegalArgumentException if one of the read-only tables is requested * @since 1.5.0 */ - public static synchronized void updateTLDOverride(ArrayType table, String [] tlds) { + public static synchronized void updateTLDOverride(ArrayType table, String[] tlds) { if (inUse) { - throw new IllegalStateException("Can only invoke this method before calling getInstance"); + throw new IllegalStateException( + "Can only invoke this method before calling getInstance"); } - String [] copy = new String[tlds.length]; + String[] copy = new String[tlds.length]; // Comparisons are always done with lower-case entries for (int i = 0; i < tlds.length; i++) { copy[i] = tlds[i].toLowerCase(Locale.ENGLISH); } Arrays.sort(copy); - switch(table) { + switch (table) { case COUNTRY_CODE_MINUS: countryCodeTLDsMinus = copy; break; @@ -1674,14 +1698,15 @@ public static synchronized void updateTLDOverride(ArrayType table, String [] tld } /** * Get a copy of the internal array. + * * @param table the array type (any of the enum values) * @return a copy of the array * @throws IllegalArgumentException if the table type is unexpected (should not happen) * @since 1.5.1 */ - public static String [] getTLDEntries(ArrayType table) { + public static String[] getTLDEntries(ArrayType table) { final String array[]; - switch(table) { + switch (table) { case COUNTRY_CODE_MINUS: array = countryCodeTLDsMinus; break; @@ -1712,8 +1737,8 @@ public static synchronized void updateTLDOverride(ArrayType table, String [] tld return Arrays.copyOf(array, array.length); // clone the array } /** - * Converts potentially Unicode input to punycode. - * If conversion fails, returns the original input. + * Converts potentially Unicode input to punycode. If conversion fails, returns the original + * input. * * @param input the string to convert, not null * @return converted input, or original input if conversion fails @@ -1729,7 +1754,7 @@ static String unicodeToASCII(String input) { return ascii; } final int length = input.length(); - if (length == 0) {// check there is a last character + if (length == 0) { // check there is a last character return input; } // RFC3490 3.1. 1) @@ -1737,8 +1762,8 @@ static String unicodeToASCII(String input) { // characters MUST be recognized as dots: U+002E (full stop), U+3002 // (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61 // (halfwidth ideographic full stop). - char lastChar = input.charAt(length-1);// fetch original last char - switch(lastChar) { + char lastChar = input.charAt(length - 1); // fetch original last char + switch (lastChar) { case '\u002E': // "." full stop case '\u3002': // ideographic full stop case '\uFF0E': // fullwidth full stop @@ -1751,11 +1776,13 @@ static String unicodeToASCII(String input) { return input; } } + private static class IDNBUGHOLDER { private static boolean keepsTrailingDot() { final String input = "a."; // must be a valid name return input.equals(IDN.toASCII(input)); } + private static final boolean IDN_TOASCII_PRESERVES_TRAILING_DOTS = keepsTrailingDot(); } /* @@ -1766,7 +1793,7 @@ private static boolean isOnlyASCII(String input) { if (input == null) { return true; } - for(int i=0; i < input.length(); i++) { + for (int i = 0; i < input.length(); i++) { if (input.charAt(i) > 0x7F) { // CHECKSTYLE IGNORE MagicNumber return false; } diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/PublicKeyPin.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/PublicKeyPin.java index 83a5419..7202c3a 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/PublicKeyPin.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/PublicKeyPin.java @@ -1,12 +1,11 @@ package com.datatheorem.android.trustkit.config; -import androidx.annotation.NonNull; import android.util.Base64; +import androidx.annotation.NonNull; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; - /** * A pin is the base64-encoded SHA-256 hash of the certificate's Subject Public Key Info, as * described in the HPKP RFC . @@ -51,5 +50,7 @@ public int hashCode() { @NonNull @Override - public String toString(){ return pin; } + public String toString() { + return pin; + } } diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/RegexValidator.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/RegexValidator.java index 53611dd..7e0935c 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/RegexValidator.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/RegexValidator.java @@ -1,4 +1,5 @@ -// TrustKit: Taken from https://apache.googlesource.com/commons-validator/+/VALIDATOR_1_5_1/src/main/java/org/apache/commons/validator/routines/RegexValidator.java +// TrustKit: Taken from +// https://apache.googlesource.com/commons-validator/+/VALIDATOR_1_5_1/src/main/java/org/apache/commons/validator/routines/RegexValidator.java package com.datatheorem.android.trustkit.config; /* @@ -21,15 +22,15 @@ import java.io.Serializable; import java.util.regex.Matcher; import java.util.regex.Pattern; + /** * Regular Expression validation (using JDK 1.4+ regex support). - *

- * Construct the validator either for a single regular expression or a set (array) of - * regular expressions. By default validation is case sensitive but constructors - * are provided to allow case in-sensitive validation. For example to create - * a validator which does case in-sensitive validation for a set of regular - * expressions: - *

+ * + *

Construct the validator either for a single regular expression or a set (array) of regular + * expressions. By default validation is case sensitive but constructors are provided to + * allow case in-sensitive validation. For example to create a validator which does case + * in-sensitive validation for a set of regular expressions: + * *

  * 
  * String[] regexs = new String[] {...};
@@ -38,32 +39,27 @@
  * 
* *
    - *
  • Validate true or false:
  • + *
  • Validate true or false: *
  • - *
      - *
    • boolean valid = validator.isValid(value);
    • - *
    - *
  • - *
  • Validate returning an aggregated String of the matched groups:
  • + *
      + *
    • boolean valid = validator.isValid(value); + *
    + *
  • Validate returning an aggregated String of the matched groups: *
  • - *
      - *
    • String result = validator.validate(value);
    • - *
    - *
  • - *
  • Validate returning the matched groups:
  • + *
      + *
    • String result = validator.validate(value); + *
    + *
  • Validate returning the matched groups: *
  • - *
      - *
    • String[] result = validator.match(value);
    • - *
    - *
  • + *
      + *
    • String[] result = validator.match(value); + *
    *
* * Note that patterns are matched against the entire input. * - *

- * Cached instances pre-compile and re-use {@link Pattern}(s) - which according - * to the {@link Pattern} API are safe to use in a multi-threaded environment. - *

+ *

Cached instances pre-compile and re-use {@link Pattern}(s) - which according to the {@link + * Pattern} API are safe to use in a multi-threaded environment. * * @version $Revision$ * @since Validator 1.4 @@ -73,65 +69,58 @@ class RegexValidator implements Serializable { private static final long serialVersionUID = -8832409930574867162L; private final Pattern[] patterns; /** - * Construct a case sensitive validator for a single - * regular expression. + * Construct a case sensitive validator for a single regular expression. * - * @param regex The regular expression this validator will - * validate against + * @param regex The regular expression this validator will validate against */ public RegexValidator(String regex) { this(regex, true); } /** - * Construct a validator for a single regular expression - * with the specified case sensitivity. + * Construct a validator for a single regular expression with the specified case sensitivity. * - * @param regex The regular expression this validator will - * validate against - * @param caseSensitive when true matching is case - * sensitive, otherwise matching is case in-sensitive + * @param regex The regular expression this validator will validate against + * @param caseSensitive when true matching is case sensitive, otherwise + * matching is case in-sensitive */ public RegexValidator(String regex, boolean caseSensitive) { this(new String[] {regex}, caseSensitive); } /** - * Construct a case sensitive validator that matches any one - * of the set of regular expressions. + * Construct a case sensitive validator that matches any one of the set of regular + * expressions. * - * @param regexs The set of regular expressions this validator will - * validate against + * @param regexs The set of regular expressions this validator will validate against */ public RegexValidator(String[] regexs) { this(regexs, true); } /** - * Construct a validator that matches any one of the set of regular - * expressions with the specified case sensitivity. + * Construct a validator that matches any one of the set of regular expressions with the + * specified case sensitivity. * - * @param regexs The set of regular expressions this validator will - * validate against - * @param caseSensitive when true matching is case - * sensitive, otherwise matching is case in-sensitive + * @param regexs The set of regular expressions this validator will validate against + * @param caseSensitive when true matching is case sensitive, otherwise + * matching is case in-sensitive */ public RegexValidator(String[] regexs, boolean caseSensitive) { if (regexs == null || regexs.length == 0) { throw new IllegalArgumentException("Regular expressions are missing"); } patterns = new Pattern[regexs.length]; - int flags = (caseSensitive ? 0: Pattern.CASE_INSENSITIVE); + int flags = (caseSensitive ? 0 : Pattern.CASE_INSENSITIVE); for (int i = 0; i < regexs.length; i++) { if (regexs[i] == null || regexs[i].length() == 0) { throw new IllegalArgumentException("Regular expression[" + i + "] is missing"); } - patterns[i] = Pattern.compile(regexs[i], flags); + patterns[i] = Pattern.compile(regexs[i], flags); } } /** * Validate a value against the set of regular expressions. * * @param value The value to validate. - * @return true if the value is valid - * otherwise false. + * @return true if the value is valid otherwise false. */ public boolean isValid(String value) { if (value == null) { @@ -145,12 +134,11 @@ public boolean isValid(String value) { return false; } /** - * Validate a value against the set of regular expressions - * returning the array of matched groups. + * Validate a value against the set of regular expressions returning the array of matched + * groups. * * @param value The value to validate. - * @return String array of the groups matched if - * valid or null if invalid + * @return String array of the groups matched if valid or null if invalid */ public String[] match(String value) { if (value == null) { @@ -162,7 +150,7 @@ public String[] match(String value) { int count = matcher.groupCount(); String[] groups = new String[count]; for (int j = 0; j < count; j++) { - groups[j] = matcher.group(j+1); + groups[j] = matcher.group(j + 1); } return groups; } @@ -170,12 +158,12 @@ public String[] match(String value) { return null; } /** - * Validate a value against the set of regular expressions - * returning a String value of the aggregated groups. + * Validate a value against the set of regular expressions returning a String value of the + * aggregated groups. * * @param value The value to validate. - * @return Aggregated String value comprised of the - * groups matched if valid or null if invalid + * @return Aggregated String value comprised of the groups matched if valid or null + * if invalid */ public String validate(String value) { if (value == null) { @@ -190,7 +178,7 @@ public String validate(String value) { } StringBuilder buffer = new StringBuilder(); for (int j = 0; j < count; j++) { - String component = matcher.group(j+1); + String component = matcher.group(j + 1); if (component != null) { buffer.append(component); } @@ -202,6 +190,7 @@ public String validate(String value) { } /** * Provide a String representation of this validator. + * * @return A String representation of this validator */ @Override diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfiguration.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfiguration.java index 93e4b73..e755231 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfiguration.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfiguration.java @@ -11,7 +11,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; - public class TrustKitConfiguration { @NonNull private final Set domainPolicies; @@ -21,14 +20,12 @@ public class TrustKitConfiguration { private final boolean shouldOverridePins; @Nullable private final Set debugCaCertificates; - public static TrustKitConfiguration fromXmlPolicy( - @NonNull Context context, @NonNull XmlPullParser parser - ) throws CertificateException, XmlPullParserException, IOException { + @NonNull Context context, @NonNull XmlPullParser parser) + throws CertificateException, XmlPullParserException, IOException { return TrustKitConfigurationParser.fromXmlPolicy(context, parser); } - protected TrustKitConfiguration(@NonNull Set domainConfigSet) { this(domainConfigSet, false, null); } @@ -36,13 +33,13 @@ protected TrustKitConfiguration(@NonNull Set domainConfigSe protected TrustKitConfiguration( @NonNull Set domainConfigSet, boolean shouldOverridePins, - @Nullable Set debugCaCerts - ) { + @Nullable Set debugCaCerts) { Set hostnameSet = new HashSet<>(); for (DomainPinningPolicy domainConfig : domainConfigSet) { if (hostnameSet.contains(domainConfig.getHostname())) { - throw new ConfigurationException("Policy contains the same domain defined twice: " - + domainConfig.getHostname()); + throw new ConfigurationException( + "Policy contains the same domain defined twice: " + + domainConfig.getHostname()); } hostnameSet.add(domainConfig.getHostname()); } @@ -70,13 +67,13 @@ public Set getAllPolicies() { } /** - * Get the {@link DomainPinningPolicy} corresponding to the provided hostname. - * When matching the most specific matching domain rule will be used, if no match exists - * then null will be returned. + * Get the {@link DomainPinningPolicy} corresponding to the provided hostname. When matching the + * most specific matching domain rule will be used, if no match exists then null will be + * returned. * * @param serverHostname the server's hostname * @return DomainPinningPolicy the domain's policy or null if the supplied hostname has no - * policy defined + * policy defined */ @Nullable public DomainPinningPolicy getPolicyForHostname(@NonNull String serverHostname) { @@ -100,7 +97,8 @@ public DomainPinningPolicy getPolicyForHostname(@NonNull String serverHostname) && isSubdomain(domainPolicy.getHostname(), serverHostname)) { if (bestMatchPolicy == null) { bestMatchPolicy = domainPolicy; - } else if (domainPolicy.getHostname().length() > bestMatchPolicy.getHostname().length()) { + } else if (domainPolicy.getHostname().length() + > bestMatchPolicy.getHostname().length()) { bestMatchPolicy = domainPolicy; } } @@ -109,8 +107,8 @@ && isSubdomain(domainPolicy.getHostname(), serverHostname)) { } /** - * Return true for all subdomains, including subdomains of subdomains, similar to how - * Android N handles includeSubdomains + * Return true for all subdomains, including subdomains of subdomains, similar to how Android N + * handles includeSubdomains */ private static boolean isSubdomain(@NonNull String domain, @NonNull String subdomain) { return subdomain.endsWith(domain) diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfigurationParser.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfigurationParser.java index 2c1f567..aca8dfa 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfigurationParser.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/config/TrustKitConfigurationParser.java @@ -1,10 +1,8 @@ package com.datatheorem.android.trustkit.config; - import android.content.Context; -import androidx.annotation.NonNull; import android.text.TextUtils; - +import androidx.annotation.NonNull; import com.datatheorem.android.trustkit.utils.TrustKitLog; import java.io.IOException; import java.io.InputStream; @@ -22,17 +20,16 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; - class TrustKitConfigurationParser { /** - * Parse an XML TrustKit / Network Security policy and return the corresponding - * {@link TrustKitConfiguration}. + * Parse an XML TrustKit / Network Security policy and return the corresponding {@link + * TrustKitConfiguration}. */ @NonNull public static TrustKitConfiguration fromXmlPolicy( - @NonNull Context context, @NonNull XmlPullParser parser - ) throws XmlPullParserException, IOException, CertificateException { + @NonNull Context context, @NonNull XmlPullParser parser) + throws XmlPullParserException, IOException, CertificateException { // Handle nested domain config tags // https://developer.android.com/training/articles/security-config.html#ConfigInheritance List builderList = new ArrayList<>(); @@ -64,11 +61,11 @@ public static TrustKitConfiguration fromXmlPolicy( } if (debugOverridesTag != null) { - config = new TrustKitConfiguration( - domainConfigSet, - debugOverridesTag.overridePins, - debugOverridesTag.debugCaCertificates - ); + config = + new TrustKitConfiguration( + domainConfigSet, + debugOverridesTag.overridePins, + debugOverridesTag.debugCaCertificates); } else { config = new TrustKitConfiguration(domainConfigSet); } @@ -78,12 +75,12 @@ public static TrustKitConfiguration fromXmlPolicy( // Heavily inspired from // https://github.com/android/platform_frameworks_base/blob/master/core/java/android/security/net/config/XmlConfigSource.java private static List readDomainConfig( - XmlPullParser parser, DomainPinningPolicy.Builder parentBuilder - ) throws XmlPullParserException, IOException { + XmlPullParser parser, DomainPinningPolicy.Builder parentBuilder) + throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, null, "domain-config"); - DomainPinningPolicy.Builder builder = new DomainPinningPolicy.Builder() - .setParent(parentBuilder); + DomainPinningPolicy.Builder builder = + new DomainPinningPolicy.Builder().setParent(parentBuilder); List builderList = new ArrayList<>(); // Put the current builder as the first one in the list, so the parent always gets built @@ -91,7 +88,8 @@ private static List readDomainConfig( builderList.add(builder); int eventType = parser.next(); - while (!((eventType == XmlPullParser.END_TAG) && "domain-config".equals(parser.getName()))) { + while (!((eventType == XmlPullParser.END_TAG) + && "domain-config".equals(parser.getName()))) { if (eventType == XmlPullParser.START_TAG) { if ("domain-config".equals(parser.getName())) { // Nested domain configuration tag @@ -122,14 +120,15 @@ private static class PinSetTag { } @NonNull - private static PinSetTag readPinSet(@NonNull XmlPullParser parser) throws IOException, - XmlPullParserException { + private static PinSetTag readPinSet(@NonNull XmlPullParser parser) + throws IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, null, "pin-set"); PinSetTag pinSetTag = new PinSetTag(); pinSetTag.pins = new HashSet<>(); // Look for the expiration attribute - // Taken from https://github.com/android/platform_frameworks_base/blob/master/core/java/android/security/net/config/XmlConfigSource.java + // Taken from + // https://github.com/android/platform_frameworks_base/blob/master/core/java/android/security/net/config/XmlConfigSource.java String expirationDate = parser.getAttributeValue(null, "expiration"); if (expirationDate != null) { try { @@ -178,7 +177,6 @@ private static TrustkitConfigTag readTrustkitConfig(@NonNull XmlPullParser parse TrustkitConfigTag result = new TrustkitConfigTag(); Set reportUris = new HashSet<>(); - // Look for the enforcePinning attribute String enforcePinning = parser.getAttributeValue(null, "enforcePinning"); if (enforcePinning != null) { @@ -193,7 +191,8 @@ private static TrustkitConfigTag readTrustkitConfig(@NonNull XmlPullParser parse // Parse until the corresponding close trustkit-config tag int eventType = parser.next(); - while (!((eventType == XmlPullParser.END_TAG) && "trustkit-config".equals(parser.getName()))) { + while (!((eventType == XmlPullParser.END_TAG) + && "trustkit-config".equals(parser.getName()))) { // Look for the next report-uri tag if ((eventType == XmlPullParser.START_TAG) && "report-uri".equals(parser.getName())) { // Found one - parse the report-uri value @@ -212,8 +211,8 @@ private static class DomainTag { } @NonNull - private static DomainTag readDomain(@NonNull XmlPullParser parser) throws IOException, - XmlPullParserException { + private static DomainTag readDomain(@NonNull XmlPullParser parser) + throws IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, null, "domain"); DomainTag result = new DomainTag(); @@ -234,8 +233,8 @@ private static class DebugOverridesTag { } @NonNull - private static DebugOverridesTag readDebugOverrides(@NonNull Context context, - @NonNull XmlPullParser parser) + private static DebugOverridesTag readDebugOverrides( + @NonNull Context context, @NonNull XmlPullParser parser) throws CertificateException, IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, null, "debug-overrides"); DebugOverridesTag result = new DebugOverridesTag(); @@ -243,7 +242,8 @@ private static DebugOverridesTag readDebugOverrides(@NonNull Context context, Set debugCaCertificates = new HashSet<>(); int eventType = parser.next(); - while (!((eventType == XmlPullParser.END_TAG) && "trust-anchors".equals(parser.getName()))) { + while (!((eventType == XmlPullParser.END_TAG) + && "trust-anchors".equals(parser.getName()))) { // Look for the next certificates tag if ((eventType == XmlPullParser.START_TAG) && "certificates".equals(parser.getName())) { // For simplicity, we only support one global overridePins setting, where Android N @@ -253,10 +253,11 @@ private static DebugOverridesTag readDebugOverrides(@NonNull Context context, if ((lastOverridePinsEncountered != null) && (lastOverridePinsEncountered != currentOverridePins)) { lastOverridePinsEncountered = false; - TrustKitLog.w("Warning: different values for overridePins are set in the " + - "policy but TrustKit only supports one value; using " + - "overridePins=false for all " + - "connections"); + TrustKitLog.w( + "Warning: different values for overridePins are set in the " + + "policy but TrustKit only supports one value; using " + + "overridePins=false for all " + + "connections"); } else { lastOverridePinsEncountered = currentOverridePins; } @@ -268,22 +269,28 @@ private static DebugOverridesTag readDebugOverrides(@NonNull Context context, // Parse the path to the certificate bundle for src=@raw - we ignore system or user // as the src - if (!TextUtils.isEmpty(caPathFromUser) && !caPathFromUser.equals("user") - && !caPathFromUser.equals("system") && caPathFromUser.startsWith("@raw")) { + if (!TextUtils.isEmpty(caPathFromUser) + && !caPathFromUser.equals("user") + && !caPathFromUser.equals("system") + && caPathFromUser.startsWith("@raw")) { InputStream stream = - context.getResources().openRawResource( - context.getResources().getIdentifier( - caPathFromUser.split("/")[1], "raw", - context.getPackageName())); + context.getResources() + .openRawResource( + context.getResources() + .getIdentifier( + caPathFromUser.split("/")[1], + "raw", + context.getPackageName())); - debugCaCertificates.add(CertificateFactory.getInstance("X.509") - .generateCertificate(stream)); + debugCaCertificates.add( + CertificateFactory.getInstance("X.509").generateCertificate(stream)); } else { - TrustKitLog.i("No certificates found by TrustKit." + - " Please check your @raw folder " + - "(TrustKit doesn't support system and user installed certificates)."); + TrustKitLog.i( + "No certificates found by TrustKit." + + " Please check your @raw folder " + + "(TrustKit doesn't support system and user installed certificates)."); } } eventType = parser.next(); @@ -298,11 +305,15 @@ private static DebugOverridesTag readDebugOverrides(@NonNull Context context, return result; } - private static String formatCertPathResourceWhenId(@NonNull Context context, String caPathFromUser) { - if(TextUtils.isDigitsOnly(caPathFromUser.replace("@", ""))){ - caPathFromUser = "@" + context.getResources() - .getResourceName(Integer.parseInt(caPathFromUser.replace("@", ""))) - .replace(context.getPackageName()+":", ""); + private static String formatCertPathResourceWhenId( + @NonNull Context context, String caPathFromUser) { + if (TextUtils.isDigitsOnly(caPathFromUser.replace("@", ""))) { + caPathFromUser = + "@" + + context.getResources() + .getResourceName( + Integer.parseInt(caPathFromUser.replace("@", ""))) + .replace(context.getPackageName() + ":", ""); } return caPathFromUser; diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/DebugOverridesTrustManager.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/DebugOverridesTrustManager.java index 7b506d9..d24db80 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/DebugOverridesTrustManager.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/DebugOverridesTrustManager.java @@ -13,7 +13,6 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; - /** * Used when is enabled in the network security policy and we are on a pre-N * Android device (as Android N automatically takes care of this). It returns a trust manager that @@ -22,8 +21,8 @@ */ class DebugOverridesTrustManager { - public static X509TrustManager getInstance(Set debugCaCerts) throws - CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException { + public static X509TrustManager getInstance(Set debugCaCerts) + throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException { X509TrustManager debugTrustManager = null; // Create a KeyStore containing our trusted CAs and the Android user and system CAs @@ -36,13 +35,13 @@ public static X509TrustManager getInstance(Set debugCaCerts) throws while (aliases.hasMoreElements()) { String alias = (String) aliases.nextElement(); X509Certificate cert = (X509Certificate) systemKeyStore.getCertificate(alias); - keyStore.setCertificateEntry(alias , cert); + keyStore.setCertificateEntry(alias, cert); } // Add the extra debug CAs to the store for (Certificate caCert : debugCaCerts) { String alias = "debug: " + ((X509Certificate) caCert).getSubjectDN().getName(); - keyStore.setCertificateEntry(alias , caCert); + keyStore.setCertificateEntry(alias, caCert); } // Create a TrustManager that trusts the CAs in our KeyStore @@ -62,4 +61,4 @@ public static X509TrustManager getInstance(Set debugCaCerts) throws } return debugTrustManager; } -} \ No newline at end of file +} diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/DistinguishedNameParser.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/DistinguishedNameParser.java index 234ce61..b84efc6 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/DistinguishedNameParser.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/DistinguishedNameParser.java @@ -1,5 +1,6 @@ package com.datatheorem.android.trustkit.pinning; -// TrustKit: imported from https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/internal/tls/DistinguishedNameParser.java +// TrustKit: imported from +// https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/internal/tls/DistinguishedNameParser.java /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,9 +20,10 @@ */ import javax.security.auth.x500.X500Principal; + /** - * A distinguished name (DN) parser. This parser only supports extracting a - * string value from a DN. It doesn't support values in the hex-string style. + * A distinguished name (DN) parser. This parser only supports extracting a string value from a DN. + * It doesn't support values in the hex-string style. * * @hide */ @@ -36,6 +38,7 @@ final class DistinguishedNameParser { private int cur; /** distinguished name chars */ private char[] chars; + public DistinguishedNameParser(X500Principal principal) { // RFC2253 is used to ensure we get attributes in the reverse // order of the underlying ASN.1 encoding, so that the most @@ -47,8 +50,7 @@ public DistinguishedNameParser(X500Principal principal) { private String nextAT() { // skip preceding space chars, they can present after // comma or semicolon (compatibility with RFC 1779) - for (; pos < length && chars[pos] == ' '; pos++) { - } + for (; pos < length && chars[pos] == ' '; pos++) {} if (pos == length) { return null; // reached the end of DN } @@ -68,20 +70,19 @@ private String nextAT() { // skip trailing space chars between attribute type and '=' // (compatibility with RFC 1779) if (chars[pos] == ' ') { - for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) { - } + for (; pos < length && chars[pos] != '=' && chars[pos] == ' '; pos++) {} if (chars[pos] != '=' || pos == length) { throw new IllegalStateException("Unexpected end of DN: " + dn); } } - pos++; //skip '=' char + pos++; // skip '=' char // skip space chars between '=' and attribute value // (compatibility with RFC 1779) - for (; pos < length && chars[pos] == ' '; pos++) { - } + for (; pos < length && chars[pos] == ' '; pos++) {} // in case of oid attribute type skip its prefix: "oid." or "OID." // (compatibility with RFC 1779) - if ((end - beg > 4) && (chars[beg + 3] == '.') + if ((end - beg > 4) + && (chars[beg + 3] == '.') && (chars[beg] == 'O' || chars[beg] == 'o') && (chars[beg + 1] == 'I' || chars[beg + 1] == 'i') && (chars[beg + 2] == 'D' || chars[beg + 2] == 'd')) { @@ -113,8 +114,7 @@ private String quotedAV() { } // skip trailing space chars before comma or semicolon. // (compatibility with RFC 1779) - for (; pos < length && chars[pos] == ' '; pos++) { - } + for (; pos < length && chars[pos] == ' '; pos++) {} return new String(chars, beg, end - beg); } // gets hex string attribute value: "#" hexstring @@ -128,8 +128,7 @@ private String hexAV() { while (true) { // check for end of attribute value // looks for space and component separators - if (pos == length || chars[pos] == '+' || chars[pos] == ',' - || chars[pos] == ';') { + if (pos == length || chars[pos] == '+' || chars[pos] == ',' || chars[pos] == ';') { end = pos; break; } @@ -138,11 +137,10 @@ private String hexAV() { pos++; // skip trailing space chars before comma or semicolon. // (compatibility with RFC 1779) - for (; pos < length && chars[pos] == ' '; pos++) { - } + for (; pos < length && chars[pos] == ' '; pos++) {} break; } else if (chars[pos] >= 'A' && chars[pos] <= 'F') { - chars[pos] += 32; //to low case + chars[pos] += 32; // to low case } pos++; } @@ -188,7 +186,9 @@ private String escapedAV() { for (; pos < length && chars[pos] == ' '; pos++) { chars[end++] = ' '; } - if (pos == length || chars[pos] == ',' || chars[pos] == '+' + if (pos == length + || chars[pos] == ',' + || chars[pos] == '+' || chars[pos] == ';') { // separator char or the end of DN has been found return new String(chars, beg, cur - beg); @@ -220,7 +220,7 @@ private char getEscaped() { case '*': case '%': case '_': - //FIXME: escaping is allowed only for leading or trailing space char + // FIXME: escaping is allowed only for leading or trailing space char return chars[pos]; default: // RFC doesn't explicitly say that escaped hex pair is @@ -232,7 +232,7 @@ private char getEscaped() { // see http://www.unicode.org for UTF-8 bit distribution table private char getUTF8() { int res = getByte(pos); - pos++; //FIXME tmp + pos++; // FIXME tmp if (res < 128) { // one byte: 0-7F return (char) res; } else if (res >= 192 && res <= 247) { @@ -251,19 +251,19 @@ private char getUTF8() { for (int i = 0; i < count; i++) { pos++; if (pos == length || chars[pos] != '\\') { - return 0x3F; //FIXME failed to decode UTF-8 char - return '?' + return 0x3F; // FIXME failed to decode UTF-8 char - return '?' } pos++; b = getByte(pos); - pos++; //FIXME tmp + pos++; // FIXME tmp if ((b & 0xC0) != 0x80) { - return 0x3F; //FIXME failed to decode UTF-8 char - return '?' + return 0x3F; // FIXME failed to decode UTF-8 char - return '?' } res = (res << 6) + (b & 0x3F); } return (char) res; } else { - return 0x3F; //FIXME failed to decode UTF-8 char - return '?' + return 0x3F; // FIXME failed to decode UTF-8 char - return '?' } } // Returns byte representation of a char pair @@ -300,8 +300,8 @@ private int getByte(int position) { return (b1 << 4) + b2; } /** - * Parses the DN and returns the most significant attribute value - * for an attribute type, or null if none found. + * Parses the DN and returns the most significant attribute value for an attribute type, or null + * if none found. * * @param attributeType attribute type to look for (e.g. "ca") */ @@ -331,7 +331,7 @@ public String findMostSpecific(String attributeType) { case '+': case ',': case ';': // compatibility with RFC 1779: semicolon can separate RDNs - //empty attribute value + // empty attribute value break; default: attValue = escapedAV(); diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHostnameVerifier.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHostnameVerifier.java index 316aa64..bc08e71 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHostnameVerifier.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHostnameVerifier.java @@ -1,5 +1,6 @@ package com.datatheorem.android.trustkit.pinning; -// TrustKit: Imported from https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java +// TrustKit: Imported from +// https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java // Removed support for IP address certificates so we don't need to import more OkHttp files /* @@ -32,7 +33,6 @@ import javax.net.ssl.SSLSession; import javax.security.auth.x500.X500Principal; - /** * A HostnameVerifier consistent with RFC 2818. */ @@ -43,8 +43,7 @@ final class OkHostnameVerifier implements HostnameVerifier { private static final int ALT_DNS_NAME = 2; private static final int ALT_IPA_NAME = 7; - private OkHostnameVerifier() { - } + private OkHostnameVerifier() {} @Override public boolean verify(String host, SSLSession session) { @@ -58,8 +57,8 @@ public boolean verify(String host, SSLSession session) { public boolean verify(String host, X509Certificate certificate) { return Utils.verifyAsIpAddress(host) - ? verifyIpAddress(host, certificate) - : verifyHostname(host, certificate); + ? verifyIpAddress(host, certificate) + : verifyHostname(host, certificate); } /** Returns true if {@code certificate} matches {@code ipAddress}. */ @@ -140,17 +139,21 @@ private static List getSubjectAltNames(X509Certificate certificate, int * * @param hostname lower-case host name. * @param pattern domain name pattern from certificate. May be a wildcard pattern such as {@code - * *.android.com}. + * *.android.com}. */ private boolean verifyHostname(String hostname, String pattern) { // Basic sanity checks // Check length == 0 instead of .isEmpty() to support Java 5. - if ((hostname == null) || (hostname.length() == 0) || (hostname.startsWith(".")) + if ((hostname == null) + || (hostname.length() == 0) + || (hostname.startsWith(".")) || (hostname.endsWith(".."))) { // Invalid domain name return false; } - if ((pattern == null) || (pattern.length() == 0) || (pattern.startsWith(".")) + if ((pattern == null) + || (pattern.length() == 0) + || (pattern.startsWith(".")) || (pattern.endsWith(".."))) { // Invalid pattern/domain name return false; @@ -193,14 +196,17 @@ private boolean verifyHostname(String hostname, String pattern) { // 3. Wildcard patterns for single-label domain names are not permitted. if ((!pattern.startsWith("*.")) || (pattern.indexOf('*', 1) != -1)) { - // Asterisk (*) is only permitted in the left-most domain name label and must be the only + // Asterisk (*) is only permitted in the left-most domain name label and must be the + // only // character in that label return false; } - // Optimization: check whether hostname is too short to match the pattern. hostName must be at + // Optimization: check whether hostname is too short to match the pattern. hostName must be + // at // least as long as the pattern because asterisk must match the whole left-most label and - // hostname starts with a non-empty label. Thus, asterisk has to match one or more characters. + // hostname starts with a non-empty label. Thus, asterisk has to match one or more + // characters. if (hostname.length() < pattern.length()) { // hostname too short to match the pattern. return false; @@ -229,4 +235,4 @@ private boolean verifyHostname(String hostname, String pattern) { // hostname matches pattern return true; } -} \ No newline at end of file +} diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp2Helper.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp2Helper.java index c47fb40..42ea102 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp2Helper.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp2Helper.java @@ -1,22 +1,18 @@ package com.datatheorem.android.trustkit.pinning; import android.os.Build; - import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; - import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.Request; - import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; - import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; public class OkHttp2Helper { - private static final X509TrustManager trustManager; + private static X509TrustManager trustManager; static { if (Build.VERSION.SDK_INT < 17) { @@ -28,19 +24,17 @@ public class OkHttp2Helper { /** * Retrieve an {@code SSLSSocketFactory} that implements SSL pinning validation based on the - * current TrustKit configuration. It can be used with an OkHttpClient to add SSL - * pinning validation to the connections. + * current TrustKit configuration. It can be used with an OkHttpClient to add SSL pinning + * validation to the connections. * - *

- * The {@code SSLSocketFactory} is configured for the current TrustKit configuration and - * will enforce the configuration's pinning policy. - *

+ *

The {@code SSLSocketFactory} is configured for the current TrustKit configuration and will + * enforce the configuration's pinning policy. */ @NonNull public static SSLSocketFactory getSSLSocketFactory() { try { SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(null, new X509TrustManager[]{trustManager}, null); + sslContext.init(null, new X509TrustManager[] {trustManager}, null); return sslContext.getSocketFactory(); } catch (NoSuchAlgorithmException | KeyManagementException e) { @@ -50,13 +44,13 @@ public static SSLSocketFactory getSSLSocketFactory() { } /** - * Returns an {@link com.squareup.okhttp.Interceptor} used to parse the hostname of the - * {@link Request} URL and then save the hostname in the {@link OkHttpRootTrustManager} which will + * Returns an {@link com.squareup.okhttp.Interceptor} used to parse the hostname of the {@link + * Request} URL and then save the hostname in the {@link OkHttpRootTrustManager} which will * later be used for Certificate Pinning. */ @NonNull @RequiresApi(api = 17) public static Interceptor getPinningInterceptor() { - return new OkHttp2PinningInterceptor((OkHttpRootTrustManager)trustManager); + return new OkHttp2PinningInterceptor((OkHttpRootTrustManager) trustManager); } } diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp2PinningInterceptor.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp2PinningInterceptor.java index 3ba1636..0e7b3d6 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp2PinningInterceptor.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp2PinningInterceptor.java @@ -1,11 +1,9 @@ package com.datatheorem.android.trustkit.pinning; import androidx.annotation.NonNull; - import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.Request; import com.squareup.okhttp.Response; - import java.io.IOException; /** @@ -19,7 +17,8 @@ public OkHttp2PinningInterceptor(@NonNull OkHttpRootTrustManager trustManager) { mTrustManager = trustManager; } - @Override public Response intercept(Interceptor.Chain chain) throws IOException { + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { Request request = chain.request(); String serverHostname = request.url().getHost(); diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp3Helper.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp3Helper.java index 5219e0c..ae6ae54 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp3Helper.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp3Helper.java @@ -1,22 +1,18 @@ package com.datatheorem.android.trustkit.pinning; import android.os.Build; - import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; - import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; - import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; - import okhttp3.Interceptor; import okhttp3.Request; public class OkHttp3Helper { - private static final X509TrustManager trustManager; + private static X509TrustManager trustManager; static { if (Build.VERSION.SDK_INT < 17) { @@ -28,19 +24,17 @@ public class OkHttp3Helper { /** * Retrieve an {@code SSLSSocketFactory} that implements SSL pinning validation based on the - * current TrustKit configuration. It can be used with an OkHttpClient to add SSL - * pinning validation to the connections. + * current TrustKit configuration. It can be used with an OkHttpClient to add SSL pinning + * validation to the connections. * - *

- * The {@code SSLSocketFactory} is configured for the current TrustKit configuration and - * will enforce the configuration's pinning policy. - *

+ *

The {@code SSLSocketFactory} is configured for the current TrustKit configuration and will + * enforce the configuration's pinning policy. */ @NonNull public static SSLSocketFactory getSSLSocketFactory() { try { SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(null, new X509TrustManager[]{trustManager}, null); + sslContext.init(null, new X509TrustManager[] {trustManager}, null); return sslContext.getSocketFactory(); } catch (NoSuchAlgorithmException | KeyManagementException e) { @@ -57,12 +51,10 @@ public static SSLSocketFactory getSSLSocketFactory() { @NonNull @RequiresApi(api = 17) public static Interceptor getPinningInterceptor() { - return new OkHttp3PinningInterceptor((OkHttpRootTrustManager)trustManager); + return new OkHttp3PinningInterceptor((OkHttpRootTrustManager) trustManager); } - /** - * Returns an instance of the {@link OkHttpRootTrustManager} used for Certificate Pinning. - */ + /** Returns an instance of the {@link OkHttpRootTrustManager} used for Certificate Pinning. */ @NonNull public static X509TrustManager getTrustManager() { return trustManager; diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp3PinningInterceptor.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp3PinningInterceptor.java index b7a44e6..5bbc1c2 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp3PinningInterceptor.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttp3PinningInterceptor.java @@ -1,9 +1,7 @@ package com.datatheorem.android.trustkit.pinning; import androidx.annotation.NonNull; - import java.io.IOException; - import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; @@ -19,7 +17,8 @@ public OkHttp3PinningInterceptor(@NonNull OkHttpRootTrustManager trustManager) { mTrustManager = trustManager; } - @Override public Response intercept(Interceptor.Chain chain) throws IOException { + @Override + public Response intercept(Interceptor.Chain chain) throws IOException { Request request = chain.request(); String serverHostname = request.url().host(); diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttpRootTrustManager.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttpRootTrustManager.java index 4ef11a4..32173d2 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttpRootTrustManager.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/OkHttpRootTrustManager.java @@ -2,43 +2,44 @@ import android.net.http.X509TrustManagerExtensions; import android.os.Build; - import androidx.annotation.NonNull; - import com.datatheorem.android.trustkit.TrustKit; import com.datatheorem.android.trustkit.config.DomainPinningPolicy; - import java.security.cert.CertificateException; import java.security.cert.X509Certificate; - import javax.net.ssl.X509TrustManager; /** * {@link X509TrustManager} used for Certificate Pinning. * *

This trust manager delegates to the appropriate {@link PinningTrustManager} decided by the - * hostname set by the {@link OkHttp3PinningInterceptor}.

+ * hostname set by the {@link OkHttp3PinningInterceptor}. */ class OkHttpRootTrustManager implements X509TrustManager { private final ThreadLocal mServerHostname = new ThreadLocal<>(); @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - TrustKit.getInstance().getTrustManager(mServerHostname.get()).checkClientTrusted(chain, authType); + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + TrustKit.getInstance() + .getTrustManager(mServerHostname.get()) + .checkClientTrusted(chain, authType); } @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { String host = mServerHostname.get(); DomainPinningPolicy serverConfig = TrustKit.getInstance().getConfiguration().getPolicyForHostname(host); X509TrustManager trustManager = TrustKit.getInstance().getTrustManager(host); - //The first check is needed for compatibility with the Platform default's implementation of - //the Trust Manager. For APIs 24 and greater, the Platform's default TrustManager states - //that it requires usage of the hostname-aware version of checkServerTrusted for app's that - //implement Android's network_security_config file. The 2nd check is to allow usage of the - //X509TrustManagerExtensions class. Any API below will default to the baseline trust manager. + // The first check is needed for compatibility with the Platform default's implementation of + // the Trust Manager. For APIs 24 and greater, the Platform's default TrustManager states + // that it requires usage of the hostname-aware version of checkServerTrusted for app's that + // implement Android's network_security_config file. The 2nd check is to allow usage of the + // X509TrustManagerExtensions class. Any API below will default to the baseline trust + // manager. if (serverConfig == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { new X509TrustManagerExtensions(trustManager).checkServerTrusted(chain, authType, host); } else { diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/PinningTrustManager.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/PinningTrustManager.java index b26d08b..70ace88 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/PinningTrustManager.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/PinningTrustManager.java @@ -15,8 +15,6 @@ import java.util.Set; import javax.net.ssl.X509TrustManager; - - @RequiresApi(api = 17) class PinningTrustManager implements X509TrustManager { @@ -26,23 +24,23 @@ class PinningTrustManager implements X509TrustManager { private final String serverHostname; private final DomainPinningPolicy serverConfig; - /** * A trust manager which implements path, hostname and pinning validation for a given hostname * and sends pinning failure reports if validation failed. - *

- * Before Android N, the PinningTrustManager implements pinning validation itself. On Android + * + *

Before Android N, the PinningTrustManager implements pinning validation itself. On Android * N and later the OS' implementation is used instead for pinning validation. * * @param serverHostname: The hostname of the server whose identity is being validated. It will - * be validated against the name(s) the leaf certificate was issued for - * when performing hostname validation. + * be validated against the name(s) the leaf certificate was issued for when performing + * hostname validation. * @param serverConfig: The pinning policy to be enforced when doing pinning validation. * @param baselineTrustManager: The trust manager to use for path validation. */ - public PinningTrustManager(@NonNull String serverHostname, - @NonNull DomainPinningPolicy serverConfig, - @NonNull X509TrustManager baselineTrustManager) { + public PinningTrustManager( + @NonNull String serverHostname, + @NonNull DomainPinningPolicy serverConfig, + @NonNull X509TrustManager baselineTrustManager) { // Store server's information this.serverHostname = serverHostname; this.serverConfig = serverConfig; @@ -64,16 +62,15 @@ public PinningTrustManager(@NonNull String serverHostname, /** * This methods gets called on Android N instead of the 2-parameter checkServerTrusted(). - *

- * If we ever drop support for versions before Android N (unlikely), we can use this method + * + *

If we ever drop support for versions before Android N (unlikely), we can use this method * to automatically get the hostname when the certificate chain needs to be validated, instead * of having to ask for the hostname a lot earlier when the trust manager (or socket factory) * gets created, making the API a lot nicer. - *

- * For now this is here only for documentation. - * See also: X509ExtendedTrustManager - * not to be confused with X509TrustManagerExtensions! * + *

For now this is here only for documentation. See also: + * https://developer.android.com/reference/javax/net/ssl/X509ExtendedTrustManager.html not to be + * confused with X509TrustManagerExtensions! */ /* public List checkServerTrusted(X509Certificate[] chain, String authType, @@ -87,7 +84,7 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) boolean didPinningValidationFail = false; // Store the received chain so we can send it later in a report if path validation fails - List servedServerChain = Arrays.asList(chain); + List servedServerChain = Arrays.asList((X509Certificate[]) chain); List validatedServerChain = servedServerChain; // Then do hostname validation first @@ -103,9 +100,8 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) // extra certificates an attacker might add: https://koz.io/pinning-cve-2016-2402/ try { - assert baselineTrustManager != null; - validatedServerChain = baselineTrustManager.checkServerTrusted(chain, authType, - serverHostname); + validatedServerChain = + baselineTrustManager.checkServerTrusted(chain, authType, serverHostname); } catch (CertificateException e) { if ((Build.VERSION.SDK_INT >= 24) @@ -123,13 +119,14 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) // validation succeeded. On Android N this was already taken care of by the netsec policy if ((Build.VERSION.SDK_INT < 24) && (!didChainValidationFail)) { - boolean hasPinningPolicyExpired = (serverConfig.getExpirationDate() != null) - && (serverConfig.getExpirationDate().compareTo(new Date()) < 0); + boolean hasPinningPolicyExpired = + (serverConfig.getExpirationDate() != null) + && (serverConfig.getExpirationDate().compareTo(new Date()) < 0); // Only do pinning validation if the policy has not expired if (!hasPinningPolicyExpired) { - didPinningValidationFail = !isPinInChain(validatedServerChain, - serverConfig.getPublicKeyPins()); + didPinningValidationFail = + !isPinInChain(validatedServerChain, serverConfig.getPublicKeyPins()); } } @@ -140,8 +137,14 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) // Hostname or path validation failed - not a pinning error validationResult = PinningValidationResult.FAILED_CERTIFICATE_CHAIN_NOT_TRUSTED; } - TrustManagerBuilder.getReporter().pinValidationFailed(serverHostname, 0, - servedServerChain, validatedServerChain, serverConfig, validationResult); + TrustManagerBuilder.getReporter() + .pinValidationFailed( + serverHostname, + 0, + servedServerChain, + validatedServerChain, + serverConfig, + validationResult); } // Throw an exception if needed @@ -149,26 +152,28 @@ public void checkServerTrusted(X509Certificate[] chain, String authType) throw new CertificateException("Certificate validation failed for " + serverHostname); } else if ((didPinningValidationFail) && (serverConfig.shouldEnforcePinning())) { // Pinning failed and is enforced - throw an exception to cancel the handshake - StringBuilder errorBuilder = new StringBuilder() - .append("Pin verification failed") - .append("\n Configured pins: "); + StringBuilder errorBuilder = + new StringBuilder() + .append("Pin verification failed") + .append("\n Configured pins: "); for (PublicKeyPin pin : serverConfig.getPublicKeyPins()) { errorBuilder.append(pin); errorBuilder.append(" "); } errorBuilder.append("\n Peer certificate chain: "); - for (X509Certificate certificate : validatedServerChain) { - errorBuilder.append("\n ") + for (Certificate certificate : validatedServerChain) { + errorBuilder + .append("\n ") .append(new PublicKeyPin(certificate)) .append(" - ") - .append(certificate.getSubjectDN()); + .append(((X509Certificate) certificate).getSubjectDN()); } throw new CertificateException(errorBuilder.toString()); } } - private static boolean isPinInChain(List verifiedServerChain, - Set configuredPins) { + private static boolean isPinInChain( + List verifiedServerChain, Set configuredPins) { boolean wasPinFound = false; for (Certificate certificate : verifiedServerChain) { PublicKeyPin certificatePin = new PublicKeyPin(certificate); diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/PinningValidationResult.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/PinningValidationResult.java index 2fdab8a..e504d22 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/PinningValidationResult.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/PinningValidationResult.java @@ -1,6 +1,5 @@ package com.datatheorem.android.trustkit.pinning; - public enum PinningValidationResult { // The server trust was successfully evaluated and contained at least one of the configured pins SUCCESS, diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/SystemTrustManager.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/SystemTrustManager.java index dcb9689..c278b59 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/SystemTrustManager.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/SystemTrustManager.java @@ -1,6 +1,5 @@ package com.datatheorem.android.trustkit.pinning; - import androidx.annotation.NonNull; import java.security.KeyStore; import java.security.KeyStoreException; @@ -9,16 +8,14 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; - public class SystemTrustManager { private static final X509TrustManager systemTrustManager = getSystemTrustManager(); /** - * Retrieve the platform's default trust manager. - * Depending on the device's API level, the trust manager will consecutively do path validation - * (all API levels), hostname validation (API level 16 to ???), and pinning validation if a - * network policy was configured (API level 24+). + * Retrieve the platform's default trust manager. Depending on the device's API level, the trust + * manager will consecutively do path validation (all API levels), hostname validation (API + * level 16 to ???), and pinning validation if a network policy was configured (API level 24+). * * @return the platform's default trust manager. */ @@ -31,22 +28,21 @@ private static X509TrustManager getSystemTrustManager() { X509TrustManager systemTrustManager = null; TrustManagerFactory trustManagerFactory; try { - trustManagerFactory = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm() - ); + trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("Should never happen"); } try { - trustManagerFactory.init((KeyStore)null); + trustManagerFactory.init((KeyStore) null); } catch (KeyStoreException e) { throw new IllegalStateException("Should never happen"); } for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { if (trustManager instanceof X509TrustManager) { - systemTrustManager = (X509TrustManager)trustManager; + systemTrustManager = (X509TrustManager) trustManager; } } diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/TrustManagerBuilder.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/TrustManagerBuilder.java index af052fd..37f2bc5 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/TrustManagerBuilder.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/TrustManagerBuilder.java @@ -14,8 +14,6 @@ import java.util.Set; import javax.net.ssl.X509TrustManager; - - public class TrustManagerBuilder { // The trust manager we will use to perform the default SSL validation @@ -27,17 +25,16 @@ public class TrustManagerBuilder { // The reporter that will send pinning failure reports protected static BackgroundReporter backgroundReporter = null; - public static void initializeBaselineTrustManager(@Nullable Set debugCaCerts, - boolean debugOverridePins, - @NonNull BackgroundReporter reporter) - throws CertificateException, NoSuchAlgorithmException, KeyStoreException, - IOException { + public static void initializeBaselineTrustManager( + @Nullable Set debugCaCerts, + boolean debugOverridePins, + @NonNull BackgroundReporter reporter) + throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException { if (baselineTrustManager != null) { throw new IllegalStateException("TrustManagerBuilder has already been initialized"); } baselineTrustManager = SystemTrustManager.getInstance(); - if (Build.VERSION.SDK_INT < 17) { // No pinning validation or debug overrides return; @@ -73,8 +70,7 @@ public static X509TrustManager getTrustManager(@NonNull String serverHostname) { } } - /** Retrieve the background reporter to be used for sending pinning validation reports. - */ + /** Retrieve the background reporter to be used for sending pinning validation reports. */ static BackgroundReporter getReporter() { if (backgroundReporter == null) { throw new IllegalStateException("TrustManagerBuilder has not been initialized"); diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/Utils.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/Utils.java index 05d463c..26bcd93 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/Utils.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/pinning/Utils.java @@ -19,22 +19,21 @@ /** Junk drawer of utility methods. */ final class Utils { - /** - * Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation - * of Android's private InetAddress#isNumeric API. - * - *

This matches IPv6 addresses as a hex string containing at least one colon, and possibly - * including dots after the first colon. It matches IPv4 addresses as strings containing only - * decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither IP - * addresses nor hostnames; they will be verified as IP addresses (which is a more strict - * verification). - */ - private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile( - "([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)"); + /** + * Quick and dirty pattern to differentiate IP addresses from hostnames. This is an + * approximation of Android's private InetAddress#isNumeric API. + * + *

This matches IPv6 addresses as a hex string containing at least one colon, and possibly + * including dots after the first colon. It matches IPv4 addresses as strings containing only + * decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither + * IP addresses nor hostnames; they will be verified as IP addresses (which is a more strict + * verification). + */ + private static final Pattern VERIFY_AS_IP_ADDRESS = + Pattern.compile("([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)"); - - /** Returns true if {@code host} is not a host name and might be an IP address. */ - public static boolean verifyAsIpAddress(String host) { - return VERIFY_AS_IP_ADDRESS.matcher(host).matches(); - } -} \ No newline at end of file + /** Returns true if {@code host} is not a host name and might be an IP address. */ + public static boolean verifyAsIpAddress(String host) { + return VERIFY_AS_IP_ADDRESS.matcher(host).matches(); + } +} diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/BackgroundReporter.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/BackgroundReporter.java index 252d34e..a80366c 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/BackgroundReporter.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/BackgroundReporter.java @@ -1,18 +1,14 @@ package com.datatheorem.android.trustkit.reporting; - import android.content.Context; import android.content.Intent; import android.util.Base64; - import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import com.datatheorem.android.trustkit.config.DomainPinningPolicy; import com.datatheorem.android.trustkit.pinning.PinningValidationResult; import com.datatheorem.android.trustkit.utils.TrustKitLog; - import java.net.URL; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; @@ -21,9 +17,9 @@ import java.util.List; import java.util.Set; - public class BackgroundReporter { - public static final String REPORT_VALIDATION_EVENT = "com.datatheorem.android.trustkit.reporting.BackgroundReporter:REPORT_VALIDATION_EVENT"; + public static final String REPORT_VALIDATION_EVENT = + "com.datatheorem.android.trustkit.reporting.BackgroundReporter:REPORT_VALIDATION_EVENT"; public static final String EXTRA_REPORT = "Report"; // App meta-data to be sent with the reports @@ -32,13 +28,15 @@ public class BackgroundReporter { private final String appVendorId; private final Context context; - public BackgroundReporter(@NonNull Context context, @NonNull String appPackageName, @NonNull String appVersion, - @NonNull String appVendorId) { + public BackgroundReporter( + @NonNull Context context, + @NonNull String appPackageName, + @NonNull String appVersion, + @NonNull String appVendorId) { this.context = context; this.appPackageName = appPackageName; this.appVersion = appVersion; this.appVendorId = appVendorId; - } private static String certificateToPem(X509Certificate certificate) { @@ -46,8 +44,8 @@ private static String certificateToPem(X509Certificate certificate) { try { certificateData = certificate.getEncoded(); } catch (CertificateEncodingException e) { - throw new IllegalStateException("Should never happen - certificate was previously " + - "parsed by the system"); + throw new IllegalStateException( + "Should never happen - certificate was previously " + "parsed by the system"); } // Create the PEM string @@ -60,18 +58,19 @@ private static String certificateToPem(X509Certificate certificate) { /** * Try to send a pin validation failure report to the reporting servers configured for the * hostname that triggered the failure. - *

- * Reports are rate-limited to one identical (same host, error and certificate chain) report + * + *

Reports are rate-limited to one identical (same host, error and certificate chain) report * every 24 hours. Also and before Android N, only the default SSL validation is performed when * connecting to the reporting server (ie. no pinning validation). */ @RequiresApi(api = 16) - public void pinValidationFailed(@NonNull String serverHostname, - @NonNull Integer serverPort, - @NonNull List servedCertificateChain, - @NonNull List validatedCertificateChain, - @NonNull DomainPinningPolicy serverConfig, - @NonNull PinningValidationResult validationResult) { + public void pinValidationFailed( + @NonNull String serverHostname, + @NonNull Integer serverPort, + @NonNull List servedCertificateChain, + @NonNull List validatedCertificateChain, + @NonNull DomainPinningPolicy serverConfig, + @NonNull PinningValidationResult validationResult) { TrustKitLog.i("Generating pin failure report for " + serverHostname); @@ -86,12 +85,21 @@ public void pinValidationFailed(@NonNull String serverHostname, } // Generate the corresponding pin failure report - PinningFailureReport report = new PinningFailureReport(appPackageName, appVersion, - appVendorId, serverHostname, serverPort, - serverConfig.getHostname(), serverConfig.shouldIncludeSubdomains(), - serverConfig.shouldEnforcePinning(), servedCertificateChainAsPem, - validatedCertificateChainAsPem, new Date(System.currentTimeMillis()), - serverConfig.getPublicKeyPins(), validationResult); + PinningFailureReport report = + new PinningFailureReport( + appPackageName, + appVersion, + appVendorId, + serverHostname, + serverPort, + serverConfig.getHostname(), + serverConfig.shouldIncludeSubdomains(), + serverConfig.shouldEnforcePinning(), + servedCertificateChainAsPem, + validatedCertificateChainAsPem, + new Date(System.currentTimeMillis()), + serverConfig.getPublicKeyPins(), + validationResult); // If a similar report hasn't been sent recently, send it now if (!(ReportRateLimiter.shouldRateLimit(report))) { @@ -103,8 +111,8 @@ validatedCertificateChainAsPem, new Date(System.currentTimeMillis()), } @RequiresApi(api = 16) - protected void sendReport(@NonNull PinningFailureReport report, - @NonNull Set reportUriSet) { + protected void sendReport( + @NonNull PinningFailureReport report, @NonNull Set reportUriSet) { // Prepare the AsyncTask's arguments ArrayList taskParameters = new ArrayList<>(); taskParameters.add(report); @@ -113,7 +121,7 @@ protected void sendReport(@NonNull PinningFailureReport report, new BackgroundReporterTask().execute(taskParameters.toArray()); } - protected void broadcastReport(@NonNull PinningFailureReport report){ + protected void broadcastReport(@NonNull PinningFailureReport report) { Intent intent = new Intent(REPORT_VALIDATION_EVENT); intent.putExtra(EXTRA_REPORT, report); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTask.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTask.java index b9aea87..1133265 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTask.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/BackgroundReporterTask.java @@ -2,12 +2,9 @@ import android.os.AsyncTask; import android.util.Base64; - import androidx.annotation.RequiresApi; - import com.datatheorem.android.trustkit.pinning.SystemTrustManager; import com.datatheorem.android.trustkit.utils.TrustKitLog; - import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -15,13 +12,11 @@ import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; - import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; - // This returns an obscure threading error on API level < 16 @RequiresApi(api = 16) class BackgroundReporterTask extends AsyncTask { @@ -36,7 +31,7 @@ protected final Integer doInBackground(Object... params) { PinningFailureReport report = (PinningFailureReport) params[0]; // Remaining parameters are report URLs - send the report to each of them - for (int i=1; i knownPins; - - PinningFailureReport(@NonNull String appBundleId, @NonNull String appVersion, - @NonNull String appVendorId, @NonNull String hostname, int port, - @NonNull String notedHostname, boolean includeSubdomains, - boolean enforcePinning, @NonNull List servedCertificateChain, - @NonNull List validatedCertificateChain, @NonNull Date dateTime, - @NonNull Set knownPins, - @NonNull PinningValidationResult validationResult) { + PinningFailureReport( + @NonNull String appBundleId, + @NonNull String appVersion, + @NonNull String appVendorId, + @NonNull String hostname, + int port, + @NonNull String notedHostname, + boolean includeSubdomains, + boolean enforcePinning, + @NonNull List servedCertificateChain, + @NonNull List validatedCertificateChain, + @NonNull Date dateTime, + @NonNull Set knownPins, + @NonNull PinningValidationResult validationResult) { this.appBundleId = appBundleId; this.appVersion = appVersion; this.appVendorId = appVendorId; diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/ReportRateLimiter.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/ReportRateLimiter.java index 03a3bd0..48584ff 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/ReportRateLimiter.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/reporting/ReportRateLimiter.java @@ -1,8 +1,6 @@ package com.datatheorem.android.trustkit.reporting; - import androidx.annotation.NonNull; - import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -12,11 +10,11 @@ // Very basic implementation to rate-limit identical reports to once a day class ReportRateLimiter { - private static final long MAX_SECONDS_BETWEEN_CACHE_RESET = 3600*24; + private static final long MAX_SECONDS_BETWEEN_CACHE_RESET = 3600 * 24; private static final Set> reportsCache = new HashSet<>(); protected static Date lastReportsCacheResetDate = new Date(); - synchronized static boolean shouldRateLimit(@NonNull final PinningFailureReport report) { + static synchronized boolean shouldRateLimit(@NonNull final PinningFailureReport report) { // Reset the cache if it was created more than 24 hours ago Date currentDate = new Date(); long secondsSinceLastReset = @@ -35,7 +33,7 @@ synchronized static boolean shouldRateLimit(@NonNull final PinningFailureReport cacheEntry.add(report.getValidationResult()); boolean shouldRateLimitReport = reportsCache.contains(cacheEntry); - if (!shouldRateLimitReport){ + if (!shouldRateLimitReport) { reportsCache.add(cacheEntry); } return shouldRateLimitReport; diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/utils/TrustKitLog.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/utils/TrustKitLog.java index 701faa5..739cf78 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/utils/TrustKitLog.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/utils/TrustKitLog.java @@ -3,7 +3,6 @@ import android.util.Log; import com.datatheorem.android.trustkit.BuildConfig; - public final class TrustKitLog { public static void i(String message) { diff --git a/trustkit/src/main/java/com/datatheorem/android/trustkit/utils/VendorIdentifier.java b/trustkit/src/main/java/com/datatheorem/android/trustkit/utils/VendorIdentifier.java index aa25b28..f2b9974 100644 --- a/trustkit/src/main/java/com/datatheorem/android/trustkit/utils/VendorIdentifier.java +++ b/trustkit/src/main/java/com/datatheorem/android/trustkit/utils/VendorIdentifier.java @@ -1,12 +1,9 @@ package com.datatheorem.android.trustkit.utils; - import android.content.Context; import android.content.SharedPreferences; - import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; - import java.util.UUID; /**