Skip to content

Protect Passwords, Tokens, Requests, and Updates with the Crypto Module

The Crypto module covers several different jobs. The hard part is usually not finding a function; it is choosing the right one for the problem in front of you.

Which function should you use when your app needs to:

  • remember a user’s password,
  • save an API token in a local settings file,
  • check that an API request was not changed, or
  • verify that an update came from you?
The examples below answer those questions with small, focused scenarios. They show where each function fits. They are not complete security products.

A good starting point is:

  • Use PBKDF2 when you need to check a password later.
  • Use AES when you need to hide data that your app must read again.
  • Use HMAC when two trusted systems share a secret and need to detect changed messages.
  • Use Ed25519 when one side signs data and other parties only need to verify it.

Example 1: Remembering a user’s password with PBKDF2

Imagine a desktop or web app with a login screen. When a user creates an account, you need to save something that lets you check the password later.

Do not save this: correct horse battery staple

If somebody gets the database, they immediately have the user’s password. A plain SHA-256 hash is not a good replacement because attackers can calculate fast hashes very quickly and try large lists of common passwords. PBKDF2 is designed for this job. It combines the password with a random salt and repeats the calculation many times.

Var password As String = "correct horse battery staple"
Var salt As String = EncodeHex(Crypto.GenerateRandomBytes(16))
Var iterations As Integer = 100000
Var derived As MemoryBlock = Crypto.PBKDF2(salt, password, iterations, 32, Crypto.HashAlgorithms.SHA2_512)
Var storedHash As String = EncodeHex(derived)

Save saltiterations, the algorithm name, and storedHash in the user’s record. The salt does not need to be secret. You need it again when the user logs in.

Var suppliedPassword As String = "correct horse battery staple"
Var calculated As MemoryBlock = Crypto.PBKDF2(salt, suppliedPassword, iterations, 32, Crypto.HashAlgorithms.SHA2_512)
Var calculatedHash As String = EncodeHex(calculated)

If calculatedHash = storedHash Then
  MessageBox("Password accepted")
Else
  MessageBox("Password rejected")
End If

The app never needs to recover the original password. It calculates the value again and compares it with the stored one.

The iteration count in this example is only an example. Test it on the slowest computer you support. Store the count with the hash so you can increase it later and update old records after a successful login.

Use Crypto.GenerateRandomBytes here because the salt must not be predictable. The same function is useful when you need a random token, nonce, or initialization vector.

Example 2: Saving an API token in an encrypted settings file

Now imagine a desktop app that lets a user connect to an online service. After the user enters an API token, the app should remember it without saving readable text to its settings file.

This is a job for symmetric encryption. The app uses the same secret key to encrypt and decrypt the value.

AES accepts keys that are 16, 24, or 32 bytes long. The initialization vector is 16 bytes for AES. This example uses a 32-byte key and a new 16-byte IV:

Var secretKey As String = Crypto.GenerateRandomBytes(32)
Var initializationVector As MemoryBlock = Crypto.GenerateRandomBytes(16)
Var apiToken As String = "token-from-the-service"
Var encryptedToken As MemoryBlock = Crypto.AESEncrypt(secretKey, apiToken, Crypto.BlockModes.CBC, initializationVector)

To read the token later:

Var decryptedToken As MemoryBlock = Crypto.AESDecrypt(secretKey, encryptedToken, Crypto.BlockModes.CBC, initializationVector)
Var apiTokenAgain As String = decryptedToken

The IV is not a password and does not need to be secret. Save it with the encrypted value. You need the same IV for decryption.

The key is the difficult part. If you generate a new key every time the app starts, the app cannot decrypt yesterday’s settings. A real application must store the key in a suitable secret store, derive it from a user-provided secret, or keep the sensitive value on a server. Do not put a production key directly in the source code.

The encrypted record might contain these fields:

version
iv
ciphertext

Store the IV and ciphertext as Base64 or hexadecimal text if the settings format cannot hold binary data. Store a version so you can change the format later.

CBC encryption hides the token, but it does not prove that the ciphertext was not changed. For real data, authenticate the encrypted record as well. With CBC, that means using an encrypt-then-MAC design with a separate MAC key. Another option is a vetted authenticated-encryption library or a service that provides an AEAD mode.

Do not reuse an IV with the same key, and do not use ECB mode for application data.

