I was directed this way from the main superuser site:

I have zero experience with Java or Android apps (I have coding experience in C) and tried reverse engineering a [now defunct/unsupported] app which would take a serial number* and encrypt it using RSA with PKCS1Padding.

I'm happy to share what I have, as the creator is uncontactable, and only a very selected few people have files (or access to the software) in relation to this, so the key can be 'in the wild'. What I believe to be the code is below... I roughly understand what it is doing.

.class public Lcom/h1dd3n/securefiles/Keygen;
.super Ljava/lang/Object;
.source "Keygen.java"

# static fields
.field static key:Ljava/lang/String;

.field static modulus:Ljava/math/BigInteger;

.field static pubExp:Ljava/math/BigInteger;

# direct methods
.method static constructor <clinit>()V
    .locals 3

    const/16 v2, 0x10

    .line 15
    const-string v0, "00FB451D3F45D82B92FC6F243D50441DD75F2DB995842A3D389FC4536A27F42242C9C8DCB4DA0E2573E5CA2E5D0A2AD7E790D8A79CAC2DE68BEAF99D21E229A9CF04ABD09D61C8C66C4FE3B32456496305792FF9D2D2198B87BFAB2637518C1D3F44D27B93EF2C0ED3379993D04944EB356D4BDE343017CFB13405B403A3D81D2C7B099AE5651CF14DD3CBE21C435076F244D9DA8F54DA19BA6301AF1F7DA699E2EBFD9C0BB2778D812E8D9BE66089B2783B9E60FA28FA83CD3B356669BC15BC84058FEEE493CCFBE2E13E0B53B01886D47EB75BFC75758A5CFA5A1836E697FD51846578B4BDEDE3A6BD1FE4D49ABAC072AED433AC5A19BF94C9F6C7F4D95740EF"

    sput-object v0, Lcom/h1dd3n/securefiles/Keygen;->key:Ljava/lang/String;

    .line 16
    new-instance v0, Ljava/math/BigInteger;

    sget-object v1, Lcom/h1dd3n/securefiles/Keygen;->key:Ljava/lang/String;

    invoke-direct {v0, v1, v2}, Ljava/math/BigInteger;-><init>(Ljava/lang/String;I)V

    sput-object v0, Lcom/h1dd3n/securefiles/Keygen;->modulus:Ljava/math/BigInteger;

    .line 17
    new-instance v0, Ljava/math/BigInteger;

    const-string v1, "010001"

    invoke-direct {v0, v1, v2}, Ljava/math/BigInteger;-><init>(Ljava/lang/String;I)V

    sput-object v0, Lcom/h1dd3n/securefiles/Keygen;->pubExp:Ljava/math/BigInteger;

.end method

.method public constructor <init>()V
    .locals 0

    .line 14
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

.end method

