Android Encryption: First step for secure notepad


Welcome to another Android-related article!

From time to time, everybody needs some privacy. I don’t want to judge, but every person on the planet has sneaky little secrets. Need to hide any information? That’s why encryption was invented.

Today I want to introduce an elementary way to encrypt (and decrypt) data on Android.

Let’s go 💣!

What do we need it for?

In my previous articles, you can find answers for such questions as:

Why is privacy so important? – check out Simple Python Ex-If remover,

How old is encryption? – I’ve devoted about 1/3 of the CryptoPy: Caesar Cipher aka Shift Cipher in Python article to the history of cryptography roots,

What is encryption, what are the types, differences? – if you are not familiar with the cryptology concept – Cryptology 101: Encryption article is there for you (actually, there is yet another one, Textbook RSA – asymmetric encryption with Java – you may find interesting, or maybe check out the #cryptology101 tag on my blog)

Already familiar? Cool 😎

When we are on the same page, I would like to reveal a secret to you (not an encrypted one!).

I’m planning to build a private notepad app for Android.

And I know there are some of these kinds of apps out there. They are ranked well (even above 4.4). But does notepad really need to require CAMERA PERMISSION (with network access)? Including contacts and billing, it’s a little bit shady for me.

That’s why I would like to present to you on this blog how to build this kind of application, and I would like to make it with blocks.

This article describes an encryption intro, so we will build a PoC app for data encryption.

We will also make use of Android: PoC Biometric Authentication (so you better get familiar).

We will probably add another layer of defense and combine it into an elegant and convenient app.

You better keep checking. 😊

Concept & User Interface

As you might notice, I like my articles and examples to be simple. This one will be very similar to Textbook RSA implementation or Android PoC.

In this example, I will use the AES encryption algorithm mentioned in Cryptology 101: Encryption article.

I also announced cycles of articles related to symmetric cryptography, block ciphers, modes of operation, and so on. I’m going to link them here:

  1. Cryptology 101: Block Cipher Modes of Operation with Python example

; stay tuned. 😊

Just to let you know, AES is the NIST approved algorithm and was announced in FIPS PUB 197.

For now, we need just a limited definition:

The Advanced Encryption Standard (AES) specifies a FIPS-approved cryptographic algorithm that can be used to protect electronic data. The AES algorithm is a symmetric block cipher that can encrypt (encipher) and decrypt (decipher) information.

Encryption converts data to an unintelligible form called ciphertext; decrypting the ciphertext converts the data back into its original form, called plaintext.

The AES algorithm is capable of using cryptographic keys of 128, 192, and 256 bits to encrypt and decrypt data in blocks of 128 bits.


The last line is crucial – cryptographic keys of 128, 192, and 256 bits to encrypt and decrypt data in blocks of 128 bits.

Here we will use AES with a cryptographic key of 256 bits (AES uses a symmetric key – it means the same key for encryption and decryption).

What do we need in the interface?

  • 3 text widgets: One plaintext to provide our message, the second one for the password – converted to the encryption key, and the last one for ciphertext output,
  • 2 buttons: to invoke encrypt or decrypt operation,
  • 3 TextViews: We will display the last operation, symmetric key, and hex value of encryption (or decryption) message – just for us, to do an additional checkup.

Simple UI ready to go presents itself as follows:

Encryption Example UI

I used the current default layout – ConstraintLayout. In my option, it is convenient but requires some practice in setting constrains properties.

Check out my XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=""

        app:layout_constraintTop_toTopOf="parent" />

        app:layout_constraintTop_toTopOf="parent" />

        app:layout_constraintTop_toTopOf="parent" />

        app:layout_constraintTop_toTopOf="parent" />


Under the surface

We do not need to modify any config in this app, so the default will do the job. All we need is MainActivity. Let’s start with defining some statics and variables:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getName();
    private static final String ALGORITHM = "AES";
    private static final String CIPHER = "AES/ECB/PKCS5Padding";
    private static final String SHA2 = "SHA-256";

    EditText etPlain, etPass, etEnc;
    TextView tvKey, tvEncHex, tvOp;
    Button btEnc, btDec;
    String output;

I want to direct your attention to CIPHER, ALGORITHM, and SHA2 statics.

They are crucial further.

Both ALGORITHM (AES) and SHA2 (SHA-256) are just the algorithm names – we will use build in Javax Cipher Class and MessageDigest Class, so we have to tell which instance of the class we want to use.

Our CIPHER static is one of many Cipher instances.

If you want to check other possibilities, visit the Cipher section on Android Development.

Then we need to assign this mess in the onCreate method:

protected void onCreate(Bundle savedInstanceState) {

        etPlain = (EditText) findViewById(;
        etPass = (EditText) findViewById(;
        etEnc = (EditText) findViewById(;

        btEnc = (Button) findViewById(;
        btDec = (Button) findViewById(;

        tvKey = (TextView) findViewById(;
        tvEncHex = (TextView) findViewById(;
        tvOp = (TextView) findViewById(;



As you may know from my previous articles (mentioned above), we will need 3 additional functions: to generate a key, to encrypt (encipher) the data, and to decrypt (decipher) it.

We are willing to use AES256 – it means we need to make sure that our key is 256 bit long.

The best way to make sure our key is always 256 bit is to use a 256-bit hash function like SHA-256.

If you want to learn more about hash functions, check out my future article: @placeholder.

So, we will make a message digest out of a string:

    private SecretKeySpec generateKey(String pass) 
throws NoSuchAlgorithmException, UnsupportedEncodingException {
        final MessageDigest digest = MessageDigest.getInstance(SHA2);
        byte[] bytes = pass.getBytes("UTF-8");
        digest.update(bytes, 0, bytes.length);
        byte[] key = digest.digest();
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        return secretKeySpec;

As you can see, it takes our string as bytes, and it makes SecretKeySpec for our ALGORITHM based on the byte array of our key.

Then we have our two siblings’ functions: encrypt and decrypt. They both take a string and generated key as parameters:

    private String encrypt(String value, SecretKeySpec key) throws Exception {
        tvKey.setText("Enc Key= " + bytesToHex(key.getEncoded(), true));
        Cipher cipher = Cipher.getInstance(CIPHER);
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] encrypted = cipher.doFinal(value.getBytes("Latin1"));
        tvEncHex.setText("B64 Hex= " + Base64.encodeToString(encrypted, android.util.Base64.DEFAULT));
        String ciphertext = bytesToHex(encrypted, false);
        return ciphertext;

    private String decrypt(String encrypted, SecretKeySpec key) throws Exception {
        tvKey.setText("Dec Key= " + bytesToHex(key.getEncoded(), true));
        Cipher cipher = Cipher.getInstance(CIPHER);
        cipher.init(Cipher.DECRYPT_MODE, key);
        byte[] enc_bytes = hexStringToByteArray(encrypted);
        byte[] original = cipher.doFinal(enc_bytes);
        return new String(original);

In both, we are setting the TextView field with key-value (with our helper bytesToHex() function), get an instance of a CIPHER, and initialize it with the correct mode.

Now, in the encrypt function, we “doFinal”. Still, in decrypt, we have to convert hexStringToByteArray() first (another helper function) – because, at the end of encryption, we are converting bytesToHex() (so we have to reverse it).

Below you can find our helper functions:

    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        return data;

    private static String bytesToHex(byte[] bytes, boolean spaces) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            if (spaces) {
                sb.append(String.format("%02X ", b));
            } else {
                sb.append(String.format("%02X", b));
        return sb.toString();

And we can finally update our OnClickListeners:

        btEnc.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try {
                    String plain = etPlain.getText().toString();

                    String enc_pass = etPass.getText().toString();
                    SecretKeySpec encKey = generateKey(enc_pass);

                    output = encrypt(plain, encKey);

                    tvOp.setText("Last operation: Encryption");
                } catch (Exception e) {
                    Log.d("Encryption Error", e.getMessage());
                    Toast.makeText(getApplicationContext(), "Encryption failed.", Toast.LENGTH_LONG);
                    tvEncHex.setText("Encryption failed - result can be unexpected.");

        btDec.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try {
                    String enc_text = etEnc.getText().toString();
                    String dec_pass = etPass.getText().toString();
                    SecretKeySpec decKey = generateKey(dec_pass);

                    output = decrypt(enc_text, decKey);
                    tvOp.setText("Last operation: Decryption");
                } catch (Exception e) {
                    Toast.makeText(getApplicationContext(), "Decryption failed.", Toast.LENGTH_LONG);
                    Log.d("Decryption Error", e.getMessage());
                    tvEncHex.setText("Decryption failed - result can be unreadable. Try different password");


Testing, Results & Conclusion

Application runs as expected:

‘HFOC’ plain text with ‘abc’ key is encrypted:

With result: 8E EF 1F 06 5D FA F4 AC 2A 72 3A 48 8D E2 21 D6

And key: BA 78 16 BF 8F 01 CF EA 41 41 40 DE 5D AE 22 23 B0 03 61 A3 96 17 7A 9C B4 10 FF 61 F2 00 15 AD

Let’s doublecheck online:

AES Encryption on CyberChef GCHQ Github

Seems right 😊

Now, let’s check if 8E EF 1F 06 5D FA F4 AC 2A 72 3a 48 8D E2 21 D6 with ‘abc’ as key will decrypt correctly:

I think so!

Again, let’s use online tool:

AES Decryption on CyberChef GCHQ Github

Looks like your app is working as expected – we have basic PoC enciphering (and deciphering app), ready encryption, decryption and key generation functions.

As I mentioned in the introduction, it is the first but not last step for an exciting project, so bear in mind checking my blog from time to time. 😊

Source code for this project on my Github: HeadFullOfCiphers/AndroidEncryptionEntry

If you would like to reach out to me, do not hesitate and go to contact me!

This article is part of the #cryptology101 and #securenotepad series. Please check out related posts:

Refrence list:

  2. Cipher section – Android Development
  3. CyberChef GCHQ – Github

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at

Up ↑

%d bloggers like this: