How to fix Proguard removes java.security code - proguard

I am using java.security.PublicKey in my project
However, when I enable proguard, I get the following error:
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String java.security.PublicKey.getAlgorithm()' on a null object reference
After some investigation I found that it falls on
val entry = KeyStore.PrivateKeyEntry(keyPair.private, arrayOf(certificate)) <--- this line
keyStore.setEntry(alias, entry, keyProtection)
How do I make it work?
adding this to the proguard file does not work:
-keep class java.security.** { *; }
-keepclassmembers class java.security.** { *; }
-keep public interface java.security.Key {*;}
-keep public interface java.security.PublicKey {*;}
-keepclassmembers class * implements java.security.PublicKey {
public <methods>;
}
After even more investigations I found that a more specific line in the constructor of KeyStore.PrivateEntry() is causing the issue
And it is
Certificate[] clonedChain = chain.clone();
clonedChain[0].publicKey is left null
How do I make it clone the public key as well?

The issue was deprecated code.
I was using deprecated spongycastle code to generate a self signed certificate.
The public key in said certificate turned to be null, when on proguard.
I changed to the following:
api 'com.madgag.spongycastle:core:1.58.0.0'
api 'com.madgag.spongycastle:prov:1.58.0.0'
api 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0'
api 'com.madgag.spongycastle:bcpg-jdk15on:1.58.0.0'
And then:
fun KeyPair.toSelfSignedCertificate(principal: String, signatureAlgorithm: String, validity: Int): X509Certificate? {
val x500Name = X500Name("CN=$principal")
val today = Calendar.getInstance()
val notBefore = today.timeInMillis
today.add(Calendar.YEAR, validity)
val notAfter = today.timeInMillis
val sigGen: ContentSigner = JcaContentSignerBuilder(signatureAlgorithm).build(private)
val publicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(public.encoded))
val certGen = X509v3CertificateBuilder(x500Name,
RSAKeyGenParameterSpec.F4,
Date(notBefore),
Date(notAfter),
x500Name,
publicKeyInfo)
val certHolder: X509CertificateHolder = certGen.build(sigGen)
return JcaX509CertificateConverter().getCertificate(certHolder)
}
And it worked well.

Related

Kotlin native. Serialization. Backend Internal error: Exception during IR lowering

I am trying to make a simple serializable class. But something goes wrong. Compilation fails for native build. But jvm target works fine.
Superclass:
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.native.concurrent.ThreadLocal
#Serializable
sealed class DefaultRequest {
private val id = Companion.id
private val name = this::class::simpleName.get()
#Transient
private val format = Json { encodeDefaults = true }
#ThreadLocal
protected companion object {
private var id = -1
get() { return ++field }
}
override fun toString() = format.encodeToString(this)
}
Subclass:
import kotlinx.serialization.Serializable
#Serializable
class PingRequest : DefaultRequest()
I had a look on this tutorial: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#sealed-classes
It looks very easy and simple. But i don't understand where i did a mistake.
Error log from ide:
Compilation failed: Backend Internal error: Exception during IR lowering
File being compiled: /src/commonMain/kotlin/connection/serializable/requests/PingRequest.kt
The root cause org.jetbrains.kotlin.backend.common.CompilationException was thrown at: org.jetbrains.kotlin.ir.symbols.impl.IrBindableSymbolBase.getOwner(IrPrivateSymbolBase.kt:59)
* Source files:
* Compiler version info: Konan: 1.7.20-Beta / Kotlin: 1.7.20
* Output kind: PROGRAM
UPDATE:
So. After changing the code from this:
private val id = Companion.id
private val name = this::class::simpleName.get()
to this:
private val id: Int
private val name: String
init {
id = Companion.id
name = this::class::simpleName.get()!!
}
the problem has gone. But i still have a question. Why is it not working?
private val id = Companion.id
private val name = this::class::simpleName.get()

How to mock an SQLiteOpenHelper