.method public static encrypt(Ljava/lang/String;)[B
    .locals 7
    .param p0, "text"    # Ljava/lang/String;

    .line 23
    new-instance v2, Ljava/security/spec/RSAPublicKeySpec;

    sget-object v5, Lcom/h1dd3n/securefiles/Keygen;->modulus:Ljava/math/BigInteger;

    sget-object v6, Lcom/h1dd3n/securefiles/Keygen;->pubExp:Ljava/math/BigInteger;

    invoke-direct {v2, v5, v6}, Ljava/security/spec/RSAPublicKeySpec;-><init>(Ljava/math/BigInteger;Ljava/math/BigInteger;)V

    .line 24
    .local v2, "keySpec":Ljava/security/spec/RSAPublicKeySpec;
    const-string v5, "RSA"

    invoke-static {v5}, Ljava/security/KeyFactory;->getInstance(Ljava/lang/String;)Ljava/security/KeyFactory;

    move-result-object v3

    .line 25
    .local v3, "kf":Ljava/security/KeyFactory;
    invoke-virtual {v3, v2}, Ljava/security/KeyFactory;->generatePublic(Ljava/security/spec/KeySpec;)Ljava/security/PublicKey;

    move-result-object v4

    .line 27
    .local v4, "publicKey":Ljava/security/PublicKey;
    const-string v5, "RSA/ECB/PKCS1Padding"

    invoke-static {v5}, Ljavax/crypto/Cipher;->getInstance(Ljava/lang/String;)Ljavax/crypto/Cipher;

    move-result-object v0

    .line 28
    .local v0, "cipher":Ljavax/crypto/Cipher;
    const/4 v5, 0x1

    invoke-virtual {v0, v5, v4}, Ljavax/crypto/Cipher;->init(ILjava/security/Key;)V

    .line 29
    invoke-virtual {p0}, Ljava/lang/String;->getBytes()[B

    move-result-object v5

    invoke-virtual {v0, v5}, Ljavax/crypto/Cipher;->doFinal([B)[B
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0

    move-result-object v5

    .line 35
    .end local v0    # "cipher":Ljavax/crypto/Cipher;
    .end local v2    # "keySpec":Ljava/security/spec/RSAPublicKeySpec;
    .end local v3    # "kf":Ljava/security/KeyFactory;
    .end local v4    # "publicKey":Ljava/security/PublicKey;
    return-object v5

    .line 31
    move-exception v1

    .line 33
    .local v1, "e":Ljava/lang/Exception;
    invoke-virtual {v1}, Ljava/lang/Exception;->printStackTrace()V

    .line 35
    const/4 v5, 0x0

    goto :goto_0
.end method

Now I have a file of which I wish to view the original contents that was encrypted using this method. How on earth do I do that? I'd also be interested in creating new files in a similar way on a Windows based system if possible...

Full APK source (minus resources as some confidential stuff in there) is available here (it only worked on some v5/v6 Android devices)

File I wish to decrypt is [here]

the file is generated in SecureKeySource\smali\com\tmsec\securedisk\Activator and saved onto the SD Card.

Serial number is the manufacturer's data from an SD card which is then encrypted and used by another piece of software to allow access. The device was password write protected to prevent deletion/formatting. If you lost this SD card you were screwed as you couldn't access the programs! The device would be checked and deny access if there was a data mismatch. This was the most secure method on an extremely low budget that was thought of at the time of creation - a few years back now; why they did it this way I do not know... I have key backups but no way of associating them with cards or no methods of creating new cards without buying old android tablets/phones and hoping they work with microSD cards (no idea how they originally did it on normal SD cards!! OTG adapters don't seem to show the manufacturer data?). I do have the password to remove write access allowing the cards to be formatted however as that was recorded at least.

I was hoping that (looking at the extracted source code) there would be some sort of KeyGenerator class where I could enter the new ID as a string and then run.

  • The links for the files attached need to be public
    – sudhackar
    Commented Feb 12, 2021 at 7:33
  • Thanks, hadn't noticed. They are now public
    – Dan Lewis
    Commented Feb 12, 2021 at 10:06

1 Answer 1


Have you tried using the Frida.re, is an excellent tool for intercepting Android functions, for example you can hook this function responsible for calculating the RSA, see an example from the frida documentation itself:

import frida, sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))

jscode = """
Java.perform(function () {
  // Function to hook is defined here
  var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');

  // Whenever button is clicked
  var onClick = MainActivity.onClick;
  onClick.implementation = function (v) {
    // Show a message to know that the function got called

    // Call the original onClick handler
    onClick.call(this, v);

    // Set our values after running the original onClick handler
    this.m.value = 0;
    this.n.value = 1;
    this.cnt.value = 999;

    // Log to the console that it's done, and we should have the flag!
    console.log('Done:' + JSON.stringify(this.cnt));

process = frida.get_usb_device().attach('com.example.seccon2015.rock_paper_scissors')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running CTF')

To use this framework you will need to have adb installed on your computer and activate the usb debbug settings, after accepting the RSA that is generated on the connection.

Please for a more complete explanation consider reading the following part of the documentation: Solving Android CTF

For beginners a step-by-step tutorial on dynamic analysis with Frida use: Frida Start

  • You can also generate the Java equivalent if you are not familiar with SMALI, this can help you with reconstruct.
    – Happy
    Commented Feb 11, 2021 at 12:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.