From c7a934a643faea50441011f1d997f663aca7f74b Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Tue, 27 Jan 2026 15:36:39 +0530 Subject: [PATCH 1/6] Updated the gradle version and AGP to 8+ --- AGP_UPGRADE_PLAN.md | 399 +++++++++++++++++++++++ auth0/build.gradle | 11 +- auth0/src/main/AndroidManifest.xml | 3 +- build.gradle | 2 +- gradle.properties | 5 +- gradle/jacoco.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- sample/build.gradle | 7 +- sample/src/main/AndroidManifest.xml | 3 +- 9 files changed, 418 insertions(+), 18 deletions(-) create mode 100644 AGP_UPGRADE_PLAN.md diff --git a/AGP_UPGRADE_PLAN.md b/AGP_UPGRADE_PLAN.md new file mode 100644 index 000000000..a484fb121 --- /dev/null +++ b/AGP_UPGRADE_PLAN.md @@ -0,0 +1,399 @@ +# AGP and Gradle Upgrade Plan: Version 7 to Version 8+ + +## Current State Analysis + +### Current Versions +- **Gradle**: 7.5 +- **AGP (Android Gradle Plugin)**: 7.4.0 +- **Kotlin**: 1.8.22 +- **Compile SDK**: 35 +- **Target SDK**: 35 +- **Min SDK**: 21 (library), 24 (sample) +- **Java Compatibility**: VERSION_11 (library), VERSION_1_8 (sample) + +### Project Structure +- Multi-module project: `auth0` (library) + `sample` (application) +- Uses Groovy DSL for build scripts (no Kotlin DSL) +- Custom Gradle scripts: jacoco.gradle, maven-publish.gradle, versioning.gradle + +### Key Dependencies Identified +**AndroidX Libraries:** +- androidx.core:core-ktx:1.6.0 +- androidx.appcompat:appcompat:1.6.0 (library), 1.3.0 (sample) +- androidx.browser:browser:1.4.0 +- androidx.biometric:biometric:1.1.0 +- androidx.credentials:credentials:1.3.0 + +**Networking:** +- com.squareup.okhttp3:okhttp:4.12.0 +- com.squareup.okhttp3:logging-interceptor:4.12.0 +- com.google.code.gson:gson:2.8.9 + +**Coroutines:** +- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2 + +**Testing:** +- JUnit 4.13.2 +- Robolectric 4.8.1 +- PowerMock 2.0.9 +- Mockito 3.12.4 +- Espresso 3.5.1 (library), 3.4.0 (sample) + +### Critical Issues Found + +1. **gradle.properties Alert**: Contains temporary workaround + ``` + # Adding this here temporarily to fix the build with compileSdKVersion 35. Remove this when migrate to gradle 8 + android.aapt2Version=8.6.1-11315950 + ``` + This indicates the project is already encountering issues with SDK 35 on AGP 7. + +2. **Deprecated JCenter Repository**: Still using JCenter for specific dependencies + - org.jetbrains.trove4j:trove4j + - com.soywiz.korlibs.korte:korte-jvm + - org.jetbrains.kotlinx:kotlinx-html-jvm + +3. **Outdated Dependencies**: Several dependencies need updates for AGP 8 compatibility + +4. **Jacoco Configuration**: Uses deprecated `xml.enabled` / `html.enabled` syntax + +5. **Lint Options**: Uses deprecated `lintOptions` block (should be `lint`) + +6. **CI/CD**: GitHub Actions setup uses older Gradle/Kotlin versions in CI config + +## Recommended Target Versions + +### Primary Recommendations +- **Gradle**: 8.10.2 (Latest stable with excellent AGP 8.x support) +- **AGP**: 8.7.3 (Latest stable for SDK 35 - removes need for AAPT2 workaround) +- **Kotlin**: 2.0.21 (Full compatibility with AGP 8.x, K2 compiler) +- **Java Target**: Remain at Java 11 (already compliant) +- **JaCoCo**: 0.8.5 → 0.8.12 + +## Critical Breaking Changes + +### 1. PowerMock Incompatibility (SHOW STOPPER) +**Problem**: PowerMock 2.0.9 uses bytecode manipulation incompatible with Java Module System required by AGP 8.x + +**Current Dependencies (auth0/build.gradle:102-104)**: +```groovy +testImplementation "org.powermock:powermock-module-junit4:$powermockVersion" +testImplementation "org.powermock:powermock-module-junit4-rule:$powermockVersion" +testImplementation "org.powermock:powermock-api-mockito2:$powermockVersion" +``` + +**Affected Test Files** (only 2 files!): +1. `auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java` + - Mocks: KeyGenerator, TextUtils, Build.VERSION, Base64, Cipher, Log, KeyStore + - Purpose: Testing Android KeyStore cryptographic operations + - Strategy: Use Robolectric's shadow classes for Android framework mocking + +2. `auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt` + - Mocks: KeyStore, KeyPairGenerator, KeyGenParameterSpec.Builder, Build.VERSION, Log + - Purpose: Testing DPoP key storage and generation + - Strategy: Use Robolectric + refactor to reduce static mocking needs + +**Chosen Approach**: Remove PowerMock and refactor tests to use Robolectric + standard Mockito +- Robolectric already provides shadows for most Android framework classes (Build.VERSION, Log, TextUtils, Base64) +- KeyStore and Cipher operations can be tested with real Android KeyStore via Robolectric +- Reduces test complexity and improves compatibility + +**Impact**: 2-3 hours of test refactoring (only 2 test files affected) + +### 2. Deprecated DSL Syntax +Must update before AGP 8.x will work: +- `lintOptions` → `lint` (auth0/build.gradle:53) +- `xml.enabled` → `xml.required` (gradle/jacoco.gradle:48-49) +- `compileSdkVersion` → `compileSdk` (sample/build.gradle:7) + +### 3. JCenter Deprecation Warning +Still using JCenter for specific Dokka dependencies (trove4j, kotlinx-html-jvm). These are now on Maven Central, so repositories will continue working. + +## Step-by-Step Upgrade Sequence + +### Phase 1: Pre-Upgrade Preparation +1. **Remove AAPT2 Workaround** + - File: `gradle.properties` + - Remove: `android.aapt2Version=8.6.1-11315950` + - This was a temporary fix that AGP 8.7.3 resolves + +2. **Validate Current Build** + ```bash + ./gradlew clean build test jacocoTestReport --stacktrace + ``` + +3. **Update Gradle Wrapper** + - File: `gradle/wrapper/gradle-wrapper.properties` + - Change: `distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip` + - Run: `./gradlew wrapper --gradle-version=8.10.2 --distribution-type=all` + +4. **Update AGP Version** + - File: `build.gradle` (root) + - Line 16: `classpath 'com.android.tools.build:gradle:8.7.3'` + +### Phase 2: Fix Deprecated DSL Syntax + +5. **Fix lintOptions** (auth0/build.gradle:53-56) + ```groovy + // OLD + lintOptions { + htmlReport true + abortOnError true + } + + // NEW + lint { + htmlReport = true + abortOnError = true + } + ``` + +6. **Fix JaCoCo Reports** (gradle/jacoco.gradle:47-50) + ```groovy + // OLD + reports { + xml.enabled = true + html.enabled = true + } + + // NEW + reports { + xml.required = true + html.required = true + } + ``` + +7. **Fix SDK Version Syntax** (sample/build.gradle:7-11) + ```groovy + // OLD + compileSdkVersion 35 + minSdkVersion 24 + targetSdkVersion 35 + + // NEW + compileSdk 35 + minSdk 24 + targetSdk 35 + ``` + +### Phase 3: Kotlin Upgrade + +8. **Update Kotlin Version** + - File: `build.gradle` (root) + - Line 3: `ext.kotlin_version = "2.0.21"` + +9. **Update Kotlin Stdlib Reference** + - File: `auth0/build.gradle` + - Line 87: `"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"` + - (Remove `-jdk8` suffix - it's now implicit) + +### Phase 4: Update Test Dependencies + +10. **Handle PowerMock Removal** (auth0/build.gradle) + - Remove lines 102-104 (PowerMock dependencies) + - Refactor 2 affected test files: + - CryptoUtilTest.java: Remove @RunWith(PowerMockRunner), @PrepareForTest, PowerMockito imports + - DPoPKeyStoreTest.kt: Remove @RunWith(PowerMockRunner), @PrepareForTest, PowerMockito usage + - Replace static mocking with Robolectric shadows and standard Mockito + +11. **Update Mockito Ecosystem** + - Line 105: `mockito-core: 3.12.4 → 5.7.0` + - Line 107: `mockito-kotlin: 2.2.0 → org.mockito.kotlin:mockito-kotlin:5.1.0` + +12. **Update Robolectric** + - Line 111: `robolectric: 4.8.1 → 4.13.1` + +13. **Update Testing Libraries** + - `androidx.test.espresso:espresso-intents: 3.5.1 → 3.6.1` + - `androidx.test.espresso:espresso-core: 3.4.0 → 3.6.1` + - `androidx.test.ext:junit: 1.1.3 → 1.2.0` + - `awaitility: 1.7.0 → 4.2.1` + +### Phase 5: Update Runtime Dependencies + +14. **Update AndroidX Libraries** + - `androidx.core:core-ktx: 1.6.0 → 1.15.0` + - `androidx.appcompat:appcompat: 1.6.0 → 1.7.0 (sample: 1.3.0 → 1.7.0)` + - `androidx.browser:browser: 1.4.0 → 1.8.0` + - `androidx.biometric:biometric: 1.1.0 → 1.2.0` + - `androidx.constraintlayout: 2.0.4 → 2.1.4` (sample) + - `androidx.navigation: 2.3.5 → 2.8.2` (sample) + - `androidx.material: 1.4.0 → 1.12.0` (sample) + +15. **Update Coroutines** + - Line 80: `coroutinesVersion = '1.6.2' → '1.7.3'` + +16. **Update Other Dependencies** + - `gson: 2.8.9 → 2.10.1` + - `okhttp: 4.12.0` (keep - already latest) + +### Phase 6: Update gradle.properties + +17. **Clean Up Properties** (gradle.properties) + - Remove: `android.aapt2Version=8.6.1-11315950` (done in Phase 1) + - Remove: `android.enableJetifier=false` (not needed with AGP 8.x) + - Keep: `android.useAndroidX=true` + - Keep: `kotlin.code.style=official` + - Optional Add: `org.gradle.caching=true` + +### Phase 7: Update CI/CD Configuration + +18. **Update GitHub Actions** (.github/actions/setup/action.yml) + - Line 12: Default Gradle: `6.7.1 → 8.10.2` + - Line 16: Default Kotlin: `1.6.21 → 2.0.21` + +### Phase 8: Update JaCoCo Version + +19. **Update JaCoCo** (gradle/jacoco.gradle:4) + - `toolVersion = "0.8.5" → "0.8.12"` + +## Complete Dependency Update Matrix + +``` +GRADLE ECOSYSTEM: +├─ Gradle: 7.5 → 8.10.2 +├─ AGP: 7.4.0 → 8.7.3 +├─ Kotlin: 1.8.22 → 2.0.21 +├─ Java: 11 (no change) +└─ JaCoCo: 0.8.5 → 0.8.12 + +KOTLIN ECOSYSTEM: +├─ kotlin-stdlib-jdk8 → kotlin-stdlib: 2.0.21 +├─ kotlinx-coroutines: 1.6.2 → 1.7.3 + +ANDROIDX LIBRARIES: +├─ core-ktx: 1.6.0 → 1.15.0 +├─ appcompat: 1.6.0/1.3.0 → 1.7.0 +├─ browser: 1.4.0 → 1.8.0 +├─ biometric: 1.1.0 → 1.2.0 +├─ credentials: 1.3.0 (keep) +├─ constraintlayout: 2.0.4 → 2.1.4 +├─ navigation: 2.3.5 → 2.8.2 +└─ material: 1.4.0 → 1.12.0 + +TEST FRAMEWORKS: +├─ Robolectric: 4.8.1 → 4.13.1 +├─ Mockito: 3.12.4 → 5.7.0 +├─ mockito-kotlin: 2.2.0 → 5.1.0 +├─ PowerMock: 2.0.9 → REMOVE +├─ MockK: NEW 1.13.14 (optional) +├─ espresso: 3.5.1/3.4.0 → 3.6.1 +├─ awaitility: 1.7.0 → 4.2.1 +└─ androidx.test.ext:junit: 1.1.3 → 1.2.0 + +NETWORK/JSON: +├─ okhttp: 4.12.0 (keep) +└─ gson: 2.8.9 → 2.10.1 +``` + +## Testing Strategy + +### Verification Steps +```bash +# 1. Basic compilation +./gradlew clean build -x test + +# 2. Unit tests +./gradlew test --stacktrace + +# 3. Coverage reports +./gradlew test jacocoTestReport --stacktrace + +# 4. Lint checks +./gradlew lint --stacktrace + +# 5. Sample app build +./gradlew :sample:build + +# 6. Library packaging +./gradlew :auth0:assembleRelease + +# 7. CI replication +./gradlew clean test jacocoTestReport lint --continue --console=plain --max-workers=1 --no-daemon + +# 8. Maven publish dry-run +./gradlew publish -x signReleasePublication --dry-run +``` + +## Rollback Plan + +### Full Revert (if critical issues arise) +```bash +git checkout build.gradle gradle.properties gradle/wrapper/gradle-wrapper.properties +./gradlew wrapper --gradle-version=7.5 +``` + +### Partial Revert +- Upgrade only to Gradle 7.6 (without AGP 8.x) +- Provides some improvements while maintaining compatibility + +## Estimated Effort +- **Total Time**: 7-9 hours +- **Critical Path**: 5 hours minimum +- **PowerMock Refactoring**: 2-4 hours (50% of total effort) +- **Risk Level**: Medium-High (PowerMock compatibility is main blocker) + +## Recommended Implementation Order + +Based on user preferences (latest stable versions, direct Kotlin 2.0.21 upgrade, PowerMock removal): + +### Commit 1: Phase 1 - Pre-upgrade preparation +- Remove AAPT2 workaround from gradle.properties +- Validate current build passes +- Create feature branch: `git checkout -b gradle-agp-8-upgrade` + +### Commit 2: Phase 1 - Gradle wrapper upgrade +- Update gradle-wrapper.properties to 8.10.2 +- Run: `./gradlew wrapper --gradle-version=8.10.2 --distribution-type=all` +- Verify: `./gradlew --version` + +### Commit 3: Phase 1 & 2 - AGP + DSL fixes +- Update AGP to 8.7.3 in root build.gradle +- Fix lintOptions → lint (auth0/build.gradle) +- Fix JaCoCo reports syntax (gradle/jacoco.gradle) +- Fix SDK version syntax (sample/build.gradle) +- Test: `./gradlew clean build -x test` (should compile) + +### Commit 4: Phase 3 - Kotlin upgrade +- Update Kotlin to 2.0.21 in root build.gradle +- Update stdlib reference in auth0/build.gradle +- Test: `./gradlew clean build -x test` + +### Commit 5: Phase 4 - PowerMock removal & test refactoring +- Remove PowerMock dependencies from auth0/build.gradle +- Refactor CryptoUtilTest.java to use Robolectric +- Refactor DPoPKeyStoreTest.kt to use Robolectric +- Update Mockito to 5.7.0 +- Update mockito-kotlin to 5.1.0 +- Test: `./gradlew test --stacktrace` (critical milestone) + +### Commit 6: Phase 4 & 5 - Dependency updates +- Update Robolectric to 4.13.1 +- Update all AndroidX libraries +- Update coroutines to 1.7.3 +- Update espresso, awaitility, gson +- Test: `./gradlew test jacocoTestReport` + +### Commit 7: Phase 6 & 8 - Properties and tooling +- Clean up gradle.properties +- Update JaCoCo to 0.8.12 +- Update CI configuration (.github/actions/setup/action.yml) +- Test: Full CI command locally + +### Commit 8: Final verification +- Run: `./gradlew clean test jacocoTestReport lint --continue --console=plain --max-workers=1 --no-daemon` +- Verify sample app builds +- Verify library packaging +- Maven publish dry-run +- Ready for PR + +## Critical Files to Modify +- `/Users/prince.mathew/workspace/Auth0.Android/build.gradle` - AGP, Kotlin versions +- `/Users/prince.mathew/workspace/Auth0.Android/auth0/build.gradle` - DSL syntax, dependencies, PowerMock removal +- `/Users/prince.mathew/workspace/Auth0.Android/sample/build.gradle` - DSL syntax, dependencies +- `/Users/prince.mathew/workspace/Auth0.Android/gradle/wrapper/gradle-wrapper.properties` - Gradle version +- `/Users/prince.mathew/workspace/Auth0.Android/gradle/jacoco.gradle` - JaCoCo DSL syntax, version +- `/Users/prince.mathew/workspace/Auth0.Android/gradle.properties` - Property cleanup +- `/Users/prince.mathew/workspace/Auth0.Android/.github/actions/setup/action.yml` - CI configuration +- `/Users/prince.mathew/workspace/Auth0.Android/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java` - PowerMock refactoring +- `/Users/prince.mathew/workspace/Auth0.Android/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt` - PowerMock refactoring diff --git a/auth0/build.gradle b/auth0/build.gradle index e3b5cae26..2a1055602 100644 --- a/auth0/build.gradle +++ b/auth0/build.gradle @@ -34,8 +34,13 @@ version = getVersionFromFile() logger.lifecycle("Using version ${version} for ${name}") android { + namespace 'com.auth0.android.auth0' compileSdk 35 + buildFeatures { + buildConfig = true + } + defaultConfig { minSdkVersion 21 targetSdk 35 @@ -50,9 +55,9 @@ android { consumerProguardFiles '../proguard/proguard-gson.pro', '../proguard/proguard-okio.pro', '../proguard/proguard-jetpack.pro' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - lintOptions { - htmlReport true - abortOnError true + lint { + htmlReport = true + abortOnError = true } testOptions { unitTests { diff --git a/auth0/src/main/AndroidManifest.xml b/auth0/src/main/AndroidManifest.xml index 96c1233b1..1532d6819 100644 --- a/auth0/src/main/AndroidManifest.xml +++ b/auth0/src/main/AndroidManifest.xml @@ -1,8 +1,7 @@ + xmlns:tools="http://schemas.android.com/tools"> diff --git a/build.gradle b/build.gradle index 316d84146..6014037be 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:7.4.0' + classpath 'com.android.tools.build:gradle:8.8.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jacoco:org.jacoco.core:0.8.5" } diff --git a/gradle.properties b/gradle.properties index 0dda7285b..7dd5d5718 100644 --- a/gradle.properties +++ b/gradle.properties @@ -24,7 +24,4 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=false # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official - -# Adding this here temporarily to fix the build with compileSdKVersion 35. Remove this when migrate to gradle 8 -android.aapt2Version=8.6.1-11315950 \ No newline at end of file +kotlin.code.style=official \ No newline at end of file diff --git a/gradle/jacoco.gradle b/gradle/jacoco.gradle index 5a1e3c327..3984adc7d 100644 --- a/gradle/jacoco.gradle +++ b/gradle/jacoco.gradle @@ -45,8 +45,8 @@ afterEvaluate { executionData.from = "${buildDir}/jacoco/${testTaskName}.exec" reports { - xml.enabled = true - html.enabled = true + xml.required = true + html.required = true } } jacocoTestReportTask.dependsOn reportTask diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2ec77e51a..18330fcba 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/sample/build.gradle b/sample/build.gradle index 530d63778..d6c793370 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -4,11 +4,12 @@ plugins { } android { - compileSdkVersion 35 + namespace 'com.auth0.sample' + compileSdk 35 defaultConfig { - minSdkVersion 24 - targetSdkVersion 35 + minSdk 24 + targetSdk 35 versionCode 1 versionName "1.0" diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 8f2c85b74..8b0b759e2 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + From 5a04b70a0888bf7125851c62d4eaeca9a38caf6c Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Tue, 27 Jan 2026 15:57:33 +0530 Subject: [PATCH 2/6] Updated kotlin version to 2.0.1 and java version to 17 --- auth0/build.gradle | 8 ++++---- build.gradle | 2 +- sample/build.gradle | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/auth0/build.gradle b/auth0/build.gradle index 2a1055602..ac35eef75 100644 --- a/auth0/build.gradle +++ b/auth0/build.gradle @@ -68,11 +68,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = JavaVersion.VERSION_11.toString() + jvmTarget = JavaVersion.VERSION_17.toString() freeCompilerArgs += [ '-Xexplicit-api=strict', // or '-Xexplicit-api=warning' ] @@ -89,7 +89,7 @@ ext { dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.6.0' implementation 'androidx.browser:browser:1.4.0' diff --git a/build.gradle b/build.gradle index 6014037be..4e11425c4 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = "1.8.22" + ext.kotlin_version = "2.0.21" repositories { google() mavenCentral() diff --git a/sample/build.gradle b/sample/build.gradle index d6c793370..d239bf435 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -36,11 +36,11 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } } From a472f18c53870d88ec06310ca0be117491e62baa Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Tue, 27 Jan 2026 16:16:55 +0530 Subject: [PATCH 3/6] Updated few tools dependencies --- .github/actions/maven-publish/action.yml | 2 +- .github/actions/setup/action.yml | 8 ++++---- .github/workflows/codeql.yml | 2 +- .github/workflows/release.yml | 4 ++-- gradle.properties | 2 -- gradle/jacoco.gradle | 2 +- 6 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/actions/maven-publish/action.yml b/.github/actions/maven-publish/action.yml index 2aac38757..fb745c124 100644 --- a/.github/actions/maven-publish/action.yml +++ b/.github/actions/maven-publish/action.yml @@ -24,7 +24,7 @@ runs: uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' - uses: gradle/wrapper-validation-action@56b90f209b02bf6d1deae490e9ef18b21a389cd4 # pin@1.1.0 diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index d18c43454..c0125fd93 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -5,15 +5,15 @@ inputs: java: description: The Java version to use required: false - default: 8.0.382-tem + default: '17' gradle: description: The Gradle version to use required: false - default: 6.7.1 + default: 8.10.2 kotlin: description: The Kotlin version to use required: false - default: 1.6.21 + default: 2.0.21 runs: using: composite @@ -23,7 +23,7 @@ runs: uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '11' + java-version: '17' - run: | curl -s "https://get.sdkman.io" | bash diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index d9d498320..667562b07 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -39,7 +39,7 @@ jobs: uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '11' + java-version: '17' - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d74a96c02..528fb5400 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: rl-scanner: uses: ./.github/workflows/rl-scanner.yml with: - java-version: 8.0.402-zulu + java-version: '17' artifact-name: 'auth0-release.aar' secrets: RLSECURE_LICENSE: ${{ secrets.RLSECURE_LICENSE }} @@ -32,7 +32,7 @@ jobs: uses: ./.github/workflows/java-release.yml needs: rl-scanner with: - java-version: 8.0.402-zulu + java-version: '17' secrets: ossr-username: ${{ secrets.OSSR_USERNAME }} ossr-token: ${{ secrets.OSSR_TOKEN }} diff --git a/gradle.properties b/gradle.properties index 7dd5d5718..1e593ff33 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,5 @@ POM_DEVELOPER_EMAIL=oss@auth0.com org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=false # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official \ No newline at end of file diff --git a/gradle/jacoco.gradle b/gradle/jacoco.gradle index 3984adc7d..7d341b02c 100644 --- a/gradle/jacoco.gradle +++ b/gradle/jacoco.gradle @@ -1,7 +1,7 @@ apply plugin: 'jacoco' jacoco { - toolVersion = "0.8.5" + toolVersion = "0.8.12" } android { From 65f1f92f3a43b49e61951845c9339a89dd4bb580 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Thu, 29 Jan 2026 12:17:05 +0530 Subject: [PATCH 4/6] Removed powermock and using mockito5 . Replaced all tests except CryptoUtilTest class --- auth0/build.gradle | 13 +-- .../java/com/auth0/android/Auth0Test.java | 2 +- .../AuthenticationAPIClientTest.kt | 12 +- .../request/ProfileRequestTest.java | 2 +- .../request/SignUpRequestTest.java | 2 +- .../storage/CredentialsManagerTest.kt | 26 +++-- .../storage/LocalAuthenticationManagerTest.kt | 8 +- ...reCredentialsManagerBiometricPolicyTest.kt | 2 +- .../storage/SecureCredentialsManagerTest.kt | 21 ++-- .../storage/SharedPreferencesStorageTest.java | 12 +- .../auth0/android/dpop/DPoPKeyStoreTest.kt | 110 ++++++++---------- .../java/com/auth0/android/dpop/DPoPTest.kt | 6 +- .../com/auth0/android/dpop/DPoPUtilTest.kt | 15 ++- .../android/management/UsersAPIClientTest.kt | 2 +- .../myaccount/MyAccountAPIClientTest.kt | 2 +- .../provider/AuthenticationActivityTest.kt | 2 +- .../android/provider/BrowserPickerTest.java | 4 +- .../provider/CustomTabsControllerTest.java | 76 ++++++------ .../provider/CustomTabsOptionsTest.java | 2 +- .../android/provider/OAuthManagerStateTest.kt | 2 +- .../com/auth0/android/provider/PKCETest.java | 4 +- .../android/provider/PasskeyManagerTest.kt | 18 +-- .../provider/PermissionHandlerTest.java | 6 +- .../android/provider/WebAuthProviderTest.kt | 2 +- .../android/request/RetryInterceptorTest.kt | 12 +- .../internal/BaseAuthenticationRequestTest.kt | 2 +- .../request/internal/BaseRequestTest.kt | 14 +-- .../CommonThreadSwitcherDelegateTest.kt | 2 +- .../internal/TLS12SocketFactoryTest.java | 8 +- 29 files changed, 191 insertions(+), 198 deletions(-) diff --git a/auth0/build.gradle b/auth0/build.gradle index ac35eef75..9c0190136 100644 --- a/auth0/build.gradle +++ b/auth0/build.gradle @@ -81,8 +81,7 @@ android { ext { okhttpVersion = '4.12.0' - powermockVersion = '2.0.9' - coroutinesVersion = '1.6.2' + coroutinesVersion = '1.7.3' biometricLibraryVersion = '1.1.0' credentialManagerVersion = "1.3.0" } @@ -104,16 +103,12 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.hamcrest:java-hamcrest:2.0.0.0' - testImplementation "org.powermock:powermock-module-junit4:$powermockVersion" - testImplementation "org.powermock:powermock-module-junit4-rule:$powermockVersion" - testImplementation "org.powermock:powermock-api-mockito2:$powermockVersion" - testImplementation 'org.mockito:mockito-core:3.12.4' - // Mockito-Kotlin: See https://github.com/nhaarman/mockito-kotlin/wiki/Parameter-specified-as-non-null-is-null - testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' + testImplementation 'org.mockito:mockito-core:5.7.0' + testImplementation 'org.mockito.kotlin:mockito-kotlin:5.1.0' testImplementation "com.squareup.okhttp3:mockwebserver:$okhttpVersion" testImplementation "com.squareup.okhttp3:okhttp-tls:$okhttpVersion" testImplementation 'com.jayway.awaitility:awaitility:1.7.0' - testImplementation 'org.robolectric:robolectric:4.8.1' + testImplementation 'org.robolectric:robolectric:4.14.1' testImplementation 'androidx.test.espresso:espresso-intents:3.5.1' testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion" diff --git a/auth0/src/test/java/com/auth0/android/Auth0Test.java b/auth0/src/test/java/com/auth0/android/Auth0Test.java index f924a95bb..52eabfd1b 100755 --- a/auth0/src/test/java/com/auth0/android/Auth0Test.java +++ b/auth0/src/test/java/com/auth0/android/Auth0Test.java @@ -23,7 +23,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; import java.lang.reflect.Method; diff --git a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt index 1eb962e33..e83b7cc16 100755 --- a/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/AuthenticationAPIClientTest.kt @@ -32,12 +32,12 @@ import com.auth0.android.util.SSLTestUtils.testClient import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.reflect.TypeToken -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.argumentCaptor -import com.nhaarman.mockitokotlin2.eq -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import okhttp3.HttpUrl.Companion.toHttpUrlOrNull diff --git a/auth0/src/test/java/com/auth0/android/authentication/request/ProfileRequestTest.java b/auth0/src/test/java/com/auth0/android/authentication/request/ProfileRequestTest.java index 6528d46da..3aa931a59 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/request/ProfileRequestTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/request/ProfileRequestTest.java @@ -7,7 +7,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/auth0/src/test/java/com/auth0/android/authentication/request/SignUpRequestTest.java b/auth0/src/test/java/com/auth0/android/authentication/request/SignUpRequestTest.java index 2718fcec6..91caf08c0 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/request/SignUpRequestTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/request/SignUpRequestTest.java @@ -23,7 +23,7 @@ import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt index cf3924763..73e17de4d 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/CredentialsManagerTest.kt @@ -16,16 +16,20 @@ import com.auth0.android.result.SSOCredentialsMock import com.auth0.android.result.toAPICredentials import com.auth0.android.util.Clock import com.google.gson.Gson -import com.nhaarman.mockitokotlin2.KArgumentCaptor -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.argumentCaptor -import com.nhaarman.mockitokotlin2.eq -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never -import com.nhaarman.mockitokotlin2.times -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions -import com.nhaarman.mockitokotlin2.verifyZeroInteractions +import org.mockito.kotlin.KArgumentCaptor +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest @@ -251,7 +255,7 @@ public class CredentialsManagerTest { @Test public fun shouldNotSaveIfTheSSOCredentialsHasNoRefreshToken() { - verifyZeroInteractions(storage) + verifyNoMoreInteractions(storage) val ssoCredentials = SSOCredentialsMock.create( "accessToken", "identityToken", "issuedTokenType", "tokenType", null, 60 diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/LocalAuthenticationManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/LocalAuthenticationManagerTest.kt index 53c14baf1..91f1ea557 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/LocalAuthenticationManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/LocalAuthenticationManagerTest.kt @@ -4,10 +4,10 @@ import androidx.biometric.BiometricManager import androidx.biometric.BiometricPrompt import androidx.fragment.app.FragmentActivity import com.auth0.android.callback.Callback -import com.nhaarman.mockitokotlin2.KArgumentCaptor -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.argumentCaptor -import com.nhaarman.mockitokotlin2.verify +import org.mockito.kotlin.KArgumentCaptor +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.verify import org.hamcrest.MatcherAssert import org.hamcrest.Matchers import org.hamcrest.core.Is diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerBiometricPolicyTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerBiometricPolicyTest.kt index db478ec2e..42449325e 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerBiometricPolicyTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerBiometricPolicyTest.kt @@ -6,7 +6,7 @@ import com.auth0.android.authentication.AuthenticationAPIClient import com.auth0.android.callback.Callback import com.auth0.android.result.Credentials import com.auth0.android.util.Clock -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import org.junit.After import org.junit.Before import org.junit.Test diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt index 893b56fbd..23acda5fe 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/SecureCredentialsManagerTest.kt @@ -22,15 +22,18 @@ import com.auth0.android.result.SSOCredentialsMock import com.auth0.android.result.toAPICredentials import com.auth0.android.util.Clock import com.google.gson.Gson -import com.nhaarman.mockitokotlin2.KArgumentCaptor -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.argumentCaptor -import com.nhaarman.mockitokotlin2.eq -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never -import com.nhaarman.mockitokotlin2.times -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions +import org.mockito.kotlin.KArgumentCaptor +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/SharedPreferencesStorageTest.java b/auth0/src/test/java/com/auth0/android/authentication/storage/SharedPreferencesStorageTest.java index 83b63210c..82fdfc210 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/SharedPreferencesStorageTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/SharedPreferencesStorageTest.java @@ -16,12 +16,12 @@ import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.nullValue; import static org.mockito.ArgumentMatchers.anySet; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyFloat; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt b/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt index c8a8c6a4c..bc0f88fb9 100644 --- a/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt +++ b/auth0/src/test/java/com/auth0/android/dpop/DPoPKeyStoreTest.kt @@ -5,29 +5,29 @@ import android.content.pm.PackageManager import android.os.Build import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties -import android.util.Log -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.anyOrNull -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.`is` import org.hamcrest.Matchers.notNullValue import org.hamcrest.Matchers.nullValue +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.MockedStatic +import org.mockito.Mockito import org.mockito.Mockito.doNothing import org.mockito.Mockito.times import org.mockito.Mockito.`when` -import org.powermock.api.mockito.PowerMockito -import org.powermock.core.classloader.annotations.PrepareForTest -import org.powermock.modules.junit4.PowerMockRunner -import org.powermock.reflect.Whitebox +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config import java.security.InvalidAlgorithmParameterException import java.security.KeyPairGenerator import java.security.KeyStore @@ -36,7 +36,6 @@ import java.security.PrivateKey import java.security.ProviderException import java.security.PublicKey import java.security.cert.Certificate -import javax.security.auth.x500.X500Principal /** * Using a subclass of [DPoPKeyStore] to help with mocking the lazy initialized keyStore property @@ -45,26 +44,20 @@ internal class MockableDPoPKeyStore(private val mockKeyStore: KeyStore) : DPoPKe override val keyStore: KeyStore by lazy { mockKeyStore } } -@RunWith(PowerMockRunner::class) -@PrepareForTest( - DPoPKeyStore::class, - KeyStore::class, - KeyPairGenerator::class, - KeyGenParameterSpec.Builder::class, - Build.VERSION::class, - X500Principal::class, - Log::class -) +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.P]) public class DPoPKeyStoreTest { private lateinit var mockKeyStore: KeyStore private lateinit var mockKeyPairGenerator: KeyPairGenerator private lateinit var mockContext: Context private lateinit var mockPackageManager: PackageManager - private lateinit var mockSpecBuilder: KeyGenParameterSpec.Builder private lateinit var dpopKeyStore: DPoPKeyStore + private lateinit var keyStoreMock: MockedStatic + private lateinit var keyPairGeneratorMock: MockedStatic + @Before public fun setUp() { @@ -72,32 +65,20 @@ public class DPoPKeyStoreTest { mockKeyPairGenerator = mock() mockContext = mock() mockPackageManager = mock() - mockSpecBuilder = mock() - - PowerMockito.mockStatic(KeyStore::class.java) - PowerMockito.mockStatic(KeyPairGenerator::class.java) - PowerMockito.mockStatic(Log::class.java) - PowerMockito.mockStatic(Build.VERSION::class.java) - Whitebox.setInternalState(Build.VERSION::class.java, "SDK_INT", Build.VERSION_CODES.P) - PowerMockito.whenNew(KeyGenParameterSpec.Builder::class.java).withAnyArguments() - .thenReturn(mockSpecBuilder) + keyStoreMock = Mockito.mockStatic(KeyStore::class.java) + keyPairGeneratorMock = Mockito.mockStatic(KeyPairGenerator::class.java) - PowerMockito.`when`(KeyStore.getInstance("AndroidKeyStore")).thenReturn(mockKeyStore) + keyStoreMock.`when` { KeyStore.getInstance("AndroidKeyStore") } + .thenReturn(mockKeyStore) doNothing().whenever(mockKeyStore).load(anyOrNull()) - PowerMockito.`when`( + keyPairGeneratorMock.`when` { KeyPairGenerator.getInstance( KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore" ) - ).thenReturn(mockKeyPairGenerator) - - whenever(mockSpecBuilder.setAlgorithmParameterSpec(any())).thenReturn(mockSpecBuilder) - whenever(mockSpecBuilder.setDigests(any())).thenReturn(mockSpecBuilder) - whenever(mockSpecBuilder.setCertificateSubject(any())).thenReturn(mockSpecBuilder) - whenever(mockSpecBuilder.setCertificateNotBefore(any())).thenReturn(mockSpecBuilder) - whenever(mockSpecBuilder.setCertificateNotAfter(any())).thenReturn(mockSpecBuilder) - whenever(mockSpecBuilder.setIsStrongBoxBacked(any())).thenReturn(mockSpecBuilder) + }.thenReturn(mockKeyPairGenerator) + whenever(mockContext.packageManager).thenReturn(mockPackageManager) whenever(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE)).thenReturn( true @@ -106,6 +87,12 @@ public class DPoPKeyStoreTest { dpopKeyStore = MockableDPoPKeyStore(mockKeyStore) } + @After + public fun tearDown() { + keyStoreMock.close() + keyPairGeneratorMock.close() + } + @Test public fun `generateKeyPair should generate a key pair successfully`() { whenever(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE)).thenReturn( @@ -113,23 +100,26 @@ public class DPoPKeyStoreTest { ) dpopKeyStore.generateKeyPair(mockContext) - verify(mockKeyPairGenerator).initialize(mockSpecBuilder.build()) + verify(mockKeyPairGenerator).initialize(anyOrNull()) verify(mockKeyPairGenerator).generateKeyPair() - verify(mockSpecBuilder, never()).setIsStrongBoxBacked(true) } @Test public fun `generateKeyPair should enable StrongBox when available`() { + val specCaptor = argumentCaptor() + dpopKeyStore.generateKeyPair(mockContext) - verify(mockSpecBuilder).setIsStrongBoxBacked(true) + + verify(mockKeyPairGenerator).initialize(specCaptor.capture()) + verify(mockKeyPairGenerator).generateKeyPair() + + assertThat(specCaptor.firstValue.isStrongBoxBacked, `is`(true)) } @Test public fun `generateKeyPair should throw KEY_GENERATION_ERROR when failed to generate key pair`() { val cause = InvalidAlgorithmParameterException("Exception") - PowerMockito.`when`( - mockKeyPairGenerator.initialize(mockSpecBuilder.build()) - ).thenThrow(cause) + `when`(mockKeyPairGenerator.initialize(anyOrNull())).thenThrow(cause) val exception = assertThrows(DPoPException::class.java) { dpopKeyStore.generateKeyPair(mockContext) @@ -141,9 +131,7 @@ public class DPoPKeyStoreTest { @Test public fun `generateKeyPair should throw UNKNOWN_ERROR when any unhandled exception occurs`() { val cause = RuntimeException("Exception") - PowerMockito.`when`( - mockKeyPairGenerator.initialize(mockSpecBuilder.build()) - ).thenThrow(cause) + `when`(mockKeyPairGenerator.initialize(anyOrNull())).thenThrow(cause) val exception = assertThrows(DPoPException::class.java) { dpopKeyStore.generateKeyPair(mockContext) @@ -238,26 +226,26 @@ public class DPoPKeyStoreTest { @Test public fun `generateKeyPair should retry without StrongBox when ProviderException occurs with StrongBox enabled`() { val providerException = ProviderException("StrongBox attestation failed") + val specCaptor = argumentCaptor() `when`(mockKeyPairGenerator.generateKeyPair()).thenThrow(providerException) .thenReturn(mock()) dpopKeyStore.generateKeyPair(mockContext) - verify(mockKeyPairGenerator, times(2)).initialize(mockSpecBuilder.build()) + verify(mockKeyPairGenerator, times(2)).initialize(specCaptor.capture()) verify(mockKeyPairGenerator, times(2)).generateKeyPair() - verify(mockSpecBuilder).setIsStrongBoxBacked(true) // First attempt - verify( - mockSpecBuilder, - never() - ).setIsStrongBoxBacked(false) + assertThat(specCaptor.allValues[0].isStrongBoxBacked, `is`(true)) + assertThat(specCaptor.allValues[1].isStrongBoxBacked, `is`(false)) } @Test public fun `generateKeyPair should throw KEY_GENERATION_ERROR when ProviderException occurs without StrongBox`() { val providerException = ProviderException("Key generation failed") - `when`(mockKeyPairGenerator.initialize(mockSpecBuilder.build())).thenThrow(providerException) + `when`(mockKeyPairGenerator.initialize(anyOrNull())).thenThrow( + providerException + ) val exception = assertThrows(DPoPException::class.java) { dpopKeyStore.generateKeyPair(mockContext, useStrongBox = false) @@ -266,7 +254,7 @@ public class DPoPKeyStoreTest { assertEquals(DPoPException.KEY_GENERATION_ERROR.message, exception.message) assertThat(exception.cause, `is`(providerException)) - verify(mockKeyPairGenerator, times(1)).initialize(mockSpecBuilder.build()) + verify(mockKeyPairGenerator, times(1)).initialize(anyOrNull()) } @Test @@ -274,7 +262,7 @@ public class DPoPKeyStoreTest { val firstException = ProviderException("StrongBox failed") val secondException = ProviderException("Retry also failed") - `when`(mockKeyPairGenerator.initialize(mockSpecBuilder.build())) + `when`(mockKeyPairGenerator.initialize(anyOrNull())) .thenThrow(firstException) .thenThrow(secondException) @@ -285,6 +273,6 @@ public class DPoPKeyStoreTest { assertEquals(DPoPException.KEY_GENERATION_ERROR.message, exception.message) assertThat(exception.cause, `is`(secondException)) - verify(mockKeyPairGenerator, times(2)).initialize(mockSpecBuilder.build()) + verify(mockKeyPairGenerator, times(2)).initialize(anyOrNull()) } } \ No newline at end of file diff --git a/auth0/src/test/java/com/auth0/android/dpop/DPoPTest.kt b/auth0/src/test/java/com/auth0/android/dpop/DPoPTest.kt index 3518e70d5..f2c45c365 100644 --- a/auth0/src/test/java/com/auth0/android/dpop/DPoPTest.kt +++ b/auth0/src/test/java/com/auth0/android/dpop/DPoPTest.kt @@ -3,9 +3,9 @@ package com.auth0.android.dpop import android.content.Context import com.auth0.android.request.HttpMethod import com.auth0.android.request.internal.Jwt -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever import okhttp3.Headers import okhttp3.Response import okhttp3.ResponseBody diff --git a/auth0/src/test/java/com/auth0/android/dpop/DPoPUtilTest.kt b/auth0/src/test/java/com/auth0/android/dpop/DPoPUtilTest.kt index 1ba8050a9..19287e814 100644 --- a/auth0/src/test/java/com/auth0/android/dpop/DPoPUtilTest.kt +++ b/auth0/src/test/java/com/auth0/android/dpop/DPoPUtilTest.kt @@ -3,12 +3,15 @@ package com.auth0.android.dpop import android.content.Context import com.auth0.android.request.internal.Jwt import com.google.gson.internal.LinkedTreeMap -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions -import com.nhaarman.mockitokotlin2.whenever +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.verifyNoMoreInteractions +import org.mockito.kotlin.whenever import okhttp3.Response import org.hamcrest.CoreMatchers.`is` import org.hamcrest.CoreMatchers.notNullValue diff --git a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.kt index 728e026d0..64bc7e25a 100755 --- a/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/management/UsersAPIClientTest.kt @@ -18,7 +18,7 @@ import com.auth0.android.util.SSLTestUtils.testClient import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.reflect.TypeToken -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import okhttp3.mockwebserver.RecordedRequest diff --git a/auth0/src/test/java/com/auth0/android/myaccount/MyAccountAPIClientTest.kt b/auth0/src/test/java/com/auth0/android/myaccount/MyAccountAPIClientTest.kt index 342cf3b34..41d647dfd 100644 --- a/auth0/src/test/java/com/auth0/android/myaccount/MyAccountAPIClientTest.kt +++ b/auth0/src/test/java/com/auth0/android/myaccount/MyAccountAPIClientTest.kt @@ -17,7 +17,7 @@ import com.auth0.android.util.SSLTestUtils.testClient import com.google.gson.Gson import com.google.gson.GsonBuilder import com.google.gson.reflect.TypeToken -import com.nhaarman.mockitokotlin2.mock +import org.mockito.kotlin.mock import okhttp3.mockwebserver.RecordedRequest import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers diff --git a/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.kt b/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.kt index b78c3aadf..ace70dd8f 100644 --- a/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.kt +++ b/auth0/src/test/java/com/auth0/android/provider/AuthenticationActivityTest.kt @@ -13,7 +13,7 @@ import com.auth0.android.provider.AuthenticationActivity.Companion.EXTRA_AUTHORI import com.auth0.android.provider.AuthenticationActivity.Companion.EXTRA_CT_OPTIONS import com.auth0.android.provider.AuthenticationActivity.Companion.EXTRA_LAUNCH_AS_TWA import com.auth0.android.provider.CustomTabsOptions -import com.nhaarman.mockitokotlin2.any +import org.mockito.kotlin.any import org.hamcrest.CoreMatchers import org.hamcrest.MatcherAssert import org.hamcrest.Matchers diff --git a/auth0/src/test/java/com/auth0/android/provider/BrowserPickerTest.java b/auth0/src/test/java/com/auth0/android/provider/BrowserPickerTest.java index 728d62f62..19704c517 100644 --- a/auth0/src/test/java/com/auth0/android/provider/BrowserPickerTest.java +++ b/auth0/src/test/java/com/auth0/android/provider/BrowserPickerTest.java @@ -29,8 +29,8 @@ import static org.hamcrest.Matchers.isOneOf; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.IsNull.notNullValue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; diff --git a/auth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.java b/auth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.java index bdbfbf5f9..b026dac88 100644 --- a/auth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.java +++ b/auth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.java @@ -1,5 +1,26 @@ package com.auth0.android.provider; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasFlag; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.Is.isA; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.withSettings; + import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -8,7 +29,6 @@ import android.content.ServiceConnection; import android.graphics.Color; import android.net.Uri; -import android.os.Looper; import androidx.annotation.NonNull; import androidx.browser.customtabs.CustomTabsCallback; @@ -16,53 +36,27 @@ import androidx.browser.customtabs.CustomTabsIntent; import androidx.browser.customtabs.CustomTabsServiceConnection; import androidx.browser.customtabs.CustomTabsSession; -import androidx.browser.trusted.TrustedWebActivityDisplayMode; import androidx.browser.trusted.TrustedWebActivityIntentBuilder; +import com.auth0.android.authentication.AuthenticationException; +import com.auth0.android.request.internal.ThreadSwitcher; +import com.google.androidbrowserhelper.trusted.TwaLauncher; +import com.google.androidbrowserhelper.trusted.splashscreens.SplashScreenStrategy; + import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import org.powermock.api.mockito.PowerMockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import java.util.List; -import static androidx.test.espresso.intent.matcher.IntentMatchers.hasFlag; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.core.Is.isA; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.auth0.android.authentication.AuthenticationException; -import com.auth0.android.request.internal.CommonThreadSwitcher; -import com.auth0.android.request.internal.ThreadSwitcher; -import com.auth0.android.util.CommonThreadSwitcherRule; -import com.google.androidbrowserhelper.trusted.TwaLauncher; -import com.google.androidbrowserhelper.trusted.splashscreens.SplashScreenStrategy; - @RunWith(RobolectricTestRunner.class) public class CustomTabsControllerTest { @@ -359,10 +353,16 @@ private void bindService(CustomTabsController controller, boolean willSucceed) { } private void connectBoundService() throws Exception { - CustomTabsSession session = mock(CustomTabsSession.class); - ComponentName componentName = new ComponentName(DEFAULT_BROWSER_PACKAGE, DEFAULT_BROWSER_PACKAGE + ".CustomTabsService"); - //This depends on an implementation detail but is the only way to test it because of methods visibility - PowerMockito.when(session, "getComponentName").thenReturn(componentName); + final ComponentName componentName = new ComponentName(DEFAULT_BROWSER_PACKAGE, DEFAULT_BROWSER_PACKAGE + ".CustomTabsService"); + + CustomTabsSession session = mock(CustomTabsSession.class, withSettings() + .lenient() + .defaultAnswer((Answer) invocation -> { + if ("getComponentName".equals(invocation.getMethod().getName())) { + return componentName; + } + return null; + })); when(customTabsClient.newSession(eq(null))).thenReturn(session); CustomTabsServiceConnection conn = serviceConnectionCaptor.getValue(); diff --git a/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java b/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java index c4f03efb4..e6d86183d 100644 --- a/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java +++ b/auth0/src/test/java/com/auth0/android/provider/CustomTabsOptionsTest.java @@ -24,7 +24,7 @@ import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; diff --git a/auth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.kt b/auth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.kt index e4ac82383..2cf2ec8e7 100644 --- a/auth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.kt +++ b/auth0/src/test/java/com/auth0/android/provider/OAuthManagerStateTest.kt @@ -2,7 +2,7 @@ package com.auth0.android.provider import android.graphics.Color import com.auth0.android.Auth0 -import com.nhaarman.mockitokotlin2.mock +import org.mockito.kotlin.mock import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith diff --git a/auth0/src/test/java/com/auth0/android/provider/PKCETest.java b/auth0/src/test/java/com/auth0/android/provider/PKCETest.java index f352324fc..d6d1ad58c 100644 --- a/auth0/src/test/java/com/auth0/android/provider/PKCETest.java +++ b/auth0/src/test/java/com/auth0/android/provider/PKCETest.java @@ -29,8 +29,8 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt b/auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt index 3f99b78da..f53dc7455 100644 --- a/auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt +++ b/auth0/src/test/java/com/auth0/android/provider/PasskeyManagerTest.kt @@ -28,15 +28,15 @@ import com.auth0.android.result.PasskeyRegistrationChallenge import com.auth0.android.result.PasskeyUser import com.auth0.android.result.PubKeyCredParam import com.auth0.android.result.RelyingParty -import com.nhaarman.mockitokotlin2.KArgumentCaptor -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.argumentCaptor -import com.nhaarman.mockitokotlin2.doAnswer -import com.nhaarman.mockitokotlin2.eq -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.never -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever +import org.mockito.kotlin.KArgumentCaptor +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever import org.junit.Assert import org.junit.Before import org.junit.Test diff --git a/auth0/src/test/java/com/auth0/android/provider/PermissionHandlerTest.java b/auth0/src/test/java/com/auth0/android/provider/PermissionHandlerTest.java index de8ff367e..e0c44bd08 100644 --- a/auth0/src/test/java/com/auth0/android/provider/PermissionHandlerTest.java +++ b/auth0/src/test/java/com/auth0/android/provider/PermissionHandlerTest.java @@ -21,9 +21,9 @@ import static org.hamcrest.core.IsCollectionContaining.hasItems; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/auth0/src/test/java/com/auth0/android/provider/WebAuthProviderTest.kt b/auth0/src/test/java/com/auth0/android/provider/WebAuthProviderTest.kt index d603d3456..fceed4e59 100644 --- a/auth0/src/test/java/com/auth0/android/provider/WebAuthProviderTest.kt +++ b/auth0/src/test/java/com/auth0/android/provider/WebAuthProviderTest.kt @@ -27,7 +27,7 @@ import com.auth0.android.request.internal.ThreadSwitcherShadow import com.auth0.android.result.Credentials import com.auth0.android.util.AuthenticationAPIMockServer import com.auth0.android.util.SSLTestUtils -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch diff --git a/auth0/src/test/java/com/auth0/android/request/RetryInterceptorTest.kt b/auth0/src/test/java/com/auth0/android/request/RetryInterceptorTest.kt index bb10f1ce5..687f3847b 100644 --- a/auth0/src/test/java/com/auth0/android/request/RetryInterceptorTest.kt +++ b/auth0/src/test/java/com/auth0/android/request/RetryInterceptorTest.kt @@ -5,12 +5,12 @@ import com.auth0.android.dpop.DPoPKeyStore import com.auth0.android.dpop.DPoPUtil import com.auth0.android.dpop.FakeECPrivateKey import com.auth0.android.dpop.FakeECPublicKey -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.argumentCaptor -import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.times -import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.whenever +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever import okhttp3.Interceptor import okhttp3.Protocol import okhttp3.Request diff --git a/auth0/src/test/java/com/auth0/android/request/internal/BaseAuthenticationRequestTest.kt b/auth0/src/test/java/com/auth0/android/request/internal/BaseAuthenticationRequestTest.kt index 76c9d7990..157a6d5a4 100644 --- a/auth0/src/test/java/com/auth0/android/request/internal/BaseAuthenticationRequestTest.kt +++ b/auth0/src/test/java/com/auth0/android/request/internal/BaseAuthenticationRequestTest.kt @@ -3,7 +3,7 @@ package com.auth0.android.request.internal import com.auth0.android.authentication.AuthenticationException import com.auth0.android.request.* import com.auth0.android.result.Credentials -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import org.hamcrest.CoreMatchers import org.hamcrest.MatcherAssert import org.hamcrest.collection.IsMapContaining diff --git a/auth0/src/test/java/com/auth0/android/request/internal/BaseRequestTest.kt b/auth0/src/test/java/com/auth0/android/request/internal/BaseRequestTest.kt index c459068f9..24f608345 100755 --- a/auth0/src/test/java/com/auth0/android/request/internal/BaseRequestTest.kt +++ b/auth0/src/test/java/com/auth0/android/request/internal/BaseRequestTest.kt @@ -8,7 +8,7 @@ import com.auth0.android.dpop.DPoPUtil.DPOP_HEADER import com.auth0.android.request.* import com.google.gson.Gson import com.google.gson.JsonIOException -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -199,7 +199,7 @@ public class BaseRequestTest { MatcherAssert.assertThat(exception, Matchers.`is`(wrappingAuth0Exception)) MatcherAssert.assertThat(result, Matchers.`is`(Matchers.nullValue())) verify(errorAdapter).fromException(networkError) - verifyZeroInteractions(resultAdapter) + verifyNoMoreInteractions(resultAdapter) verifyNoMoreInteractions(errorAdapter) } @@ -253,7 +253,7 @@ public class BaseRequestTest { eq(422), any() ) MatcherAssert.assertThat(wasResponseStreamClosed, Matchers.`is`(true)) - verifyZeroInteractions(resultAdapter) + verifyNoMoreInteractions(resultAdapter) verifyNoMoreInteractions(errorAdapter) } @@ -287,7 +287,7 @@ public class BaseRequestTest { ) ) MatcherAssert.assertThat(wasResponseStreamClosed, Matchers.`is`(true)) - verifyZeroInteractions(resultAdapter) + verifyNoMoreInteractions(resultAdapter) verifyNoMoreInteractions(errorAdapter) } @@ -308,7 +308,7 @@ public class BaseRequestTest { verify(resultAdapter).fromJson(any(), any()) MatcherAssert.assertThat(wasResponseStreamClosed, Matchers.`is`(true)) verifyNoMoreInteractions(resultAdapter) - verifyZeroInteractions(errorAdapter) + verifyNoMoreInteractions(errorAdapter) } @Test @@ -328,7 +328,7 @@ public class BaseRequestTest { verify(errorAdapter).fromJsonResponse(eq(422), any()) verify(errorAdapter).fromException(networkError) MatcherAssert.assertThat(wasResponseStreamClosed, Matchers.`is`(true)) - verifyZeroInteractions(resultAdapter) + verifyNoMoreInteractions(resultAdapter) verifyNoMoreInteractions(errorAdapter) } @@ -348,7 +348,7 @@ public class BaseRequestTest { MatcherAssert.assertThat(exception, Matchers.`is`(wrappingAuth0Exception)) verify(errorAdapter).fromException(networkError) MatcherAssert.assertThat(wasResponseStreamClosed, Matchers.`is`(true)) - verifyZeroInteractions(resultAdapter) + verifyNoMoreInteractions(resultAdapter) verifyNoMoreInteractions(errorAdapter) } diff --git a/auth0/src/test/java/com/auth0/android/request/internal/CommonThreadSwitcherDelegateTest.kt b/auth0/src/test/java/com/auth0/android/request/internal/CommonThreadSwitcherDelegateTest.kt index c29eccf15..cd7c1eb55 100644 --- a/auth0/src/test/java/com/auth0/android/request/internal/CommonThreadSwitcherDelegateTest.kt +++ b/auth0/src/test/java/com/auth0/android/request/internal/CommonThreadSwitcherDelegateTest.kt @@ -5,7 +5,7 @@ import com.auth0.android.callback.Callback import com.auth0.android.request.* import com.auth0.android.util.CommonThreadSwitcherRule import com.google.gson.Gson -import com.nhaarman.mockitokotlin2.* +import org.mockito.kotlin.* import org.hamcrest.MatcherAssert import org.hamcrest.Matchers import org.junit.Before diff --git a/auth0/src/test/java/com/auth0/android/request/internal/TLS12SocketFactoryTest.java b/auth0/src/test/java/com/auth0/android/request/internal/TLS12SocketFactoryTest.java index 8391bebd2..6e773ba5c 100644 --- a/auth0/src/test/java/com/auth0/android/request/internal/TLS12SocketFactoryTest.java +++ b/auth0/src/test/java/com/auth0/android/request/internal/TLS12SocketFactoryTest.java @@ -17,10 +17,10 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; From ea381ff6b207722db676392378bbf0a387f44428 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Mon, 2 Feb 2026 21:58:32 +0530 Subject: [PATCH 5/6] Removed Powermock dependency and updated tests to use Mockito --- .../storage/CryptoUtilTest.java | 1031 ++++++++--------- .../provider/CustomTabsControllerTest.java | 6 +- 2 files changed, 455 insertions(+), 582 deletions(-) diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java b/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java index d9f84c910..6fcf0507c 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java @@ -1,29 +1,43 @@ package com.auth0.android.authentication.storage; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; -import android.os.Build; import android.security.KeyPairGeneratorSpec; import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.text.TextUtils; import android.util.Base64; -import android.util.Log; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.stubbing.Answer; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.util.ReflectionHelpers; import java.io.IOException; import java.math.BigInteger; @@ -53,37 +67,15 @@ import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; -import javax.security.auth.x500.X500Principal; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.greaterThan; -import static org.mockito.ArgumentMatchers.nullable; -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.times; -import static org.mockito.Mockito.when; -import static org.powermock.api.mockito.PowerMockito.doReturn; -import static org.powermock.api.mockito.PowerMockito.doThrow; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.verifyPrivate; /** - * In the rest of the test files we use Mockito as that's enough for most cases. However, - * when Kotlin classes are introduced in the project, Mockito fails to mock them because - * they are final by default. - * The solution is to use the 'mockito-inline' plugin. However, when used in combination - * with Powermock, both configuration files clash and the tests fail. - * The MockMaker needs to be set up only in one place, the Powermock configuration file. - *