I am trying to mock an SQLiteOpenHelper class in instrumented tests so whenever any fragment tries to get information from the database it returns a generic result. However, I keep getting an error saying:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
com.example.cleaningschedule.helpers.DatabaseHandler Mockito cannot
mock/spy because :
final class
at com.example.cleaningschedule.ToDoListInstrumentedTest.oneTask(ToDoListInstrumentedTest.kt:81)
The test class is:
#RunWith(AndroidJUnit4::class)
class ToDoListInstrumentedTest {
#Rule
#JvmField var activityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)
private fun getActivity() = activityRule.activity
#After
fun tearDown() {
InstrumentationRegistry.getInstrumentation().getTargetContext().deleteDatabase("TaskDatabase")
}
#Test
fun oneTask() {
val mock = mock(DatabaseHandler::class.java)
`when`(mock.getTasks()).thenThrow()
onView(withId(R.id.taskName)).check(matches(isDisplayed()))
}
}
The class I am trying to mock is:
class DatabaseHandler(context: Context): SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
companion object {
private const val DATABASE_VERSION = 5
private const val DATABASE_NAME = "TaskDatabase"
...
}
override fun onCreate(db: SQLiteDatabase?) {
...
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
...
}
fun getTasks(): MutableList<Pair<MutableList<String>, MutableList<Room>>> {
...
}
}
I have looked at several other similar questions but none have helped:
Error mocking Class which hold reference to SQLiteOpenHelper
Mock final class in java using mockito library - I had a lot of issues with import PowerMock
How to mock a final class with mockito - I have added the dependency and created the file with the mock-maker-inline line as suggested in the answers put I still get the same error. I also tried the answer that suggested Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable)) but this gave me a 'Not enough information to infer type variable T' error
Mock final class with Mockito 2
Mockito cannot mock/spy because : Final Class
Cannot mock/spy class java.util.Optional
I will made an Interface :
public interface ContainerHandler {
MutableList<Pair<MutableList<String>, MutableList<Room>>> getTasks();
}
Then I made DatabaseHandler inherit this interface, I call Mockito's mock function with the Interface.
val mock = mock(ContainerHandler::class.java)
`when`(mock.getTasks()).thenThrow()
And finally I inject my mock into the tested class.

Error with Parcelable / Parceler when minifyEnabled true

