Nativescript: Creating RSA keypair for signatures on Android

Created on 9 Nov 2017  路  6Comments  路  Source: NativeScript/NativeScript

Did you verify this is a real problem by searching Stack Overflow and the other open issues in this repo?
  • I could not find any help regarding {N} on SO. There are plenty of examples on the native Android side but this is not the problem. My code runs in a native Android app without problems.

  • The {N} issues list does not entail similar open tickets.

Tell us about the problem

I want to create a plugin which creates & uses cryptographic keys for signatures (I use the plugin seed project). While my example code runs in a native example Android app, the same does not work in a {N} app. The problem lies in the step of using the KeyGenParameterSpec.Builder class. When I am only building an instance of it, followed by forwarding to KeyPairGenerator.initilize() the operation succeeds. This is not the case if I add any parameters to the building process, like digests or signature paddings (see code example below).

Which platform(s) does your issue occur on?

Only approved this on Android. I am some workdays away from the iOS side (hopefully this will be more successful^^).

Please provide the following version numbers that your issue occurs with:
  • CLI: 3.2.1
  • Cross-platform modules: 3.3.0
  • Runtime(s): 3.0.0 (Android & iOS)
  • Plugin(s): -
Please tell us how to recreate the issue in as much detail as possible.

I created a repo for this which entails a complete {N} project to reproduce the problem.

Is there code involved? If so, please share the minimal amount of code needed to recreate the problem.

Any example of an successful variant goes as follows:

let keyPairGenerator: java.security.KeyPairGenerator = 
    java.security.KeyPairGenerator.getInstance(algorithm, "AndroidKeyStore");
console.log("KeyPairGenerator created");
let keyGenParameterSpec: any =
    new android.security.keystore.KeyGenParameterSpec.Builder(
        alias,
        purpose)
        //.setAlgorithmParameterSpec(new java.security.spec.RSAKeyGenParameterSpec(2048,
        //                java.security.spec.RSAKeyGenParameterSpec.F4))
        //.setDigests("SHA-256")
        //.setSignaturePaddings("PSS")
        .build();
keyPairGenerator.initialize(keyGenParameterSpec);
keyPairGenerator.generateKeyPair();

An failing approach would result in uncommenting either digests or signature padding settings. The failure output itself looks like this:

JS: KeyPairGenerator created
11-09 14:20:39.125 15109 15109 F art     : art/runtime/java_vm_ext.cc:470]     from java.lang.Object com.tns.Runtime.callJSMethodNative(int, int, java.lang.String, int, boolean, java.lang.Object[])
11-09 14:20:39.125 15109 15109 F art     : art/runtime/java_vm_ext.cc:470]   at com.tns.Runtime.callJSMethodNative(Native method)
11-09 14:20:39.125 15109 15109 F art     : art/runtime/java_vm_ext.cc:470]   at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1043)
11-09 14:20:39.125 15109 15109 F art     : art/runtime/java_vm_ext.cc:470]   at com.tns.Runtime.callJSMethodImpl(Runtime.java:925)
11-09 14:20:39.125 15109 15109 F art     : art/runtime/java_vm_ext.cc:470]   at com.tns.Runtime.callJSMethod(Runtime.java:912)
11-09 14:20:39.125 15109 15109 F art     : art/runtime/java_vm_ext.cc:470]   at com.tns.Runtime.callJSMethod(Runtime.java:896)
11-09 14:20:39.125 15109 15109 F art     : art/runtime/java_vm_ext.cc:470]   at com.tns.Runtime.callJSMethod(Runtime.java:888)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethodNative(Native method)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1043)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethodImpl(Runtime.java:925)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethod(Runtime.java:912)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethod(Runtime.java:896)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethod(Runtime.java:888)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethodNative(Native method)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1043)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethodImpl(Runtime.java:925)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethod(Runtime.java:912)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethod(Runtime.java:896)
11-09 14:20:39.291 15109 15109 F art     : art/runtime/runtime.cc:422]   at com.tns.Runtime.callJSMethod(Runtime.java:888)

Thanks for any help and hint on what is wrong here.
Sincerely, David

android

All 6 comments