Example 3: Signing an API request with HMAC

Suppose you control both a client and a server application. The client sends an order to the server, and the server needs to know whether somebody changed the request while it was being sent.

The client and server can share a secret. The client signs the request with HMAC, and the server calculates the same HMAC. If the values differ, the server rejects the request.

Var sharedSecret As String = "replace-this-with-a-real-secret"
Var requestText As String = "POST|/api/orders|1714492800|{""id"":42}"
Var requestData As MemoryBlock = requestText
Var signature As MemoryBlock = Crypto.HMAC(sharedSecret, requestData, Crypto.HashAlgorithms.SHA2_256)
Var signatureText As String = EncodeBase64(signature)

Send requestText and signatureText to the server. The server must build exactly the same text before calculating its own signature. A different separator, line ending, or JSON layout produces a different result.

A signature by itself does not stop somebody from sending the same valid request again. Include a timestamp and reject requests that are too old. For an operation that must only happen once, include a unique request ID and record IDs that the server has already processed.

HMAC is a good fit for two backend services or a controlled client-server arrangement. It is a poor fit when you ship the shared secret inside an untrusted desktop or mobile app. A secret compiled into an app can eventually be extracted.

Example 4: Verifying an update with Ed25519

A different situation comes up when you distribute application updates. The update server should be able to sign a file, but the installed app should only need to verify it. The installed app should not possess a secret that could create fake signatures.

Use a public and private key pair:

  • Keep the private key on the signing machine.
  • Put the public key in the application or distribute it through a trusted channel.
Var privateKey As String
Var publicKey As String

If Crypto.ED25519GenerateKeyPair(privateKey, publicKey) Then
  Var updateInfo As String = "version=4.2.1|sha256=..."
  Var signature As MemoryBlock = Crypto.ED25519Sign(updateInfo, privateKey)

  If signature <> Nil And Crypto.ED25519VerifySignature(updateInfo, signature, publicKey) Then
    MessageBox("Update information is authentic")
  End If
End If

In a real updater, verify the signature before trusting the version or downloading the file. The signature does not prove that the update is safe by itself. It proves that the data matches the private key associated with the public key.

Keep the exact bytes stable. If the signing tool adds a line ending but the app removes it before verification, the signature will fail.

Which function solves which problem?

ProblemUseWhat you store or send
Check a password laterCrypto.PBKDF2Salt, iteration count, algorithm, derived value
Create an unpredictable salt or IVCrypto.GenerateRandomBytesThe salt or IV, when the operation needs it later
Hide data that your app must decryptCrypto.AESEncrypt and Crypto.AESDecryptKey, IV, and ciphertext, using a safe storage design
Detect changes to a shared-secret requestCrypto.HMACMessage and HMAC value
Verify data signed by another systemCrypto.ED25519Sign and Crypto.ED25519VerifySignatureSignature and public key
Create a fixed fingerprint of public dataCrypto.HashThe expected hash from a trusted source

Start with the problem, not the function name. Ask what the other side needs to do:

  • Does it need to check a password? Use PBKDF2.
  • Does it need to read the original data? Use encryption.
  • Does it need to detect a changed message and share a secret with the sender? Use HMAC.
  • Does it need to verify a message without being able to create signatures? Use a public-key signature.

A short safety checklist

  1. Never store plaintext passwords.
  2. Do not use a fast general-purpose hash as a password-storage scheme.
  3. Do not hard-code production secrets in source code or a desktop application.
  4. Generate salts and IVs with Crypto.GenerateRandomBytes.
  5. Use SHA2_256 or stronger for new hashes and HMACs. Avoid MD5 and SHA-1 for new security designs.
  6. Do not treat AES-CBC ciphertext as authenticated unless you add an integrity check.
  7. Keep the exact bytes being signed stable.
  8. Test wrong passwords, wrong keys, changed messages, replayed requests, and corrupted ciphertext.
  9. Check the Xojo compatibility notes for every algorithm you choose.
  10. Use an established format or library instead of inventing a cryptographic protocol.

The Crypto module gives you the operations. Your application still has to decide where keys live, what gets signed, how data is formatted, and how keys are replaced. Those decisions determine whether the example can grow into something you can support.

Gabriel is a digital marketing enthusiast who loves coding with Xojo to create cool software tools for any platform. He is always eager to learn and share new ideas!