I am getting the following error when I set minifyEnabled true:
Unable to find generated Parcelable class for com.codeworks.myapp.FirestoreModel, verify that your class is configured properly and that the Parcelable class com.codeworks.myapp.FirestoreModel$$Parcelable is generated by Parceler.
I placed the following code as shown in the parceler.org website in proguard-rules.pro:
# Parceler configuration
-keep interface org.parceler.Parcel
-keep #org.parceler.Parcel class * { *; }
-keep class **$$Parcelable { *; }
-keep class org.parceler.Parceler$$Parcels
... and added the #Keep annotation in the FirestoreModel and the Fragment's class:
#Parcel
#Keep
class FirestoreModel{
...
}
#Keep
class MyFragment: Fragment() {
...
}
and then added some pro-guard rules, or as I understood it from the examples from stackoverflow.com (because I can't understand the rules from official documentation):
-keepnames class com.codeworks.myapp.MyFragment { *; }
-keepnames class com.codeworks.myappFirestoreModel { *; }
I am still getting the following error:
2020-04-21 08:07:38.554 28188-28188/com.codeworks.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.codeworks.myapp, PID: 28188
h.a.d: Unable to find generated Parcelable class for com.codeworks.myappFirestoreModel, verify that your class is configured properly and that the Parcelable class com.codeworks.myappFirestoreModel$$Parcelable is generated by Parceler.
at h.a.e$b.a(:154)
at h.a.e.a(:73)
at h.a.e.a(:57)
at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:103)
at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:68)
at com.firebase.ui.firestore.FirestoreRecyclerAdapter.b(:158)
at androidx.recyclerview.widget.RecyclerView$g.a(:7065)
at androidx.recyclerview.widget.RecyclerView$g.a(:7107)
at androidx.recyclerview.widget.RecyclerView$v.a(:6012)
at androidx.recyclerview.widget.RecyclerView$v.a(:6279)
at androidx.recyclerview.widget.RecyclerView$v.b(:6118)
at androidx.recyclerview.widget.RecyclerView$v.d(:6114)
at androidx.recyclerview.widget.LinearLayoutManager$c.a(:2303)
at androidx.recyclerview.widget.LinearLayoutManager.a(:1627)
at androidx.recyclerview.widget.LinearLayoutManager.a(:1587)
at androidx.recyclerview.widget.LinearLayoutManager.e(:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(:4134)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(:3851)
at androidx.recyclerview.widget.RecyclerView.consumePendingUpdateOperations(:1897)
at androidx.recyclerview.widget.RecyclerView$a.run(:414)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:727)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7464)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:549)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:955)
Apparently, the variable names of the onViewCreated function on MyFragment are being renamed despite the pro-guard rules.
The error seems to point to this lines:
// Here... at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:68)
adapter = object : FirestoreRecyclerAdapter<DpMemoModel, DpViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FirestoreViewHolder{
val view : View = LayoutInflater.from(parent.context).inflate(R.layout.firestore_cardview, parent, false)
return FirestoreViewHolder(view)
}
override fun onBindViewHolder(
holder: FirestoreViewHolder,
position: Int,
model: FirestoreModel
) {
...
// ...and Here: **at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:103)**
val wrapped : Parcelable = Parcels.wrap(model)
holder.cardView.setOnClickListener {
val intent= Intent(activity, FirestoreInfoActivity::class.java)
intent.putExtra("parcel", wrapped)
startActivity(intent)
}
P.S.: I am using FirestoreRecyclerAdapter, I thought it was the best way since I was dealing with Cloud Firestore and Firebase Storage data.
I don't know what went wrong or what I've missed. I switched to #Parcelize for now. The code is a bit longer, but at least it works. Also, there is no need to use:
# Parceler configuration
-keep interface org.parceler.Parcel
-keep #org.parceler.Parcel class * { *; }
-keep class **$$Parcelable { *; }
-keep class org.parceler.Parceler$$Parcels
...in the proguard-rules.pro. All I need is #Keep annotation in the data class. I've spent a day and a half on research and trial and error, but, well, c'est la vie.

Extension property inside class: Unresolved reference: errorResponse

In my android project:
// Extension Properties
val Response<*>.errorResponse: ErrorResponse
get() = ErrorUtils.parseError(this)
class TransportService {
companion object {
private val SERVICE_UNAVAILABLE_CODE = 503
private val traderMonitorRestClient = RestClientFactory.createRestClient(TraderMonitorRestClient::class.java)
private val serviceUnavailableErrorResponse: Response<*>
}
and here use extension property
class TradersViewModel(application: Application) : AndroidViewModel(application) {
val errorResponse = response.errorResponse
}
nice it's work,
But if I move errorResponse inside class:
class TransportService {
companion object {
private val SERVICE_UNAVAILABLE_CODE = 503
private val traderMonitorRestClient = RestClientFactory.createRestClient(TraderMonitorRestClient::class.java)
private val serviceUnavailableErrorResponse: Response<*>
private val TAG = TransportService::class.java.name
val Response<*>.errorResponse: ErrorResponse
get() = ErrorUtils.parseError(this)
}
}
then I get compile error in use place:
val errorResponse = response.errorResponse
error:
Unresolved reference: errorResponse
This is because the errorResponse extension is only available in the context of the companion object of TransportService when you define it there.
If your extension function doesn't need the companion's properties or methods, there is no real reason to put the extension there. Don't be afraid of polluting the global scope. Technically, you're only defining this extension on your Response object.
That being said, if you really need the companion object's context for your extension function (e.g. if the body of your extension property uses some methods or properties of the companion), you can manually provide the context on the usage site this way:
val errorResponse = with(TransportService.Companion) { response.errorResponse }
This is not limited to companion objects, you just need to provide an instance of the class that contains the extension to with(instance).

How to overcome "same JVM signature" error when implementing a Java interface?

With the code below, I am getting the following error in IntelliJ IDEA 13.1.6 and Kotlin plugin 0.11.91.AndroidStudio.3:
Platform declaration clash: The following declarations have the same JVM signature (getName()Ljava/lang/String;):
• public open fun getName(): kotlin.String?
• internal final fun <get-name>(): kotlin.String?
Java class, JavaInterface.java:
public interface JavaInterface {
public String getName();
}
Kotlin class, KotlinClass.kt
public class KotlinClass(val name: String?) : JavaInterface
I've tried overriding the 'getter' method by
adding override fun getName(): String? = name, but that produces the same error.
I can see one workaround by doing this instead:
public class KotlinClass(val namePrivate: String?) : JavaInterface {
override fun getName(): String? = namePrivate
}
But in my real-world case I have a number of properties to implement and need setters too. Doing this for each property doesn't seem very Kotlin-ish. What am I missing?
Making that variable private solves the problem.
public class KotlinClass(private val name: String?) : JavaInterface
You could use #JvmField for instructs the compiler not generate getter/setter, and you can implement your setters and getters. With this your code work well in Java (as attribute getter/setter) and Kotlin as property
Example:
JAVA:
public interface Identifiable<ID extends Serializable>
{
ID getId();
}
KOTLIN:
class IdentifiableImpl(#JvmField var id: String) :Identifiable<String>
{
override fun getId(): String
{
TODO("not implemented")
}
}
The annotation feature of Kotlin named #JvmName will solve the duplication problem in Java and Kotlin when having the same signature.
fun function(p: String) {
// ...
}
// Signature: function(Ljava/lang/String)
With the use of JvmName will be:
#JvmName("functionOfKotlin")
fun function(p: String) {
// ...
}
// Signature: functionOfKotlin(Ljava/lang/String)
IMHO most readable combination is field + explicit interface implementation by the single-expression function (combination of #Renato Garcia's and #Steven Spungin's answers):
Java:
public inteface SomeInterface {
String getFoo();
}
Kotlin:
class Implementation(#JvmField val foo: String) : SomeInterface {
override fun getFoo() = foo
}
Another work-around is to declare the properties in an abstract Kotlin class, then write a small java class that extends KotlinClass and implements JavaInterface.
// JavaInterface.java
public interface JavaInterface {
int getFoo();
void setFoo(int value);
}
// KotlinClass.kt
abstract class KotlinClass(open var foo : Int = 0) {
}
// JavaAdapter.java
class JavaAdapter extends KotlinClass implements JavaInterface {
// all code in KotlinClass, but can't implement JavaInterface there
// because kotlin properties cannot override java methods.
}
We have found that to use the same names without clashing, the ctor args must be private AND you must still override the interfaces methods. You don't need any additional backing fields. Also, your expression body assignment will not recurse, so you can safely use that syntax.
Java Interface
interface IUser {
String getUserScope();
String getUserId();
}
Kotlin Class
class SampleUser(private val userScope: String, private val userId: String) : IUser {
override fun getUserId() = userId
override fun getUserScope() = userScope
}
If you have direct control over the interface then the best approach is to write the interface in Kotlin. You can then write your class
public class KotlinClass(override val name: String?) : KotlinInterface
and still reference it from any Java code using the same interface as before. This looks a lot neater than setting all the properties to private and overriding the get function. Obviously if you can't migrate the interface to Java because you don't own it then that seems to be the only solution.
public interface JavaInterface {
public String getName();
}
public class KotlinClass(val namePrivate: String?) : JavaInterface {
private var name = namePrivate
override fun getName(): String? {
return name
}
}
Rename the variable to something else, or make it private if u dont want it to be public.
convert function to property instead of initializing property from a function.
for ex:
fun getCountriesList(): List<Country> {
val countries = mutableListOf<Country>()
countries.add(Country("in", "+91", "India", R.drawable.indian_flag))
countries.add(Country("us", "+1", "United States",R.drawable.us_flag))
return countries
}
to
val countriesList: List<Country>
get() {
val countries = mutableListOf<Country>()
countries.add(Country("in", "+91", "India", R.drawable.indian_flag))
countries.add(Country("us", "+1", "United States", R.drawable.us_flag))
return countries
}