HI @dartmann ,
Thank you for your interest in NativeScript.

Regarding your question, at this time, it is not possible to test a plugin on the playground. Something that I would suggest is to create a sample project in a playground and to try reproducing the same problem in it while using the implementation of RSA key pair for signatures.

If this is not applicable for you, it would help if you could send us the plugin, which will allow us to recreate the problem on our side and to investigate, what is causing this problem.
Thank you in advance for your cooperation.

Hi @tsonevn ,
thx for your response. I tried to follow your suggestion and recreated the relevant code with {N} Playground but it fails while creating a link for sharing the projects. Additionally the previewer app never shows anything but the splash screen on my smartphone. But nevermind: I downloaded the zip file from {N} Playground and added the stuff to a new repo here.
You will recognize the same behavior as already described.

Hey @dartmann,

After running the sample and inspecting the adb logcat (device log), I stumbled upon 2 particular errors, namely -

JNI DETECTED ERROR IN APPLICATION: bad arguments passed to android.security.keystore.KeyGenParameterSpec$Builder android.security.keystore.KeyGenParameterSpec$Builder.setDigests(java.lang.String[]) (see above for details)
and
JNI DETECTED ERROR IN APPLICATION: bad arguments passed to android.security.keystore.KeyGenParameterSpec$Builder android.security.keystore.KeyGenParameterSpec$Builder.setSignaturePaddings(java.lang.String[]) (see above for details)

The JNI call to Java's KeyGenparameterSpec$Builder setDigests (or setSignaturePaddings) which the JavaScript calls translate to, expect an array, instead of a singular string. Now that we see what the problem is, we can easily address it by wrapping the strings in arrays like so:

let keyGenParameterSpec: any =
                new android.security.keystore.KeyGenParameterSpec.Builder(alias, purpose)
                    .setAlgorithmParameterSpec(new java.security.spec.RSAKeyGenParameterSpec(2048, java.security.spec.RSAKeyGenParameterSpec.F4))
                    .setDigests(["SHA-256"])
                    .setSignaturePaddings(["PKCS1"])
                    .build();

Why couldn't you pass arguments as-is to a variadic function? - I tried to shed a bit of light in a similar SO post - https://stackoverflow.com/a/47205617/6408287 - the most important bit being arguments meant for the JNI -> Java call cannot be easily inferred from a function call in JavaScript, so instead you need to wrap the arguments in an array.

As for the abrupt crash with no visible explanation - JNI errors / c signals cannot be handled universally for all android devices, and we are still looking for ways to tackle that problem.

And finally - you can have android typings dynamically generated for your project and the compile SDK (and support libs) used within, so you will no longer have to work with the outdated android17.d.ts. The PROs of that is that you would get hints that setDigests and setSignaturePaddings methods take array as the only parameter. To get those run tns run/build android --androidTypings, the result is a android.d.ts on the root of your project, which you may then include if the typescript compiler doesn't find automatically.
Getting android typings is optional for the time being, but may become a default soon enough.

Hi @Pip3r4o ,
first of all, thanks for your detailed answer.

As you mentioned putting the arguments in an array solves the issue.

Lastly I have one question:
Regarding dynamic android typings: I followed your suggestion of creating the android.d.ts file. Is it ok to do that once in an arbitrary project and add it to my plugin project afterwards?
Because I do not get any hints like you mentioned in the logs.

I could've solved the issue by simply using adb logcat and opening my eyes a little bit but I completely forgot about that...sorry 馃槗 .

Thanks again. @tsonevn @Pip3r4o I really appreciate your fast and competent help. 馃槃 馃憤
Sincerely, David

@dartmann it's absolutely fine to reuse the same android.d.ts in multiple projects. It's not unique per-se, it is generated against the compileSdk version, and the support libraries (v4, v7) of whatever version you've specified in the app.gradle, or defaults to the latest installed android support version.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NordlingDev picture NordlingDev  路  3Comments

fmmsilva picture fmmsilva  路  3Comments

dhanalakshmitawwa picture dhanalakshmitawwa  路  3Comments

guillaume-roy picture guillaume-roy  路  3Comments

rogangriffin picture rogangriffin  路  3Comments