- * Read more: https://github.com/powermock/powermock/issues/992#issuecomment-662845804 + * This test class uses MockedStatic for static method mocking (KeyStore, Cipher, KeyGenerator, + * KeyPairGenerator, Base64, TextUtils) and relies on Robolectric shadows for Android SDK + * builder classes like KeyGenParameterSpec.Builder and KeyPairGeneratorSpec.Builder. + * Note: Robolectric 4.x requires SDK 21+ (Android 5.0+). */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({CryptoUtil.class, KeyGenerator.class, TextUtils.class, Build.VERSION.class, Base64.class, Cipher.class, Log.class, KeyStore.class}) +@RunWith(RobolectricTestRunner.class) +@Config(manifest = Config.NONE) public class CryptoUtilTest { private static final String RSA_TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; @@ -95,13 +87,20 @@ public class CryptoUtilTest { private static final String ALGORITHM_RSA = "RSA"; private static final int RSA_KEY_SIZE = 2048; - private final Storage storage = PowerMockito.mock(Storage.class); - private final Cipher rsaOaepCipher = PowerMockito.mock(Cipher.class); - private final Cipher rsaPkcs1Cipher = PowerMockito.mock(Cipher.class); - private final Cipher aesCipher = PowerMockito.mock(Cipher.class); - private final KeyStore keyStore = PowerMockito.mock(KeyStore.class); - private final KeyPairGenerator keyPairGenerator = PowerMockito.mock(KeyPairGenerator.class); - private final KeyGenerator keyGenerator = PowerMockito.mock(KeyGenerator.class); + private final Storage storage = Mockito.mock(Storage.class); + private final Cipher rsaOaepCipher = Mockito.mock(Cipher.class); + private final Cipher rsaPkcs1Cipher = Mockito.mock(Cipher.class); + private final Cipher aesCipher = Mockito.mock(Cipher.class); + private final KeyStore keyStore = Mockito.mock(KeyStore.class); + private final KeyPairGenerator keyPairGenerator = Mockito.mock(KeyPairGenerator.class); + private final KeyGenerator keyGenerator = Mockito.mock(KeyGenerator.class); + + private MockedStatic keyStoreMock; + private MockedStatic cipherMock; + private MockedStatic keyGeneratorMock; + private MockedStatic keyPairGeneratorMock; + private MockedStatic base64Mock; + private MockedStatic textUtilsMock; private CryptoUtil cryptoUtil; @@ -117,18 +116,49 @@ public class CryptoUtilTest { @Before public void setUp() throws Exception { - PowerMockito.mockStatic(Log.class); - PowerMockito.mockStatic(TextUtils.class); - PowerMockito.when(TextUtils.isEmpty(nullable(String.class))).then((Answer) invocation -> { - String input = invocation.getArgument(0, String.class); - return input == null || input.isEmpty(); + // Initialize MockedStatic instances for static method mocking + keyStoreMock = Mockito.mockStatic(KeyStore.class); + keyStoreMock.when(() -> KeyStore.getInstance(ANDROID_KEY_STORE)).thenReturn(keyStore); + + cipherMock = Mockito.mockStatic(Cipher.class); + cipherMock.when(() -> Cipher.getInstance(anyString())).thenAnswer((Answer) invocation -> { + String transformation = invocation.getArgument(0, String.class); + if (RSA_TRANSFORMATION.equals(transformation)) { + return rsaOaepCipher; + } else if (OLD_RSA_PKCS1_TRANSFORMATION.equals(transformation)) { + return rsaPkcs1Cipher; + } else if (AES_TRANSFORMATION.equals(transformation)) { + return aesCipher; + } + return null; }); + keyGeneratorMock = Mockito.mockStatic(KeyGenerator.class); + keyGeneratorMock.when(() -> KeyGenerator.getInstance(ALGORITHM_AES)).thenReturn(keyGenerator); + + keyPairGeneratorMock = Mockito.mockStatic(KeyPairGenerator.class); + keyPairGeneratorMock.when(() -> KeyPairGenerator.getInstance(ALGORITHM_RSA, ANDROID_KEY_STORE)) + .thenReturn(keyPairGenerator); + + base64Mock = Mockito.mockStatic(Base64.class, Mockito.CALLS_REAL_METHODS); + textUtilsMock = Mockito.mockStatic(TextUtils.class, Mockito.CALLS_REAL_METHODS); + context = mock(Context.class); when(context.getPackageName()).thenReturn(APP_PACKAGE_NAME); cryptoUtil = newCryptoUtilSpy(); } + @After + public void tearDown() { + // Close all MockedStatic instances to prevent memory leaks + if (keyStoreMock != null) keyStoreMock.close(); + if (cipherMock != null) cipherMock.close(); + if (keyGeneratorMock != null) keyGeneratorMock.close(); + if (keyPairGeneratorMock != null) keyPairGeneratorMock.close(); + if (base64Mock != null) base64Mock.close(); + if (textUtilsMock != null) textUtilsMock.close(); + } + /* * GET RSA KEY tests */ @@ -142,49 +172,42 @@ public void shouldThrowWhenRSAKeyAliasIsInvalid() { } @Test - @Config(sdk = 19) - public void shouldNotCreateProtectedRSAKeyPairIfMissingAndLockScreenEnabledOnAPI19() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 19); - - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - KeyStore.PrivateKeyEntry expectedEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); - - KeyPairGeneratorSpec spec = PowerMockito.mock(KeyPairGeneratorSpec.class); - KeyPairGeneratorSpec.Builder builder = newKeyPairGeneratorSpecBuilder(spec); - PowerMockito.whenNew(KeyPairGeneratorSpec.Builder.class).withAnyArguments().thenReturn(builder); + @Config(sdk = 21) + public void shouldNotCreateProtectedRSAKeyPairIfMissingAndLockScreenEnabled() throws Exception { + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); + KeyStore.PrivateKeyEntry expectedEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); - ArgumentCaptor principalCaptor = ArgumentCaptor.forClass(X500Principal.class); - ArgumentCaptor startDateCaptor = ArgumentCaptor.forClass(Date.class); - ArgumentCaptor endDateCaptor = ArgumentCaptor.forClass(Date.class); + ArgumentCaptor specCaptor = ArgumentCaptor.forClass(AlgorithmParameterSpec.class); - //Set LockScreen as Enabled - KeyguardManager kService = PowerMockito.mock(KeyguardManager.class); - PowerMockito.when(context.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(kService); - PowerMockito.when(kService.isKeyguardSecure()).thenReturn(true); + //Set LockScreen as Enabled but with null device credential intent + KeyguardManager kService = Mockito.mock(KeyguardManager.class); + Mockito.when(context.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(kService); + Mockito.when(kService.isKeyguardSecure()).thenReturn(true); + Mockito.when(kService.createConfirmDeviceCredentialIntent(nullable(CharSequence.class), nullable(CharSequence.class))).thenReturn(null); final KeyStore.PrivateKeyEntry entry = cryptoUtil.getRSAKeyEntry(); - Mockito.verify(builder).setKeySize(2048); - Mockito.verify(builder).setSubject(principalCaptor.capture()); - Mockito.verify(builder).setAlias(KEY_ALIAS); - Mockito.verify(builder).setSerialNumber(BigInteger.ONE); - Mockito.verify(builder).setStartDate(startDateCaptor.capture()); - Mockito.verify(builder).setEndDate(endDateCaptor.capture()); - Mockito.verify(builder, never()).setEncryptionRequired(); - Mockito.verify(keyPairGenerator).initialize(spec); + Mockito.verify(keyPairGenerator).initialize(specCaptor.capture()); Mockito.verify(keyPairGenerator).generateKeyPair(); - assertThat(principalCaptor.getValue(), is(notNullValue())); - assertThat(principalCaptor.getValue().getName(), is(CERTIFICATE_PRINCIPAL)); + // Verify the spec properties directly (Robolectric shadows the real builder) + KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) specCaptor.getValue(); + assertThat(spec.getKeySize(), is(2048)); + assertThat(spec.getKeystoreAlias(), is(KEY_ALIAS)); + assertThat(spec.getSerialNumber(), is(BigInteger.ONE)); + // Note: setEncryptionRequired was NOT called since authIntent is null + + assertThat(spec.getSubjectDN(), is(notNullValue())); + assertThat(spec.getSubjectDN().getName(), is(CERTIFICATE_PRINCIPAL)); - assertThat(startDateCaptor.getValue(), is(notNullValue())); - long diffMillis = startDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getStartDate(), is(notNullValue())); + long diffMillis = spec.getStartDate().getTime() - new Date().getTime(); long days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(0L)); //Date is Today - assertThat(endDateCaptor.getValue(), is(notNullValue())); - diffMillis = endDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getEndDate(), is(notNullValue())); + diffMillis = spec.getEndDate().getTime() - new Date().getTime(); days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(greaterThan(25 * 365L))); //Date more than 25 Years in days @@ -194,48 +217,40 @@ public void shouldNotCreateProtectedRSAKeyPairIfMissingAndLockScreenEnabledOnAPI @Test @Config(sdk = 21) public void shouldCreateUnprotectedRSAKeyPairIfMissingAndLockScreenDisabledOnAPI21() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 21); - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - KeyStore.PrivateKeyEntry expectedEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); + KeyStore.PrivateKeyEntry expectedEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); - KeyPairGeneratorSpec spec = PowerMockito.mock(KeyPairGeneratorSpec.class); - KeyPairGeneratorSpec.Builder builder = newKeyPairGeneratorSpecBuilder(spec); - PowerMockito.whenNew(KeyPairGeneratorSpec.Builder.class).withAnyArguments().thenReturn(builder); - - ArgumentCaptor principalCaptor = ArgumentCaptor.forClass(X500Principal.class); - ArgumentCaptor startDateCaptor = ArgumentCaptor.forClass(Date.class); - ArgumentCaptor endDateCaptor = ArgumentCaptor.forClass(Date.class); + ArgumentCaptor specCaptor = ArgumentCaptor.forClass(AlgorithmParameterSpec.class); //Set LockScreen as Disabled - KeyguardManager kService = PowerMockito.mock(KeyguardManager.class); - PowerMockito.when(context.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(kService); - PowerMockito.when(kService.isKeyguardSecure()).thenReturn(false); - PowerMockito.when(kService.createConfirmDeviceCredentialIntent(any(CharSequence.class), any(CharSequence.class))).thenReturn(null); + KeyguardManager kService = Mockito.mock(KeyguardManager.class); + Mockito.when(context.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(kService); + Mockito.when(kService.isKeyguardSecure()).thenReturn(false); + Mockito.when(kService.createConfirmDeviceCredentialIntent(any(CharSequence.class), any(CharSequence.class))).thenReturn(null); final KeyStore.PrivateKeyEntry entry = cryptoUtil.getRSAKeyEntry(); - Mockito.verify(builder).setKeySize(2048); - Mockito.verify(builder).setSubject(principalCaptor.capture()); - Mockito.verify(builder).setAlias(KEY_ALIAS); - Mockito.verify(builder).setSerialNumber(BigInteger.ONE); - Mockito.verify(builder).setStartDate(startDateCaptor.capture()); - Mockito.verify(builder).setEndDate(endDateCaptor.capture()); - Mockito.verify(builder, never()).setEncryptionRequired(); - Mockito.verify(keyPairGenerator).initialize(spec); + Mockito.verify(keyPairGenerator).initialize(specCaptor.capture()); Mockito.verify(keyPairGenerator).generateKeyPair(); - assertThat(principalCaptor.getValue(), is(notNullValue())); - assertThat(principalCaptor.getValue().getName(), is(CERTIFICATE_PRINCIPAL)); + // Verify the spec properties directly + KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) specCaptor.getValue(); + assertThat(spec.getKeySize(), is(2048)); + assertThat(spec.getKeystoreAlias(), is(KEY_ALIAS)); + assertThat(spec.getSerialNumber(), is(BigInteger.ONE)); + + assertThat(spec.getSubjectDN(), is(notNullValue())); + assertThat(spec.getSubjectDN().getName(), is(CERTIFICATE_PRINCIPAL)); - assertThat(startDateCaptor.getValue(), is(notNullValue())); - long diffMillis = startDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getStartDate(), is(notNullValue())); + long diffMillis = spec.getStartDate().getTime() - new Date().getTime(); long days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(0L)); //Date is Today - assertThat(endDateCaptor.getValue(), is(notNullValue())); - diffMillis = endDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getEndDate(), is(notNullValue())); + diffMillis = spec.getEndDate().getTime() - new Date().getTime(); days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(greaterThan(25 * 365L))); //Date more than 25 Years in days @@ -245,48 +260,42 @@ public void shouldCreateUnprotectedRSAKeyPairIfMissingAndLockScreenDisabledOnAPI @Test @Config(sdk = 21) public void shouldCreateProtectedRSAKeyPairIfMissingAndLockScreenEnabledOnAPI21() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 21); - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - KeyStore.PrivateKeyEntry expectedEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); + KeyStore.PrivateKeyEntry expectedEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); - KeyPairGeneratorSpec spec = PowerMockito.mock(KeyPairGeneratorSpec.class); - KeyPairGeneratorSpec.Builder builder = newKeyPairGeneratorSpecBuilder(spec); - PowerMockito.whenNew(KeyPairGeneratorSpec.Builder.class).withAnyArguments().thenReturn(builder); - - ArgumentCaptor principalCaptor = ArgumentCaptor.forClass(X500Principal.class); - ArgumentCaptor startDateCaptor = ArgumentCaptor.forClass(Date.class); - ArgumentCaptor endDateCaptor = ArgumentCaptor.forClass(Date.class); + ArgumentCaptor specCaptor = ArgumentCaptor.forClass(AlgorithmParameterSpec.class); //Set LockScreen as Enabled - KeyguardManager kService = PowerMockito.mock(KeyguardManager.class); - PowerMockito.when(context.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(kService); - PowerMockito.when(kService.isKeyguardSecure()).thenReturn(true); - PowerMockito.when(kService.createConfirmDeviceCredentialIntent(any(), any())).thenReturn(new Intent()); + KeyguardManager kService = Mockito.mock(KeyguardManager.class); + Mockito.when(context.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(kService); + Mockito.when(kService.isKeyguardSecure()).thenReturn(true); + Mockito.when(kService.createConfirmDeviceCredentialIntent(any(), any())).thenReturn(new Intent()); final KeyStore.PrivateKeyEntry entry = cryptoUtil.getRSAKeyEntry(); - Mockito.verify(builder).setKeySize(2048); - Mockito.verify(builder).setSubject(principalCaptor.capture()); - Mockito.verify(builder).setAlias(KEY_ALIAS); - Mockito.verify(builder).setSerialNumber(BigInteger.ONE); - Mockito.verify(builder).setStartDate(startDateCaptor.capture()); - Mockito.verify(builder).setEndDate(endDateCaptor.capture()); - Mockito.verify(builder).setEncryptionRequired(); - Mockito.verify(keyPairGenerator).initialize(spec); + Mockito.verify(keyPairGenerator).initialize(specCaptor.capture()); Mockito.verify(keyPairGenerator).generateKeyPair(); - assertThat(principalCaptor.getValue(), is(notNullValue())); - assertThat(principalCaptor.getValue().getName(), is(CERTIFICATE_PRINCIPAL)); + // Verify the spec properties directly + KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) specCaptor.getValue(); + assertThat(spec.getKeySize(), is(2048)); + assertThat(spec.getKeystoreAlias(), is(KEY_ALIAS)); + assertThat(spec.getSerialNumber(), is(BigInteger.ONE)); + // Note: setEncryptionRequired WAS called since lock screen is enabled with valid authIntent + assertThat(spec.isEncryptionRequired(), is(true)); + + assertThat(spec.getSubjectDN(), is(notNullValue())); + assertThat(spec.getSubjectDN().getName(), is(CERTIFICATE_PRINCIPAL)); - assertThat(startDateCaptor.getValue(), is(notNullValue())); - long diffMillis = startDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getStartDate(), is(notNullValue())); + long diffMillis = spec.getStartDate().getTime() - new Date().getTime(); long days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(0L)); //Date is Today - assertThat(endDateCaptor.getValue(), is(notNullValue())); - diffMillis = endDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getEndDate(), is(notNullValue())); + diffMillis = spec.getEndDate().getTime() - new Date().getTime(); days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(greaterThan(25 * 365L))); //Date more than 25 Years in days @@ -296,45 +305,37 @@ public void shouldCreateProtectedRSAKeyPairIfMissingAndLockScreenEnabledOnAPI21( @Test @Config(sdk = 23) public void shouldCreateRSAKeyPairIfMissingOnAPI23AndUp() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 23); - - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - KeyStore.PrivateKeyEntry expectedEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); - - KeyGenParameterSpec spec = PowerMockito.mock(KeyGenParameterSpec.class); - KeyGenParameterSpec.Builder builder = newKeyGenParameterSpecBuilder(spec); - PowerMockito.whenNew(KeyGenParameterSpec.Builder.class).withArguments(KEY_ALIAS, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT).thenReturn(builder); - ArgumentCaptor principalCaptor = ArgumentCaptor.forClass(X500Principal.class); - ArgumentCaptor startDateCaptor = ArgumentCaptor.forClass(Date.class); - ArgumentCaptor endDateCaptor = ArgumentCaptor.forClass(Date.class); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); + KeyStore.PrivateKeyEntry expectedEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); + ArgumentCaptor specCaptor = ArgumentCaptor.forClass(AlgorithmParameterSpec.class); final KeyStore.PrivateKeyEntry entry = cryptoUtil.getRSAKeyEntry(); - - Mockito.verify(builder).setKeySize(2048); - Mockito.verify(builder).setCertificateSubject(principalCaptor.capture()); - Mockito.verify(builder).setCertificateSerialNumber(BigInteger.ONE); - Mockito.verify(builder).setCertificateNotBefore(startDateCaptor.capture()); - Mockito.verify(builder).setCertificateNotAfter(endDateCaptor.capture()); - Mockito.verify(builder).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); - Mockito.verify(builder).setDigests(KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256); - Mockito.verify(builder).setBlockModes(KeyProperties.BLOCK_MODE_ECB); - Mockito.verify(keyPairGenerator).initialize(spec); + Mockito.verify(keyPairGenerator).initialize(specCaptor.capture()); Mockito.verify(keyPairGenerator).generateKeyPair(); - assertThat(principalCaptor.getValue(), is(notNullValue())); - assertThat(principalCaptor.getValue().getName(), is(CERTIFICATE_PRINCIPAL)); + // Verify the spec properties directly + KeyGenParameterSpec spec = (KeyGenParameterSpec) specCaptor.getValue(); + assertThat(spec.getKeySize(), is(2048)); + assertThat(spec.getKeystoreAlias(), is(KEY_ALIAS)); + assertThat(spec.getCertificateSerialNumber(), is(BigInteger.ONE)); + assertThat(spec.getEncryptionPaddings(), is(new String[]{KeyProperties.ENCRYPTION_PADDING_RSA_OAEP})); + assertThat(spec.getDigests(), is(new String[]{KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256})); + assertThat(spec.getBlockModes(), is(new String[]{KeyProperties.BLOCK_MODE_ECB})); + + assertThat(spec.getCertificateSubject(), is(notNullValue())); + assertThat(spec.getCertificateSubject().getName(), is(CERTIFICATE_PRINCIPAL)); - assertThat(startDateCaptor.getValue(), is(notNullValue())); - long diffMillis = startDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getCertificateNotBefore(), is(notNullValue())); + long diffMillis = spec.getCertificateNotBefore().getTime() - new Date().getTime(); long days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(0L)); //Date is Today - assertThat(endDateCaptor.getValue(), is(notNullValue())); - diffMillis = endDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getCertificateNotAfter(), is(notNullValue())); + diffMillis = spec.getCertificateNotAfter().getTime() - new Date().getTime(); days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(greaterThan(25 * 365L))); //Date more than 25 Years in days @@ -344,44 +345,37 @@ public void shouldCreateRSAKeyPairIfMissingOnAPI23AndUp() throws Exception { @Test @Config(sdk = 28) public void shouldCreateRSAKeyPairIfMissingOnAPI28AndUp() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 28); - - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - KeyStore.PrivateKeyEntry expectedEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); - KeyGenParameterSpec spec = PowerMockito.mock(KeyGenParameterSpec.class); - KeyGenParameterSpec.Builder builder = newKeyGenParameterSpecBuilder(spec); - PowerMockito.whenNew(KeyGenParameterSpec.Builder.class).withArguments(KEY_ALIAS, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT).thenReturn(builder); - - ArgumentCaptor principalCaptor = ArgumentCaptor.forClass(X500Principal.class); - ArgumentCaptor startDateCaptor = ArgumentCaptor.forClass(Date.class); - ArgumentCaptor endDateCaptor = ArgumentCaptor.forClass(Date.class); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); + KeyStore.PrivateKeyEntry expectedEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); + ArgumentCaptor specCaptor = ArgumentCaptor.forClass(AlgorithmParameterSpec.class); final KeyStore.PrivateKeyEntry entry = cryptoUtil.getRSAKeyEntry(); - Mockito.verify(builder).setKeySize(2048); - Mockito.verify(builder).setCertificateSubject(principalCaptor.capture()); - Mockito.verify(builder).setCertificateSerialNumber(BigInteger.ONE); - Mockito.verify(builder).setCertificateNotBefore(startDateCaptor.capture()); - Mockito.verify(builder).setCertificateNotAfter(endDateCaptor.capture()); - Mockito.verify(builder).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); - Mockito.verify(builder).setDigests(KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256); - Mockito.verify(builder).setBlockModes(KeyProperties.BLOCK_MODE_ECB); - Mockito.verify(keyPairGenerator).initialize(spec); + Mockito.verify(keyPairGenerator).initialize(specCaptor.capture()); Mockito.verify(keyPairGenerator).generateKeyPair(); - assertThat(principalCaptor.getValue(), is(notNullValue())); - assertThat(principalCaptor.getValue().getName(), is(CERTIFICATE_PRINCIPAL)); + // Verify the spec properties directly + KeyGenParameterSpec spec = (KeyGenParameterSpec) specCaptor.getValue(); + assertThat(spec.getKeySize(), is(2048)); + assertThat(spec.getKeystoreAlias(), is(KEY_ALIAS)); + assertThat(spec.getCertificateSerialNumber(), is(BigInteger.ONE)); + assertThat(spec.getEncryptionPaddings(), is(new String[]{KeyProperties.ENCRYPTION_PADDING_RSA_OAEP})); + assertThat(spec.getDigests(), is(new String[]{KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256})); + assertThat(spec.getBlockModes(), is(new String[]{KeyProperties.BLOCK_MODE_ECB})); + + assertThat(spec.getCertificateSubject(), is(notNullValue())); + assertThat(spec.getCertificateSubject().getName(), is(CERTIFICATE_PRINCIPAL)); - assertThat(startDateCaptor.getValue(), is(notNullValue())); - long diffMillis = startDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getCertificateNotBefore(), is(notNullValue())); + long diffMillis = spec.getCertificateNotBefore().getTime() - new Date().getTime(); long days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(0L)); //Date is Today - assertThat(endDateCaptor.getValue(), is(notNullValue())); - diffMillis = endDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getCertificateNotAfter(), is(notNullValue())); + diffMillis = spec.getCertificateNotAfter().getTime() - new Date().getTime(); days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(greaterThan(25 * 365L))); //Date more than 25 Years in days @@ -391,55 +385,42 @@ public void shouldCreateRSAKeyPairIfMissingOnAPI28AndUp() throws Exception { @Test @Config(sdk = 28) public void shouldCreateNewRSAKeyPairWhenExistingRSAKeyPairCannotBeRebuiltOnAPI28AndUp() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 28); - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); - //This is required to trigger the fallback when alias is present but key is not - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); - PowerMockito.when(keyStore.getKey(KEY_ALIAS, null)).thenReturn(privateKey).thenReturn(null); - PowerMockito.when(keyStore.getCertificate(KEY_ALIAS)).thenReturn(null); - //This is required to trigger finding the key after generating it - KeyStore.PrivateKeyEntry expectedEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); - - //Tests no instantiation of PrivateKeyEntry - PowerMockito.verifyZeroInteractions(KeyStore.PrivateKeyEntry.class); - - //Creation assertion - KeyGenParameterSpec spec = PowerMockito.mock(KeyGenParameterSpec.class); - KeyGenParameterSpec.Builder builder = newKeyGenParameterSpecBuilder(spec); - PowerMockito.whenNew(KeyGenParameterSpec.Builder.class).withArguments(KEY_ALIAS, KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_ENCRYPT).thenReturn(builder); - - ArgumentCaptor principalCaptor = ArgumentCaptor.forClass(X500Principal.class); - ArgumentCaptor startDateCaptor = ArgumentCaptor.forClass(Date.class); - ArgumentCaptor endDateCaptor = ArgumentCaptor.forClass(Date.class); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); + Mockito.when(keyStore.getKey(KEY_ALIAS, null)).thenReturn(privateKey).thenReturn(null); + Mockito.when(keyStore.getCertificate(KEY_ALIAS)).thenReturn(null); + KeyStore.PrivateKeyEntry expectedEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(expectedEntry); + ArgumentCaptor specCaptor = ArgumentCaptor.forClass(AlgorithmParameterSpec.class); final KeyStore.PrivateKeyEntry entry = cryptoUtil.getRSAKeyEntry(); - Mockito.verify(builder).setKeySize(2048); - Mockito.verify(builder).setCertificateSubject(principalCaptor.capture()); - Mockito.verify(builder).setCertificateSerialNumber(BigInteger.ONE); - Mockito.verify(builder).setCertificateNotBefore(startDateCaptor.capture()); - Mockito.verify(builder).setCertificateNotAfter(endDateCaptor.capture()); - Mockito.verify(builder).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP); - Mockito.verify(builder).setDigests(KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256); - Mockito.verify(builder).setBlockModes(KeyProperties.BLOCK_MODE_ECB); - Mockito.verify(keyPairGenerator).initialize(spec); + Mockito.verify(keyPairGenerator).initialize(specCaptor.capture()); Mockito.verify(keyPairGenerator).generateKeyPair(); - assertThat(principalCaptor.getValue(), is(notNullValue())); - assertThat(principalCaptor.getValue().getName(), is(CERTIFICATE_PRINCIPAL)); + // Verify the spec properties directly + KeyGenParameterSpec spec = (KeyGenParameterSpec) specCaptor.getValue(); + assertThat(spec.getKeySize(), is(2048)); + assertThat(spec.getKeystoreAlias(), is(KEY_ALIAS)); + assertThat(spec.getCertificateSerialNumber(), is(BigInteger.ONE)); + assertThat(spec.getEncryptionPaddings(), is(new String[]{KeyProperties.ENCRYPTION_PADDING_RSA_OAEP})); + assertThat(spec.getDigests(), is(new String[]{KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA256})); + assertThat(spec.getBlockModes(), is(new String[]{KeyProperties.BLOCK_MODE_ECB})); + + assertThat(spec.getCertificateSubject(), is(notNullValue())); + assertThat(spec.getCertificateSubject().getName(), is(CERTIFICATE_PRINCIPAL)); - assertThat(startDateCaptor.getValue(), is(notNullValue())); - long diffMillis = startDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getCertificateNotBefore(), is(notNullValue())); + long diffMillis = spec.getCertificateNotBefore().getTime() - new Date().getTime(); long days = TimeUnit.MILLISECONDS.toDays(diffMillis); assertThat(days, is(0L)); //Date is Today - assertThat(endDateCaptor.getValue(), is(notNullValue())); - diffMillis = endDateCaptor.getValue().getTime() - new Date().getTime(); + assertThat(spec.getCertificateNotAfter(), is(notNullValue())); + diffMillis = spec.getCertificateNotAfter().getTime() - new Date().getTime(); days = TimeUnit.MILLISECONDS.toDays(diffMillis); - assertThat(days, is(greaterThan(25 * 365L))); //Date more than 25 Years in days + assertThat(days, is(greaterThan(25 * 365L))); assertThat(entry, is(expectedEntry)); } @@ -447,68 +428,57 @@ public void shouldCreateNewRSAKeyPairWhenExistingRSAKeyPairCannotBeRebuiltOnAPI2 @Test @Config(sdk = 28) public void shouldUseExistingRSAKeyPairRebuildingTheEntryOnAPI28AndUp() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 28); - KeyStore.PrivateKeyEntry entry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - Certificate certificate = PowerMockito.mock(Certificate.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + Certificate certificate = Mockito.mock(Certificate.class); - ArgumentCaptor varargsCaptor = ArgumentCaptor.forClass(Object.class); - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); - PowerMockito.when(keyStore.getKey(KEY_ALIAS, null)).thenReturn(privateKey); - PowerMockito.when(keyStore.getCertificate(KEY_ALIAS)).thenReturn(certificate); - PowerMockito.whenNew(KeyStore.PrivateKeyEntry.class).withAnyArguments().thenReturn(entry); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); + Mockito.when(keyStore.getKey(KEY_ALIAS, null)).thenReturn(privateKey); + Mockito.when(keyStore.getCertificate(KEY_ALIAS)).thenReturn(certificate); - KeyStore.PrivateKeyEntry rsaEntry = cryptoUtil.getRSAKeyEntry(); - PowerMockito.verifyNew(KeyStore.PrivateKeyEntry.class).withArguments(varargsCaptor.capture()); - assertThat(rsaEntry, is(notNullValue())); - assertThat(rsaEntry, is(entry)); - assertThat(varargsCaptor.getAllValues(), is(notNullValue())); - PrivateKey capturedPrivateKey = (PrivateKey) varargsCaptor.getAllValues().get(0); - Certificate[] capturedCertificatesArray = (Certificate[]) varargsCaptor.getAllValues().get(1); - assertThat(capturedPrivateKey, is(privateKey)); - assertThat(capturedCertificatesArray[0], is(certificate)); - assertThat(capturedCertificatesArray.length, is(1)); + // Use mockConstruction to intercept PrivateKeyEntry constructor + try (MockedConstruction mockedConstruction = Mockito.mockConstruction( + KeyStore.PrivateKeyEntry.class, + (mock, context) -> { + // Capture constructor arguments + })) { + + KeyStore.PrivateKeyEntry rsaEntry = cryptoUtil.getRSAKeyEntry(); + + assertThat(rsaEntry, is(notNullValue())); + assertThat(mockedConstruction.constructed().size(), is(1)); + assertThat(rsaEntry, is(mockedConstruction.constructed().get(0))); + } } @Test @Config(sdk = 28) public void shouldUseExistingPrivateKeyForOldKeyAlias() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 28); - KeyStore.PrivateKeyEntry entry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - Certificate certificate = PowerMockito.mock(Certificate.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + Certificate certificate = Mockito.mock(Certificate.class); - KeyGenParameterSpec.Builder builder = PowerMockito.mock(KeyGenParameterSpec.Builder.class); - PowerMockito.when(builder.setKeySize(anyInt())).thenReturn(builder); - PowerMockito.when(builder.setCertificateSubject(any(X500Principal.class))).thenReturn(builder); + Mockito.when(keyStore.containsAlias(OLD_KEY_ALIAS)).thenReturn(true); + Mockito.when(keyStore.getKey(OLD_KEY_ALIAS, null)).thenReturn(privateKey); + Mockito.when(keyStore.getCertificate(OLD_KEY_ALIAS)).thenReturn(certificate); - ArgumentCaptor varargsCaptor = ArgumentCaptor.forClass(Object.class); - PowerMockito.when(keyStore.containsAlias(OLD_KEY_ALIAS)).thenReturn(true); - PowerMockito.when(keyStore.getKey(OLD_KEY_ALIAS, null)).thenReturn(privateKey); - PowerMockito.when(keyStore.getCertificate(OLD_KEY_ALIAS)).thenReturn(certificate); - PowerMockito.whenNew(KeyStore.PrivateKeyEntry.class).withAnyArguments().thenReturn(entry); + try (MockedConstruction mockedConstruction = Mockito.mockConstruction( + KeyStore.PrivateKeyEntry.class, + (mock, context) -> { + })) { - KeyStore.PrivateKeyEntry rsaEntry = cryptoUtil.getRSAKeyEntry(); - PowerMockito.verifyNew(KeyStore.PrivateKeyEntry.class).withArguments(varargsCaptor.capture()); - assertThat(rsaEntry, is(notNullValue())); - assertThat(rsaEntry, is(entry)); - assertThat(varargsCaptor.getAllValues(), is(notNullValue())); - PrivateKey capturedPrivateKey = (PrivateKey) varargsCaptor.getAllValues().get(0); - Certificate[] capturedCertificatesArray = (Certificate[]) varargsCaptor.getAllValues().get(1); - assertThat(capturedPrivateKey, is(privateKey)); - assertThat(capturedCertificatesArray[0], is(certificate)); - assertThat(capturedCertificatesArray.length, is(1)); + KeyStore.PrivateKeyEntry rsaEntry = cryptoUtil.getRSAKeyEntry(); + + assertThat(rsaEntry, is(notNullValue())); + assertThat(mockedConstruction.constructed().size(), is(1)); + assertThat(rsaEntry, is(mockedConstruction.constructed().get(0))); + } } @Test @Config(sdk = 28) public void shouldUseExistingRSAKeyPairOnAPI28AndUp() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 28); - KeyStore.PrivateKeyEntry entry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(entry); - PrivateKey privateKey = null; - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); - PowerMockito.when(keyStore.getKey(KEY_ALIAS, null)).thenReturn(privateKey); + KeyStore.PrivateKeyEntry entry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(entry); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); KeyStore.PrivateKeyEntry rsaEntry = cryptoUtil.getRSAKeyEntry(); assertThat(rsaEntry, is(notNullValue())); @@ -518,10 +488,9 @@ public void shouldUseExistingRSAKeyPairOnAPI28AndUp() throws Exception { @Test @Config(sdk = 27) public void shouldUseExistingRSAKeyPairOnAPI27AndDown() throws Exception { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 27); - KeyStore.PrivateKeyEntry entry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(entry); + KeyStore.PrivateKeyEntry entry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)).thenReturn(entry); KeyStore.PrivateKeyEntry rsaEntry = cryptoUtil.getRSAKeyEntry(); assertThat(rsaEntry, is(notNullValue())); @@ -532,9 +501,9 @@ public void shouldUseExistingRSAKeyPairOnAPI27AndDown() throws Exception { public void shouldDeleteRSAAndAESKeysAndThrowOnUnrecoverableEntryExceptionWhenTryingToObtainRSAKeys() throws Exception { Assert.assertThrows("The existing RSA key pair could not be recovered and has been deleted. " + "This occasionally happens when the Lock Screen settings are changed. You can safely retry this operation.", CryptoException.class, () -> { - KeyStore.PrivateKeyEntry entry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); - PowerMockito.when(keyStore.getEntry(KEY_ALIAS, null)) + KeyStore.PrivateKeyEntry entry = Mockito.mock(KeyStore.PrivateKeyEntry.class); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); + Mockito.when(keyStore.getEntry(KEY_ALIAS, null)) .thenThrow(new UnrecoverableEntryException()) .thenReturn(entry); @@ -591,8 +560,7 @@ public void shouldDeleteAESKeysAndThrowOnDoubleIOExceptionWhenTryingToObtainRSAK @Test public void shouldThrowOnKeyStoreExceptionWhenTryingToObtainRSAKeys() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PowerMockito.mockStatic(KeyStore.class); - PowerMockito.when(KeyStore.getInstance(anyString())) + Mockito.when(KeyStore.getInstance(anyString())) .thenThrow(new KeyStoreException()); cryptoUtil.getRSAKeyEntry(); @@ -619,15 +587,10 @@ public void shouldThrowOnProviderExceptionWhenTryingToObtainRSAKeys() { @Test public void shouldThrowOnNoSuchProviderExceptionWhenTryingToObtainRSAKeys() { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 19); Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - KeyPairGeneratorSpec spec = PowerMockito.mock(KeyPairGeneratorSpec.class); - KeyPairGeneratorSpec.Builder builder = newKeyPairGeneratorSpecBuilder(spec); - PowerMockito.whenNew(KeyPairGeneratorSpec.Builder.class).withAnyArguments().thenReturn(builder); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - PowerMockito.mockStatic(KeyPairGenerator.class); - PowerMockito.when(KeyPairGenerator.getInstance(ALGORITHM_RSA, ANDROID_KEY_STORE)) + keyPairGeneratorMock.when(() -> KeyPairGenerator.getInstance(ALGORITHM_RSA, ANDROID_KEY_STORE)) .thenThrow(new NoSuchProviderException()); cryptoUtil.getRSAKeyEntry(); @@ -636,15 +599,10 @@ public void shouldThrowOnNoSuchProviderExceptionWhenTryingToObtainRSAKeys() { @Test public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToObtainRSAKeys() { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 19); Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - KeyPairGeneratorSpec spec = PowerMockito.mock(KeyPairGeneratorSpec.class); - KeyPairGeneratorSpec.Builder builder = newKeyPairGeneratorSpecBuilder(spec); - PowerMockito.whenNew(KeyPairGeneratorSpec.Builder.class).withAnyArguments().thenReturn(builder); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - PowerMockito.mockStatic(KeyPairGenerator.class); - PowerMockito.when(KeyPairGenerator.getInstance(ALGORITHM_RSA, ANDROID_KEY_STORE)) + keyPairGeneratorMock.when(() -> KeyPairGenerator.getInstance(ALGORITHM_RSA, ANDROID_KEY_STORE)) .thenThrow(new NoSuchAlgorithmException()); cryptoUtil.getRSAKeyEntry(); @@ -653,12 +611,8 @@ public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToObtainRSAKeys() { @Test public void shouldThrowOnInvalidAlgorithmParameterExceptionWhenTryingToObtainRSAKeys() { - ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", 19); Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PowerMockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); - KeyPairGeneratorSpec spec = PowerMockito.mock(KeyPairGeneratorSpec.class); - KeyPairGeneratorSpec.Builder builder = newKeyPairGeneratorSpecBuilder(spec); - PowerMockito.whenNew(KeyPairGeneratorSpec.Builder.class).withAnyArguments().thenReturn(builder); + Mockito.when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(false); doThrow(new InvalidAlgorithmParameterException()).when(keyPairGenerator).initialize(any(AlgorithmParameterSpec.class)); @@ -673,17 +627,15 @@ public void shouldThrowOnInvalidAlgorithmParameterExceptionWhenTryingToObtainRSA @Test public void shouldCreateAESKeyIfMissing() throws Exception { byte[] sampleBytes = new byte[]{0, 1, 2, 3, 4, 5}; - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.encode(sampleBytes, Base64.DEFAULT)).thenReturn("data".getBytes()); - PowerMockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn(null); - PowerMockito.when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(null); - PowerMockito.mockStatic(TextUtils.class); - PowerMockito.when(TextUtils.isEmpty(null)).thenReturn(true); - - SecretKey secretKey = PowerMockito.mock(SecretKey.class); - PowerMockito.when(keyGenerator.generateKey()).thenReturn(secretKey); - PowerMockito.when(secretKey.getEncoded()).thenReturn(sampleBytes); - PowerMockito.doReturn(sampleBytes).when(cryptoUtil, "RSAEncrypt", sampleBytes); + base64Mock.when(() -> Base64.encode(sampleBytes, Base64.DEFAULT)).thenReturn("data".getBytes()); + Mockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn(null); + Mockito.when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(null); + textUtilsMock.when(() -> TextUtils.isEmpty(null)).thenReturn(true); + + SecretKey secretKey = Mockito.mock(SecretKey.class); + Mockito.when(keyGenerator.generateKey()).thenReturn(secretKey); + Mockito.when(secretKey.getEncoded()).thenReturn(sampleBytes); + Mockito.doReturn(sampleBytes).when(cryptoUtil).RSAEncrypt(sampleBytes); final byte[] aesKey = cryptoUtil.getAESKey(); @@ -700,22 +652,21 @@ public void shouldCreateAESKeyIfStoredOneIsEmpty() throws BadPaddingException, I String emptyString = ""; byte[] sampleBytes = emptyString.getBytes(); byte[] sampleOutput = new byte[]{99, 33, 11}; - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode(emptyString, Base64.DEFAULT)).thenReturn(sampleBytes); - PowerMockito.when(Base64.encode(sampleBytes, Base64.DEFAULT)).thenReturn("data".getBytes()); - PowerMockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn(emptyString); + base64Mock.when(() -> Base64.decode(emptyString, Base64.DEFAULT)).thenReturn(sampleBytes); + base64Mock.when(() -> Base64.encode(sampleBytes, Base64.DEFAULT)).thenReturn("data".getBytes()); + Mockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn(emptyString); doReturn(sampleBytes).when(cryptoUtil).RSAEncrypt(sampleBytes); //Assume RSAKeyEntry exists - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(privateKey).when(privateKeyEntry).getPrivateKey(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); doReturn(sampleOutput).when(rsaOaepCipher).doFinal(sampleBytes); - SecretKey secretKey = PowerMockito.mock(SecretKey.class); - PowerMockito.when(secretKey.getEncoded()).thenReturn(sampleBytes); - PowerMockito.when(keyGenerator.generateKey()).thenReturn(secretKey); + SecretKey secretKey = Mockito.mock(SecretKey.class); + Mockito.when(secretKey.getEncoded()).thenReturn(sampleBytes); + Mockito.when(keyGenerator.generateKey()).thenReturn(secretKey); final byte[] aesKey = cryptoUtil.getAESKey(); @@ -735,9 +686,8 @@ public void shouldUseExistingAESKey() { Arrays.fill(sampleBytes, (byte) 1); String aesString = "non null string"; - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode(aesString, Base64.DEFAULT)).thenReturn(sampleBytes); - PowerMockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn(aesString); + base64Mock.when(() -> Base64.decode(aesString, Base64.DEFAULT)).thenReturn(sampleBytes); + Mockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn(aesString); doReturn(sampleBytes).when(cryptoUtil).RSADecrypt(sampleBytes); final byte[] aesKey = cryptoUtil.getAESKey(); @@ -748,12 +698,10 @@ public void shouldUseExistingAESKey() { @Test public void shouldThrowOnNoSuchAlgorithmExceptionWhenCreatingAESKey() throws Exception { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PowerMockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn(null); - PowerMockito.when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(null); - PowerMockito.mockStatic(TextUtils.class); - PowerMockito.when(TextUtils.isEmpty(null)).thenReturn(true); - PowerMockito.mockStatic(KeyGenerator.class); - PowerMockito.when(KeyGenerator.getInstance(ALGORITHM_AES)) + Mockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn(null); + Mockito.when(storage.retrieveString(OLD_KEY_ALIAS)).thenReturn(null); + textUtilsMock.when(() -> TextUtils.isEmpty(null)).thenReturn(true); + Mockito.when(KeyGenerator.getInstance(ALGORITHM_AES)) .thenThrow(new NoSuchAlgorithmException()); cryptoUtil.getAESKey(); @@ -769,10 +717,10 @@ public void shouldRSAEncryptData() throws Exception { byte[] sampleInput = new byte[]{0, 1, 2, 3, 4, 5}; byte[] sampleOutput = new byte[]{99, 33, 11}; - PublicKey publicKey = PowerMockito.mock(PublicKey.class); - Certificate certificate = PowerMockito.mock(Certificate.class); + PublicKey publicKey = Mockito.mock(PublicKey.class); + Certificate certificate = Mockito.mock(Certificate.class); doReturn(publicKey).when(certificate).getPublicKey(); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(certificate).when(privateKeyEntry).getCertificate(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); doReturn(sampleOutput).when(rsaOaepCipher).doFinal(sampleInput); @@ -787,14 +735,13 @@ public void shouldRSAEncryptData() throws Exception { public void shouldThrowOnInvalidKeyExceptionWhenTryingToRSAEncrypt() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { byte[] sampleBytes = new byte[0]; - PublicKey publicKey = PowerMockito.mock(PublicKey.class); - Certificate certificate = PowerMockito.mock(Certificate.class); + PublicKey publicKey = Mockito.mock(PublicKey.class); + Certificate certificate = Mockito.mock(Certificate.class); doReturn(publicKey).when(certificate).getPublicKey(); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(certificate).when(privateKeyEntry).getCertificate(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); + Mockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); doThrow(new InvalidKeyException()).when(rsaOaepCipher).init(eq(Cipher.ENCRYPT_MODE), eq(publicKey), any(AlgorithmParameterSpec.class)); cryptoUtil.RSAEncrypt(sampleBytes); @@ -806,13 +753,12 @@ public void shouldDeleteAESKeysAndThrowOnBadPaddingExceptionWhenTryingToRSAEncry Assert.assertThrows("The RSA decrypted input is invalid.", CryptoException.class, () -> { byte[] sampleBytes = new byte[0]; - Certificate certificate = PowerMockito.mock(Certificate.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + Certificate certificate = Mockito.mock(Certificate.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(certificate).when(privateKeyEntry).getCertificate(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); - PowerMockito.when(rsaOaepCipher.doFinal(sampleBytes)).thenThrow(new BadPaddingException()); + Mockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); + Mockito.when(rsaOaepCipher.doFinal(sampleBytes)).thenThrow(new BadPaddingException()); cryptoUtil.RSAEncrypt(sampleBytes); }); @@ -828,13 +774,12 @@ public void shouldDeleteAESKeysAndThrowOnBadPaddingExceptionWhenTryingToRSAEncry @Test public void shouldDeleteAESKeysAndThrowOnIllegalBlockSizeExceptionWhenTryingToRSAEncrypt() throws Exception { Assert.assertThrows("The RSA decrypted input is invalid.", CryptoException.class, () -> { - Certificate certificate = PowerMockito.mock(Certificate.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + Certificate certificate = Mockito.mock(Certificate.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(certificate).when(privateKeyEntry).getCertificate(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); - PowerMockito.when(rsaOaepCipher.doFinal(any(byte[].class))).thenThrow(new IllegalBlockSizeException()); + Mockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); + Mockito.when(rsaOaepCipher.doFinal(any(byte[].class))).thenThrow(new IllegalBlockSizeException()); cryptoUtil.RSAEncrypt(new byte[0]); }); @@ -850,12 +795,11 @@ public void shouldDeleteAESKeysAndThrowOnIllegalBlockSizeExceptionWhenTryingToRS @Test public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToRSAEncrypt() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - Certificate certificate = PowerMockito.mock(Certificate.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + Certificate certificate = Mockito.mock(Certificate.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(certificate).when(privateKeyEntry).getCertificate(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenThrow(new NoSuchAlgorithmException()); + Mockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenThrow(new NoSuchAlgorithmException()); cryptoUtil.RSAEncrypt(new byte[0]); }); @@ -864,12 +808,11 @@ public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToRSAEncrypt() { @Test public void shouldThrowOnNoSuchPaddingExceptionWhenTryingToRSAEncrypt() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - Certificate certificate = PowerMockito.mock(Certificate.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + Certificate certificate = Mockito.mock(Certificate.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(certificate).when(privateKeyEntry).getCertificate(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenThrow(new NoSuchPaddingException()); + Mockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenThrow(new NoSuchPaddingException()); cryptoUtil.RSAEncrypt(new byte[0]); }); @@ -884,8 +827,8 @@ public void shouldRSADecryptData() throws Exception { byte[] sampleInput = new byte[]{0, 1, 2, 3, 4, 5}; byte[] sampleOutput = new byte[]{99, 33, 11}; - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(privateKey).when(privateKeyEntry).getPrivateKey(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); doReturn(sampleOutput).when(rsaOaepCipher).doFinal(sampleInput); @@ -900,12 +843,11 @@ public void shouldRSADecryptData() throws Exception { public void shouldThrowOnInvalidKeyExceptionWhenTryingToRSADecrypt() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { byte[] sampleBytes = new byte[0]; - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(privateKey).when(privateKeyEntry).getPrivateKey(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); + Mockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenReturn(rsaOaepCipher); doThrow(new InvalidKeyException()).when(rsaOaepCipher).init(eq(Cipher.DECRYPT_MODE), eq(privateKey), any(AlgorithmParameterSpec.class)); cryptoUtil.RSADecrypt(sampleBytes); @@ -915,12 +857,11 @@ public void shouldThrowOnInvalidKeyExceptionWhenTryingToRSADecrypt() { @Test public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToRSADecrypt() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(privateKey).when(privateKeyEntry).getPrivateKey(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenThrow(new NoSuchAlgorithmException()); + Mockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenThrow(new NoSuchAlgorithmException()); cryptoUtil.RSADecrypt(new byte[0]); }); @@ -929,12 +870,11 @@ public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToRSADecrypt() { @Test public void shouldThrowOnNoSuchPaddingExceptionWhenTryingToRSADecrypt() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(privateKey).when(privateKeyEntry).getPrivateKey(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenThrow(new NoSuchPaddingException()); + Mockito.when(Cipher.getInstance(RSA_TRANSFORMATION)).thenThrow(new NoSuchPaddingException()); cryptoUtil.RSADecrypt(new byte[0]); }); @@ -943,8 +883,8 @@ public void shouldThrowOnNoSuchPaddingExceptionWhenTryingToRSADecrypt() { @Test public void shouldDeleteAESKeysAndThrowOnBadPaddingExceptionWhenTryingToRSADecrypt() throws Exception { Assert.assertThrows("The RSA encrypted input is corrupted and cannot be recovered. Please discard it.", CryptoException.class, () -> { - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(privateKey).when(privateKeyEntry).getPrivateKey(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); @@ -963,8 +903,8 @@ public void shouldDeleteAESKeysAndThrowOnBadPaddingExceptionWhenTryingToRSADecry @Test public void shouldDeleteAESKeysAndThrowOnIllegalBlockSizeExceptionWhenTryingToRSADecrypt() throws Exception { Assert.assertThrows("The RSA encrypted input is corrupted and cannot be recovered. Please discard it.", CryptoException.class, () -> { - PrivateKey privateKey = PowerMockito.mock(PrivateKey.class); - KeyStore.PrivateKeyEntry privateKeyEntry = PowerMockito.mock(KeyStore.PrivateKeyEntry.class); + PrivateKey privateKey = Mockito.mock(PrivateKey.class); + KeyStore.PrivateKeyEntry privateKeyEntry = Mockito.mock(KeyStore.PrivateKeyEntry.class); doReturn(privateKey).when(privateKeyEntry).getPrivateKey(); doReturn(privateKeyEntry).when(cryptoUtil).getRSAKeyEntry(); @@ -994,8 +934,8 @@ public void shouldAESEncryptData() throws Exception { doReturn(aesKey).when(cryptoUtil).getAESKey(); doReturn(encryptedData).when(aesCipher).doFinal(data); - PowerMockito.when(aesCipher.doFinal(data)).thenReturn(encryptedData); - PowerMockito.when(aesCipher.getIV()).thenReturn(iv); + Mockito.when(aesCipher.doFinal(data)).thenReturn(encryptedData); + Mockito.when(aesCipher.getIV()).thenReturn(iv); final byte[] encrypted = cryptoUtil.encrypt(data); @@ -1006,17 +946,17 @@ public void shouldAESEncryptData() throws Exception { // IV is NO LONGER stored in storage - it's bundled with the encrypted data Mockito.verify(storage, never()).store(eq(KEY_ALIAS + "_iv"), anyString()); - + assertThat(encrypted, is(notNullValue())); assertThat(encrypted.length, is(1 + 1 + iv.length + encryptedData.length)); assertThat(encrypted[0], is((byte) 0x01)); assertThat(encrypted[1], is((byte) iv.length)); - + // Verify IV is correctly embedded byte[] extractedIV = new byte[iv.length]; System.arraycopy(encrypted, 2, extractedIV, 0, iv.length); assertThat(extractedIV, is(iv)); - + // Verify encrypted data is correctly embedded byte[] extractedEncrypted = new byte[encryptedData.length]; System.arraycopy(encrypted, 2 + iv.length, extractedEncrypted, 0, encryptedData.length); @@ -1026,9 +966,8 @@ public void shouldAESEncryptData() throws Exception { @Test public void shouldThrowOnCryptoExceptionOnRSAKeyReadingWhenTryingToAESEncrypt() { Assert.assertThrows(CryptoException.class, () -> { - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("encoded-key", Base64.DEFAULT)).thenReturn(new byte[0]); - PowerMockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn("encoded-key"); + base64Mock.when(() -> Base64.decode("encoded-key", Base64.DEFAULT)).thenReturn(new byte[0]); + Mockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn("encoded-key"); doThrow(new CryptoException("err", null)).when(cryptoUtil).getRSAKeyEntry(); cryptoUtil.encrypt(new byte[0]); @@ -1046,9 +985,8 @@ public void shouldThrowOnCryptoExceptionOnAESKeyReadingWhenTryingToAESEncrypt() @Test public void shouldThrowOnIncompatibleDeviceExceptionOnRSAKeyReadingWhenTryingToAESEncrypt() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("encoded-key", Base64.DEFAULT)).thenReturn(new byte[0]); - PowerMockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn("encoded-key"); + base64Mock.when(() -> Base64.decode("encoded-key", Base64.DEFAULT)).thenReturn(new byte[0]); + Mockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn("encoded-key"); doThrow(new IncompatibleDeviceException(null)).when(cryptoUtil).getRSAKeyEntry(); cryptoUtil.encrypt(new byte[0]); @@ -1069,8 +1007,7 @@ public void shouldThrowOnNoSuchPaddingExceptionWhenTryingToAESEncrypt() { Assert.assertThrows(IncompatibleDeviceException.class, () -> { doReturn(new byte[]{11, 22, 33}).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenThrow(new NoSuchPaddingException()); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenThrow(new NoSuchPaddingException()); cryptoUtil.encrypt(new byte[0]); }); @@ -1081,8 +1018,7 @@ public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToAESEncrypt() throws Assert.assertThrows(IncompatibleDeviceException.class, () -> { doReturn(new byte[]{11, 22, 33}).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenThrow(new NoSuchAlgorithmException()); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenThrow(new NoSuchAlgorithmException()); cryptoUtil.encrypt(new byte[0]); }); @@ -1096,8 +1032,7 @@ public void shouldThrowOnInvalidKeyExceptionWhenTryingToAESEncrypt() throws Exce try { doReturn(aesKeyBytes).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); doThrow(new InvalidKeyException()).when(aesCipher).init(eq(Cipher.ENCRYPT_MODE), secretKeyArgumentCaptor.capture()); cryptoUtil.encrypt(new byte[0]); @@ -1160,9 +1095,9 @@ public void shouldDetectNewFormatWithValidMarkerAndIVLength12() { for (int i = 2; i < newFormatData.length; i++) { newFormatData[i] = (byte) i; } - + boolean result = cryptoUtil.isNewFormat(newFormatData); - + assertThat(result, is(true)); } @@ -1177,9 +1112,9 @@ public void shouldDetectNewFormatWithValidMarkerAndIVLength16() { for (int i = 2; i < newFormatData.length; i++) { newFormatData[i] = (byte) i; } - + boolean result = cryptoUtil.isNewFormat(newFormatData); - + assertThat(result, is(true)); } @@ -1189,9 +1124,9 @@ public void shouldNotDetectNewFormatWithInvalidMarker() { byte[] invalidData = new byte[30]; invalidData[0] = 0x02; // Wrong marker invalidData[1] = 12; // Valid IV length - + boolean result = cryptoUtil.isNewFormat(invalidData); - + assertThat(result, is(false)); } @@ -1201,9 +1136,9 @@ public void shouldNotDetectNewFormatWithInvalidIVLength() { byte[] invalidData = new byte[30]; invalidData[0] = 0x01; // Valid marker invalidData[1] = 10; // Invalid IV length (not 12 or 16) - + boolean result = cryptoUtil.isNewFormat(invalidData); - + assertThat(result, is(false)); } @@ -1211,24 +1146,24 @@ public void shouldNotDetectNewFormatWithInvalidIVLength() { public void shouldExtractIVFromNewFormatCorrectly() { byte[] iv = new byte[]{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120}; byte[] encryptedPayload = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; // At least 17 bytes (16 tag + 1 data) - + byte[] newFormatData = new byte[1 + 1 + iv.length + encryptedPayload.length]; newFormatData[0] = 0x01; newFormatData[1] = (byte) iv.length; System.arraycopy(iv, 0, newFormatData, 2, iv.length); System.arraycopy(encryptedPayload, 0, newFormatData, 2 + iv.length, encryptedPayload.length); - + // Verify format detection assertThat(cryptoUtil.isNewFormat(newFormatData), is(true)); - + // Manually extract and verify IV int ivLength = newFormatData[1] & 0xFF; assertThat(ivLength, is(12)); - + byte[] extractedIV = new byte[ivLength]; System.arraycopy(newFormatData, 2, extractedIV, 0, ivLength); assertThat(extractedIV, is(iv)); - + // Verify encrypted payload position int dataOffset = 2 + ivLength; int dataLength = newFormatData.length - dataOffset; @@ -1288,7 +1223,7 @@ public void shouldRejectInvalidIVLengthsInNewFormat() { byte[] ivLength255 = new byte[274]; ivLength255[0] = 0x01; - ivLength255[1] = (byte) 255; + ivLength255[1] = (byte) 255; assertThat(cryptoUtil.isNewFormat(ivLength255), is(false)); } @@ -1305,12 +1240,10 @@ public void shouldDecryptLegacyFormatDataWithIVInStorage() throws Exception { // Setup: Old format has IV stored separately in storage doReturn(aesKey).when(cryptoUtil).getAESKey(); - PowerMockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("encoded-iv-data"); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("encoded-iv-data", Base64.DEFAULT)).thenReturn(iv); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.when(aesCipher.doFinal(encryptedData)).thenReturn(originalData); + Mockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("encoded-iv-data"); + base64Mock.when(() -> Base64.decode("encoded-iv-data", Base64.DEFAULT)).thenReturn(iv); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); + Mockito.when(aesCipher.doFinal(encryptedData)).thenReturn(originalData); // Execute: Decrypt old format data (should be detected as legacy format) final byte[] decrypted = cryptoUtil.decrypt(encryptedData); @@ -1337,22 +1270,20 @@ public void shouldMigrateFromLegacyFormatToNewFormat() throws Exception { byte[] newIv = new byte[]{11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22}; doReturn(aesKey).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.mockStatic(Base64.class); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); // Step 1: Decrypt old format (IV from storage) - PowerMockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("old-encoded-iv"); - PowerMockito.when(Base64.decode("old-encoded-iv", Base64.DEFAULT)).thenReturn(oldIv); - PowerMockito.when(aesCipher.doFinal(oldEncryptedData)).thenReturn(originalData); + Mockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("old-encoded-iv"); + base64Mock.when(() -> Base64.decode("old-encoded-iv", Base64.DEFAULT)).thenReturn(oldIv); + Mockito.when(aesCipher.doFinal(oldEncryptedData)).thenReturn(originalData); byte[] decryptedOld = cryptoUtil.decrypt(oldEncryptedData); assertThat(decryptedOld, is(originalData)); assertThat(cryptoUtil.isNewFormat(oldEncryptedData), is(false)); // Step 2: Re-encrypt in new format (IV bundled) - PowerMockito.when(aesCipher.doFinal(originalData)).thenReturn(newEncryptedData); - PowerMockito.when(aesCipher.getIV()).thenReturn(newIv); + Mockito.when(aesCipher.doFinal(originalData)).thenReturn(newEncryptedData); + Mockito.when(aesCipher.getIV()).thenReturn(newIv); byte[] reEncrypted = cryptoUtil.encrypt(originalData); @@ -1367,7 +1298,7 @@ public void shouldMigrateFromLegacyFormatToNewFormat() throws Exception { assertThat(extractedIV, is(newIv)); // Step 3: Decrypt new format (IV bundled in data) - PowerMockito.when(aesCipher.doFinal(any(byte[].class), anyInt(), anyInt())).thenReturn(originalData); + Mockito.when(aesCipher.doFinal(any(byte[].class), anyInt(), anyInt())).thenReturn(originalData); byte[] decryptedNew = cryptoUtil.decrypt(reEncrypted); assertThat(decryptedNew, is(originalData)); @@ -1399,20 +1330,18 @@ public void shouldDecryptBothLegacyAndNewFormatInSameSession() throws Exception System.arraycopy(newEncryptedPayload, 0, newEncrypted, 2 + newIv.length, newEncryptedPayload.length); doReturn(aesKey).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.mockStatic(Base64.class); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); // Decrypt old format first - PowerMockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("old-iv-encoded"); - PowerMockito.when(Base64.decode("old-iv-encoded", Base64.DEFAULT)).thenReturn(oldIv); - PowerMockito.when(aesCipher.doFinal(oldEncrypted)).thenReturn(dataA); + Mockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("old-iv-encoded"); + base64Mock.when(() -> Base64.decode("old-iv-encoded", Base64.DEFAULT)).thenReturn(oldIv); + Mockito.when(aesCipher.doFinal(oldEncrypted)).thenReturn(dataA); byte[] decryptedOld = cryptoUtil.decrypt(oldEncrypted); assertThat(decryptedOld, is(dataA)); // Decrypt new format next - PowerMockito.when(aesCipher.doFinal(any(byte[].class), anyInt(), anyInt())).thenReturn(dataB); + Mockito.when(aesCipher.doFinal(any(byte[].class), anyInt(), anyInt())).thenReturn(dataB); byte[] decryptedNew = cryptoUtil.decrypt(newEncrypted); assertThat(decryptedNew, is(dataB)); @@ -1446,14 +1375,13 @@ public void shouldAESDecryptData() throws Exception { System.arraycopy(encryptedPayload, 0, newFormatData, 2 + iv.length, encryptedPayload.length); doReturn(aesKey).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.when(aesCipher.doFinal(any(byte[].class), anyInt(), anyInt())).thenReturn(originalData); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); + Mockito.when(aesCipher.doFinal(any(byte[].class), anyInt(), anyInt())).thenReturn(originalData); final byte[] decrypted = cryptoUtil.decrypt(newFormatData); assertThat(cryptoUtil.isNewFormat(newFormatData), is(true)); - + Mockito.verify(aesCipher).init(eq(Cipher.DECRYPT_MODE), secretKeyCaptor.capture(), ivParameterSpecCaptor.capture()); assertThat(secretKeyCaptor.getValue(), is(notNullValue())); assertThat(secretKeyCaptor.getValue().getAlgorithm(), is(ALGORITHM_AES)); @@ -1466,9 +1394,8 @@ public void shouldAESDecryptData() throws Exception { @Test public void shouldThrowOnCryptoExceptionOnRSAKeyReadingWhenTryingToAESDecrypt() { Assert.assertThrows(CryptoException.class, () -> { - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("encoded-key", Base64.DEFAULT)).thenReturn(new byte[0]); - PowerMockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn("encoded-key"); + base64Mock.when(() -> Base64.decode("encoded-key", Base64.DEFAULT)).thenReturn(new byte[0]); + Mockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn("encoded-key"); doThrow(new CryptoException("err", null)).when(cryptoUtil).getRSAKeyEntry(); cryptoUtil.decrypt(new byte[0]); @@ -1486,9 +1413,8 @@ public void shouldThrowOnCryptoExceptionOnAESKeyReadingWhenTryingToAESDecrypt() @Test public void shouldThrowOnIncompatibleDeviceExceptionOnRSAKeyReadingWhenTryingToAESDecrypt() { Assert.assertThrows("The device is not compatible with the CryptoUtil class", IncompatibleDeviceException.class, () -> { - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("encoded-key", Base64.DEFAULT)).thenReturn(new byte[0]); - PowerMockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn("encoded-key"); + base64Mock.when(() -> Base64.decode("encoded-key", Base64.DEFAULT)).thenReturn(new byte[0]); + Mockito.when(storage.retrieveString(KEY_ALIAS)).thenReturn("encoded-key"); doThrow(new IncompatibleDeviceException(null)).when(cryptoUtil).getRSAKeyEntry(); cryptoUtil.decrypt(new byte[0]); @@ -1508,8 +1434,7 @@ public void shouldThrowOnNoSuchPaddingExceptionWhenTryingToAESDecrypt() { Assert.assertThrows(IncompatibleDeviceException.class, () -> { doReturn(new byte[]{11, 22, 33}).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenThrow(new NoSuchPaddingException()); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenThrow(new NoSuchPaddingException()); cryptoUtil.decrypt(new byte[0]); }); @@ -1520,8 +1445,7 @@ public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToAESDecrypt() { Assert.assertThrows(IncompatibleDeviceException.class, () -> { doReturn(new byte[]{11, 22, 33}).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenThrow(new NoSuchAlgorithmException()); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenThrow(new NoSuchAlgorithmException()); cryptoUtil.decrypt(new byte[0]); }); @@ -1531,12 +1455,11 @@ public void shouldThrowOnNoSuchAlgorithmExceptionWhenTryingToAESDecrypt() { public void shouldThrowOnEmptyInitializationVectorWhenTryingToAESDecryptWithOldFormat() { Assert.assertThrows("The encryption keys changed recently. You need to re-encrypt something first.", CryptoException.class, () -> { doReturn(new byte[]{11, 22, 33}).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn(""); - PowerMockito.when(storage.retrieveString(BASE_ALIAS + "_iv")).thenReturn(""); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); + Mockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn(""); + Mockito.when(storage.retrieveString(BASE_ALIAS + "_iv")).thenReturn(""); - cryptoUtil.decrypt(new byte[]{12,1,3,14,15,16,17}); + cryptoUtil.decrypt(new byte[]{12, 1, 3, 14, 15, 16, 17}); }); } @@ -1550,16 +1473,14 @@ public void shouldThrowOnInvalidKeyExceptionWhenTryingToAESDecrypt() throws Exce try { doReturn(aesKeyBytes).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("a_valid_iv"); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); + Mockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("a_valid_iv"); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("a_valid_iv", Base64.DEFAULT)).thenReturn(ivBytes); + base64Mock.when(() -> Base64.decode("a_valid_iv", Base64.DEFAULT)).thenReturn(ivBytes); doThrow(new InvalidKeyException()).when(aesCipher).init(eq(Cipher.DECRYPT_MODE), secretKeyArgumentCaptor.capture(), ivParameterSpecArgumentCaptor.capture()); - cryptoUtil.decrypt(new byte[]{12,13,14,15,16}); + cryptoUtil.decrypt(new byte[]{12, 13, 14, 15, 16}); } catch (IncompatibleDeviceException e) { exception = e; } @@ -1580,15 +1501,13 @@ public void shouldThrowOnInvalidAlgorithmParameterExceptionWhenTryingToAESDecryp try { doReturn(aesKeyBytes).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("a_valid_iv"); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); + Mockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("a_valid_iv"); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("a_valid_iv", Base64.DEFAULT)).thenReturn(ivBytes); + base64Mock.when(() -> Base64.decode("a_valid_iv", Base64.DEFAULT)).thenReturn(ivBytes); doThrow(new InvalidAlgorithmParameterException()).when(aesCipher).init(eq(Cipher.DECRYPT_MODE), secretKeyArgumentCaptor.capture(), ivParameterSpecArgumentCaptor.capture()); - cryptoUtil.decrypt(new byte[]{12,13,14,15,16,17}); + cryptoUtil.decrypt(new byte[]{12, 13, 14, 15, 16, 17}); } catch (IncompatibleDeviceException e) { exception = e; } @@ -1606,16 +1525,14 @@ public void shouldThrowButNotDeleteAESKeysOnBadPaddingExceptionWhenTryingToAESDe byte[] ivBytes = new byte[]{99, 22}; doReturn(aesKeyBytes).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("a_valid_iv"); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); + Mockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("a_valid_iv"); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("a_valid_iv", Base64.DEFAULT)).thenReturn(ivBytes); + base64Mock.when(() -> Base64.decode("a_valid_iv", Base64.DEFAULT)).thenReturn(ivBytes); doThrow(new BadPaddingException()).when(aesCipher).doFinal(any(byte[].class)); - cryptoUtil.decrypt(new byte[]{12,13,14,15,16,17}); + cryptoUtil.decrypt(new byte[]{12, 13, 14, 15, 16, 17}); }); Mockito.verify(keyStore, never()).deleteEntry(KEY_ALIAS); @@ -1631,17 +1548,15 @@ public void shouldThrowButNotDeleteAESKeysOnIllegalBlockSizeExceptionWhenTryingT Assert.assertThrows("The AES encrypted input is corrupted and cannot be recovered. Please discard it.", CryptoException.class, () -> { byte[] aesKeyBytes = new byte[]{11, 22, 33}; doReturn(aesKeyBytes).when(cryptoUtil).getAESKey(); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); - PowerMockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("a_valid_iv"); + Mockito.when(Cipher.getInstance(AES_TRANSFORMATION)).thenReturn(aesCipher); + Mockito.when(storage.retrieveString(KEY_ALIAS + "_iv")).thenReturn("a_valid_iv"); byte[] ivBytes = new byte[]{99, 22}; - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode("a_valid_iv", Base64.DEFAULT)).thenReturn(ivBytes); + base64Mock.when(() -> Base64.decode("a_valid_iv", Base64.DEFAULT)).thenReturn(ivBytes); doThrow(new IllegalBlockSizeException()).when(aesCipher).doFinal(any(byte[].class)); - cryptoUtil.decrypt(new byte[]{12,13,14,15,16,17}); + cryptoUtil.decrypt(new byte[]{12, 13, 14, 15, 16, 17}); }); Mockito.verify(keyStore, never()).deleteEntry(KEY_ALIAS); @@ -1653,66 +1568,10 @@ public void shouldThrowButNotDeleteAESKeysOnIllegalBlockSizeExceptionWhenTryingT } - /* - * Helper methods - */ - private KeyPairGeneratorSpec.Builder newKeyPairGeneratorSpecBuilder(KeyPairGeneratorSpec expectedBuilderOutput) { - KeyPairGeneratorSpec.Builder builder = PowerMockito.mock(KeyPairGeneratorSpec.Builder.class); - PowerMockito.when(builder.setAlias(anyString())).thenReturn(builder); - PowerMockito.when(builder.setSubject(any(X500Principal.class))).thenReturn(builder); - PowerMockito.when(builder.setKeySize(anyInt())).thenReturn(builder); - PowerMockito.when(builder.setSerialNumber(any(BigInteger.class))).thenReturn(builder); - PowerMockito.when(builder.setStartDate(any(Date.class))).thenReturn(builder); - PowerMockito.when(builder.setEndDate(any(Date.class))).thenReturn(builder); - PowerMockito.when(builder.setEncryptionRequired()).thenReturn(builder); - PowerMockito.when(builder.build()).thenReturn(expectedBuilderOutput); - return builder; - } - - private KeyGenParameterSpec.Builder newKeyGenParameterSpecBuilder(KeyGenParameterSpec expectedBuilderOutput) { - KeyGenParameterSpec.Builder builder = PowerMockito.mock(KeyGenParameterSpec.Builder.class); - PowerMockito.when(builder.setKeySize(anyInt())).thenReturn(builder); - PowerMockito.when(builder.setCertificateSubject(any(X500Principal.class))).thenReturn(builder); - PowerMockito.when(builder.setCertificateSerialNumber(any(BigInteger.class))).thenReturn(builder); - PowerMockito.when(builder.setCertificateNotBefore(any(Date.class))).thenReturn(builder); - PowerMockito.when(builder.setCertificateNotAfter(any(Date.class))).thenReturn(builder); - //noinspection WrongConstant - PowerMockito.when(builder.setEncryptionPaddings(anyString())).thenReturn(builder); - //noinspection WrongConstant - PowerMockito.when(builder.setDigests(anyString(), anyString())).thenReturn(builder); - //noinspection WrongConstant - PowerMockito.when(builder.setBlockModes(anyString())).thenReturn(builder); - PowerMockito.when(builder.build()).thenReturn(expectedBuilderOutput); - return builder; - } - - private CryptoUtil newCryptoUtilSpy() throws Exception { - CryptoUtil cryptoUtil = PowerMockito.spy(new CryptoUtil(context, storage, BASE_ALIAS)); - PowerMockito.mockStatic(KeyStore.class); - PowerMockito.when(KeyStore.getInstance(ANDROID_KEY_STORE)).thenReturn(keyStore); - PowerMockito.mockStatic(KeyPairGenerator.class); - PowerMockito.when(KeyPairGenerator.getInstance(ALGORITHM_RSA, ANDROID_KEY_STORE)).thenReturn(keyPairGenerator); - PowerMockito.mockStatic(KeyGenerator.class); - PowerMockito.when(KeyGenerator.getInstance(ALGORITHM_AES)).thenReturn(keyGenerator); - PowerMockito.mockStatic(Cipher.class); - PowerMockito.when(Cipher.getInstance(anyString())).then((Answer) invocation -> { - String transformation = invocation.getArgument(0, String.class); - if (RSA_TRANSFORMATION.equals(transformation)) { - return rsaOaepCipher; - } else if (OLD_RSA_PKCS1_TRANSFORMATION.equals(transformation)) { - return rsaPkcs1Cipher; - } else if (AES_TRANSFORMATION.equals(transformation)) { - return aesCipher; - } - return null; - }); - return cryptoUtil; - } - @Test public void shouldDetectAndMigratePKCS1KeyToOAEP() throws Exception { CryptoUtil cryptoUtil = newCryptoUtilSpy(); - + byte[] aesKeyBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; byte[] encryptedAESKeyPKCS1 = new byte[]{20, 21, 22, 23, 24}; byte[] encryptedAESKeyOAEP = new byte[]{30, 31, 32, 33, 34}; @@ -1721,13 +1580,12 @@ public void shouldDetectAndMigratePKCS1KeyToOAEP() throws Exception { when(storage.retrieveString(eq(KEY_ALIAS))).thenReturn(encodedEncryptedAESPKCS1); when(storage.retrieveString(eq(OLD_KEY_ALIAS))).thenReturn(null); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode(encodedEncryptedAESPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESKeyPKCS1); - PowerMockito.when(Base64.encode(encryptedAESKeyOAEP, Base64.DEFAULT)) - .thenReturn(encodedEncryptedAESOAEP.getBytes(StandardCharsets.UTF_8)); + base64Mock.when(() -> Base64.decode(encodedEncryptedAESPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESKeyPKCS1); + base64Mock.when(() -> Base64.encode(encryptedAESKeyOAEP, Base64.DEFAULT)) + .thenReturn(encodedEncryptedAESOAEP.getBytes(StandardCharsets.UTF_8)); IncompatibleDeviceException incompatibleException = new IncompatibleDeviceException( - new KeyStoreException("Incompatible padding mode") + new KeyStoreException("Incompatible padding mode") ); doThrow(incompatibleException).when(cryptoUtil).RSADecrypt(encryptedAESKeyPKCS1); @@ -1740,10 +1598,10 @@ public void shouldDetectAndMigratePKCS1KeyToOAEP() throws Exception { when(mockKeyEntry.getCertificate()).thenReturn(mockCertificate); when(mockCertificate.getPublicKey()).thenReturn(mockPublicKey); when(keyStore.getEntry(eq(KEY_ALIAS), nullable(KeyStore.ProtectionParameter.class))) - .thenReturn(mockKeyEntry); + .thenReturn(mockKeyEntry); when(rsaPkcs1Cipher.doFinal(encryptedAESKeyPKCS1)).thenReturn(aesKeyBytes); - + doReturn(encryptedAESKeyOAEP).when(cryptoUtil).RSAEncrypt(aesKeyBytes); byte[] result = cryptoUtil.getAESKey(); @@ -1760,18 +1618,17 @@ public void shouldDetectAndMigratePKCS1KeyToOAEP() throws Exception { @Test public void shouldHandleKeyStoreErrorDuringMigration() throws Exception { CryptoUtil cryptoUtil = newCryptoUtilSpy(); - + String encodedEncryptedAES = "encrypted_key"; byte[] encryptedAESBytes = new byte[]{5, 6, 7, 8, 9}; - + when(storage.retrieveString(eq(KEY_ALIAS))).thenReturn(encodedEncryptedAES); when(storage.retrieveString(eq(OLD_KEY_ALIAS))).thenReturn(null); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode(encodedEncryptedAES, Base64.DEFAULT)).thenReturn(encryptedAESBytes); + base64Mock.when(() -> Base64.decode(encodedEncryptedAES, Base64.DEFAULT)).thenReturn(encryptedAESBytes); CryptoException cryptoException = new CryptoException( - "Decryption failed", - new ProviderException("KeyStore error code -1000") + "Decryption failed", + new ProviderException("KeyStore error code -1000") ); doThrow(cryptoException).when(cryptoUtil).RSADecrypt(encryptedAESBytes); @@ -1779,12 +1636,12 @@ public void shouldHandleKeyStoreErrorDuringMigration() throws Exception { SecretKey mockSecretKey = mock(SecretKey.class); when(mockSecretKey.getEncoded()).thenReturn(newAESKey); when(keyGenerator.generateKey()).thenReturn(mockSecretKey); - + byte[] encryptedNewKey = new byte[]{30, 31, 32, 33}; doReturn(encryptedNewKey).when(cryptoUtil).RSAEncrypt(any(byte[].class)); String encodedNewKey = "new_generated_key"; - PowerMockito.when(Base64.encode(encryptedNewKey, Base64.DEFAULT)) - .thenReturn(encodedNewKey.getBytes(StandardCharsets.UTF_8)); + base64Mock.when(() -> Base64.encode(encryptedNewKey, Base64.DEFAULT)) + .thenReturn(encodedNewKey.getBytes(StandardCharsets.UTF_8)); byte[] result = cryptoUtil.getAESKey(); @@ -1797,14 +1654,13 @@ public void shouldHandleKeyStoreErrorDuringMigration() throws Exception { @Test public void shouldUseOAEPDirectlyForNewUsers() throws Exception { CryptoUtil cryptoUtil = newCryptoUtilSpy(); - + byte[] aesKeyBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; byte[] encryptedAESKeyOAEP = new byte[]{20, 21, 22, 23, 24}; String encodedEncryptedAESOAEP = "oaep_encrypted_key"; when(storage.retrieveString(eq(KEY_ALIAS))).thenReturn(encodedEncryptedAESOAEP); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode(encodedEncryptedAESOAEP, Base64.DEFAULT)).thenReturn(encryptedAESKeyOAEP); + base64Mock.when(() -> Base64.decode(encodedEncryptedAESOAEP, Base64.DEFAULT)).thenReturn(encryptedAESKeyOAEP); doReturn(aesKeyBytes).when(cryptoUtil).RSADecrypt(encryptedAESKeyOAEP); @@ -1812,8 +1668,6 @@ public void shouldUseOAEPDirectlyForNewUsers() throws Exception { assertThat(result, is(aesKeyBytes)); - verifyPrivate(cryptoUtil).invoke("RSADecrypt", encryptedAESKeyOAEP); - Mockito.verify(rsaPkcs1Cipher, never()).init(anyInt(), any(PrivateKey.class)); Mockito.verify(rsaPkcs1Cipher, never()).doFinal(any(byte[].class)); @@ -1823,20 +1677,19 @@ public void shouldUseOAEPDirectlyForNewUsers() throws Exception { @Test public void shouldRecognizeIncompatiblePaddingModeInExceptionChain() throws Exception { CryptoUtil cryptoUtil = newCryptoUtilSpy(); - + String encodedEncryptedAES = "encrypted_key"; byte[] encryptedAESBytes = new byte[]{5, 6, 7, 8}; - + when(storage.retrieveString(eq(KEY_ALIAS))).thenReturn(encodedEncryptedAES); when(storage.retrieveString(eq(OLD_KEY_ALIAS))).thenReturn(null); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode(encodedEncryptedAES, Base64.DEFAULT)).thenReturn(encryptedAESBytes); + base64Mock.when(() -> Base64.decode(encodedEncryptedAES, Base64.DEFAULT)).thenReturn(encryptedAESBytes); ProviderException rootCause = new ProviderException("Incompatible padding mode"); IllegalBlockSizeException middleException = new IllegalBlockSizeException("Encryption failed"); middleException.initCause(rootCause); IncompatibleDeviceException topException = new IncompatibleDeviceException(middleException); - + doThrow(topException).when(cryptoUtil).RSADecrypt(encryptedAESBytes); when(keyStore.containsAlias(KEY_ALIAS)).thenReturn(true); @@ -1844,28 +1697,28 @@ public void shouldRecognizeIncompatiblePaddingModeInExceptionChain() throws Exce PrivateKey mockPrivateKey = mock(PrivateKey.class); when(mockKeyEntry.getPrivateKey()).thenReturn(mockPrivateKey); when(keyStore.getEntry(eq(KEY_ALIAS), nullable(KeyStore.ProtectionParameter.class))) - .thenReturn(mockKeyEntry); - + .thenReturn(mockKeyEntry); + byte[] aesKeyBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; when(rsaPkcs1Cipher.doFinal(encryptedAESBytes)).thenReturn(aesKeyBytes); - + byte[] encryptedAESKeyOAEP = new byte[]{20, 21, 22, 23}; doReturn(encryptedAESKeyOAEP).when(cryptoUtil).RSAEncrypt(aesKeyBytes); String encodedOAEP = "oaep_key"; - PowerMockito.when(Base64.encode(encryptedAESKeyOAEP, Base64.DEFAULT)) - .thenReturn(encodedOAEP.getBytes(StandardCharsets.UTF_8)); + base64Mock.when(() -> Base64.encode(encryptedAESKeyOAEP, Base64.DEFAULT)) + .thenReturn(encodedOAEP.getBytes(StandardCharsets.UTF_8)); byte[] result = cryptoUtil.getAESKey(); assertThat(result, is(aesKeyBytes)); Mockito.verify(rsaPkcs1Cipher).doFinal(encryptedAESBytes); - + } @Test public void shouldAllowMultipleRetrievalsAfterMigration() throws Exception { - + CryptoUtil cryptoUtil = newCryptoUtilSpy(); - + byte[] aesKeyBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; byte[] encryptedAESKeyPKCS1 = new byte[]{20, 21, 22, 23, 24}; byte[] encryptedAESKeyOAEP = new byte[]{30, 31, 32, 33, 34}; @@ -1875,13 +1728,12 @@ public void shouldAllowMultipleRetrievalsAfterMigration() throws Exception { // First retrieval - migration happens, returns decrypted key when(storage.retrieveString(eq(KEY_ALIAS))).thenReturn(encodedEncryptedAESPKCS1); when(storage.retrieveString(eq(OLD_KEY_ALIAS))).thenReturn(null); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode(encodedEncryptedAESPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESKeyPKCS1); - PowerMockito.when(Base64.encode(encryptedAESKeyOAEP, Base64.DEFAULT)) - .thenReturn(encodedEncryptedAESOAEP.getBytes(StandardCharsets.UTF_8)); + base64Mock.when(() -> Base64.decode(encodedEncryptedAESPKCS1, Base64.DEFAULT)).thenReturn(encryptedAESKeyPKCS1); + base64Mock.when(() -> Base64.encode(encryptedAESKeyOAEP, Base64.DEFAULT)) + .thenReturn(encodedEncryptedAESOAEP.getBytes(StandardCharsets.UTF_8)); IncompatibleDeviceException incompatibleException = new IncompatibleDeviceException( - new KeyStoreException("Incompatible padding mode") + new KeyStoreException("Incompatible padding mode") ); doThrow(incompatibleException).when(cryptoUtil).RSADecrypt(encryptedAESKeyPKCS1); @@ -1894,16 +1746,16 @@ public void shouldAllowMultipleRetrievalsAfterMigration() throws Exception { when(mockKeyEntry.getCertificate()).thenReturn(mockCertificate); when(mockCertificate.getPublicKey()).thenReturn(mockPublicKey); when(keyStore.getEntry(eq(KEY_ALIAS), nullable(KeyStore.ProtectionParameter.class))) - .thenReturn(mockKeyEntry); + .thenReturn(mockKeyEntry); when(rsaPkcs1Cipher.doFinal(encryptedAESKeyPKCS1)).thenReturn(aesKeyBytes); - + // Mock RSAEncrypt for re-encrypting with OAEP after migration doReturn(encryptedAESKeyOAEP).when(cryptoUtil).RSAEncrypt(aesKeyBytes); byte[] result1 = cryptoUtil.getAESKey(); assertThat(result1, is(aesKeyBytes)); - + // Migration should delete old keys and store re-encrypted AES key Mockito.verify(keyStore).deleteEntry(KEY_ALIAS); Mockito.verify(storage).store(KEY_ALIAS, encodedEncryptedAESOAEP); @@ -1912,28 +1764,27 @@ public void shouldAllowMultipleRetrievalsAfterMigration() throws Exception { @Test public void shouldGenerateNewKeyWhenMigrationFails() throws Exception { CryptoUtil cryptoUtil = newCryptoUtilSpy(); - + String encodedOldKey = "corrupted_old_key"; byte[] encryptedOldKey = new byte[]{5, 6, 7}; - + when(storage.retrieveString(eq(KEY_ALIAS))).thenReturn(null); when(storage.retrieveString(eq(OLD_KEY_ALIAS))).thenReturn(encodedOldKey); - PowerMockito.mockStatic(Base64.class); - PowerMockito.when(Base64.decode(encodedOldKey, Base64.DEFAULT)).thenReturn(encryptedOldKey); + base64Mock.when(() -> Base64.decode(encodedOldKey, Base64.DEFAULT)).thenReturn(encryptedOldKey); doThrow(new CryptoException("Key corrupted", new KeyStoreException("Entry not found"))) - .when(cryptoUtil).getRSAKeyEntry(); + .when(cryptoUtil).getRSAKeyEntry(); byte[] newAESKey = new byte[]{21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36}; SecretKey mockSecretKey = mock(SecretKey.class); when(mockSecretKey.getEncoded()).thenReturn(newAESKey); when(keyGenerator.generateKey()).thenReturn(mockSecretKey); - + byte[] encryptedNewKey = new byte[]{40, 41, 42}; doReturn(encryptedNewKey).when(cryptoUtil).RSAEncrypt(any(byte[].class)); String encodedNewKey = "fresh_key"; - PowerMockito.when(Base64.encode(encryptedNewKey, Base64.DEFAULT)) - .thenReturn(encodedNewKey.getBytes(StandardCharsets.UTF_8)); + base64Mock.when(() -> Base64.encode(encryptedNewKey, Base64.DEFAULT)) + .thenReturn(encodedNewKey.getBytes(StandardCharsets.UTF_8)); byte[] result = cryptoUtil.getAESKey(); assertThat(result, is(newAESKey)); Mockito.verify(storage).store(KEY_ALIAS, encodedNewKey); @@ -1941,4 +1792,26 @@ public void shouldGenerateNewKeyWhenMigrationFails() throws Exception { Mockito.verify(storage, times(1)).remove(KEY_ALIAS); Mockito.verify(storage, times(1)).remove(OLD_KEY_ALIAS); } + + /* + * Helper methods + */ + private CryptoUtil newCryptoUtilSpy() throws Exception { + CryptoUtil cryptoUtil = Mockito.spy(new CryptoUtil(context, storage, BASE_ALIAS)); + Mockito.when(KeyStore.getInstance(ANDROID_KEY_STORE)).thenReturn(keyStore); + Mockito.when(KeyPairGenerator.getInstance(ALGORITHM_RSA, ANDROID_KEY_STORE)).thenReturn(keyPairGenerator); + Mockito.when(KeyGenerator.getInstance(ALGORITHM_AES)).thenReturn(keyGenerator); + Mockito.when(Cipher.getInstance(anyString())).then((Answer) invocation -> { + String transformation = invocation.getArgument(0, String.class); + if (RSA_TRANSFORMATION.equals(transformation)) { + return rsaOaepCipher; + } else if (OLD_RSA_PKCS1_TRANSFORMATION.equals(transformation)) { + return rsaPkcs1Cipher; + } else if (AES_TRANSFORMATION.equals(transformation)) { + return aesCipher; + } + return null; + }); + return cryptoUtil; + } } diff --git a/auth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.java b/auth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.java index b026dac88..7d34e776c 100644 --- a/auth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.java +++ b/auth0/src/test/java/com/auth0/android/provider/CustomTabsControllerTest.java @@ -50,7 +50,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; +import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; @@ -354,9 +354,9 @@ private void bindService(CustomTabsController controller, boolean willSucceed) { private void connectBoundService() throws Exception { final ComponentName componentName = new ComponentName(DEFAULT_BROWSER_PACKAGE, DEFAULT_BROWSER_PACKAGE + ".CustomTabsService"); - + CustomTabsSession session = mock(CustomTabsSession.class, withSettings() - .lenient() + .strictness(Strictness.LENIENT) .defaultAnswer((Answer) invocation -> { if ("getComponentName".equals(invocation.getMethod().getName())) { return componentName; From 1509a0878961fcf3c7873f459400019eefd14b86 Mon Sep 17 00:00:00 2001 From: Prince Mathew Date: Mon, 2 Feb 2026 22:06:09 +0530 Subject: [PATCH 6/6] removed unused import --- .../com/auth0/android/authentication/storage/CryptoUtilTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java b/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java index 6fcf0507c..1fc567d8d 100644 --- a/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java +++ b/auth0/src/test/java/com/auth0/android/authentication/storage/CryptoUtilTest.java @@ -85,7 +85,6 @@ public class CryptoUtilTest { private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; private static final String ALGORITHM_AES = "AES"; private static final String ALGORITHM_RSA = "RSA"; - private static final int RSA_KEY_SIZE = 2048; private final Storage storage = Mockito.mock(Storage.class); private final Cipher rsaOaepCipher = Mockito.mock(Cipher.class);