XOR cipher
XORs your text with a repeating key and shows the result as hex: “Hello” with the key “key” becomes 230015070a, and the same key turns it back into text. Built for puzzles, CTF practice and lessons, not for protecting secrets. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Text
- Hello
- Key
- key
- Calculation mode
- Encrypt
- Result
- 230015070a
- Key length
- 3
- Bytes
- 5
The XOR cipher is a classical repeating-key stream cipher that combines each data byte with a key byte using the exclusive-or operation. Because XOR is its own inverse, the same key that scrambles a message also restores it when applied again. Decrypt mode accepts that hex, reapplies the key stream, and returns UTF-8 text when the key matches. People use repeating-key XOR for CTF warm-ups, classroom demos of stream ciphers, puzzle books, and light obfuscation when modern authenticated encryption would be overkill.
Validation rules, empty keys, and common pitfalls
Invalid mode strings outside encrypt, decrypt, encode, and decode fail the same way. Do not treat the output as encryption for confidentiality: repeating-key XOR is vulnerable to known-plaintext attacks, crib dragging, and frequency analysis when the key is short relative to the message, and the product page should never market this tool as a security control for passwords, tokens, or personal data.
Prefer this landing when the search intent is xor cipher, repeating-key XOR, or XOR encrypt decrypt hex; Atbash and ROT13 answer fixed alphabet substitution, rail fence answers transposition, Base64 and hex-text answer encoding without a key, so they stay separate tools. When decrypting, use the exact same key string that produced the ciphertext; a wrong key either fails UTF-8 validation or yields gibberish that will not match the original plaintext.
Classroom uses and practical limits
Call the free in-browser XOR cipher for homework checks, lecture slides, CTF practice, and one-off encrypt or decrypt runs without writing a keystream loop in every language. A typical classroom workflow shows that a one-byte key is a simple Caesar-like stream over bytes, then lengthens the key and demonstrates that ciphertext length equals plaintext length in bytes while hex doubles the character count. Support playbooks can reverse lightly obfuscated notes that staff wrote with a known shared key.
Puzzle sites can precompute hex answers for display and reveal them on demand with mode decrypt. Pair with modern crypto libraries only when a later step needs authenticated encryption—keep XOR on its own landing so SEO intent stays sharp around classical repeating-key exclusive-or.
What you can do with it
CTF and puzzle XOR warm-ups
Hide short flags or clues under a known repeating key, publish the hex, then decrypt with the same key when solvers need the reveal without installing crypto libraries.
FAQ
How do I encrypt text with the XOR cipher?
The response result field holds lowercase hexadecimal ciphertext.
How do I decrypt XOR hex ciphertext?
Send the hex string as text, the same key used to encrypt, and mode decrypt (or decode). The result is the reconstructed UTF-8 plaintext when the key matches.
What happens if the key is empty?
A non-empty key is required so the repeating keystream is well defined.
Is the XOR cipher tool free?
The browser tool runs free on your device.
Is repeating-key XOR secure encryption?
No. It is a classical teaching and puzzle cipher, not confidentiality for secrets. Never use it for passwords or sensitive production data; use modern authenticated encryption instead.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/enc/xor \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Hello","key":"key"}'const res = await fetch("https://api.kit.forhosting.com/enc/xor", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"text": "Hello",
"key": "key"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/enc/xor",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"text": "Hello",
"key": "key"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/enc/xor", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"text":"Hello","key":"key"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"text":"Hello","key":"key"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/enc/xor", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"text": "Hello",
"key": "key"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "enc.xor",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
This capability encodes plain text as UTF-8, cycles the key bytes across the payload, and presents ciphertext as compact lowercase hexadecimal so binary results stay copy-safe. Enter non-empty text, a non-empty key, and an optional encrypt or decrypt mode; empty keys fail with invalid_input.
How repeating-key XOR works and what the response contains
Encryption first converts your message to UTF-8 bytes so multi-byte characters are handled the same way in every isolate. The key string is also UTF-8 encoded; those key bytes form a stream that repeats from the start whenever the message is longer than the key. Each data byte is exclusive-ored with the key byte at position index modulo key length, producing a binary ciphertext of equal length. Decryption reverses the presentation: the text field must be hexadecimal (optional spaces, colons, dashes, or 0x prefixes are stripped), the same key stream is applied, and the resulting bytes are decoded as UTF-8 text. Acceptable mode values are encrypt, encode, decrypt, and decode; encode maps to encrypt and decode maps to decrypt. Send JSON with required string field text set to the plain message or hex ciphertext, required string field key set to any non-empty passphrase, and optional mode defaulting to encrypt. Aliases input, value, s, message, and ciphertext work for the body; password, secret, k, and passphrase alias the key; op, action, and direction alias mode. On success the response includes result with the hex or plain string, mode normalized to encrypt or decrypt, key_length equal to the UTF-8 byte length of the key, bytes equal to the number of data bytes processed, and method equal to xor so clients can branch without sniffing shapes. Identical inputs always produce identical JSON because the path is pure byte arithmetic with no network, no random seed, and no wall clock.
Validation rules, empty keys, and common pitfalls
Missing text, non-string text, or an empty string all raise invalid_input and are not charged on the prepaid API after the engine rejects the request. Missing key, non-string key, or an empty key also fail with invalid_input; the product promise is a real repeating key stream, and zero key bytes would leave the data unchanged or undefined as a cipher. In decrypt mode, odd-length hex after stripping separators, non-hex characters, or decrypted bytes that are not valid UTF-8 also raise invalid_input so clients never receive silently corrupted strings.
API usage, pricing, classroom workflows, and practical limits
Use the prepaid KIT API when a backend, serverless function, tutoring platform, or SDK needs a stable contract under balance at $0.002 per successful item; validation failures never consume balance after rejection. Authors shipping SDKs can map the result field straight into a copy button while mode, key_length, bytes, and method make OpenAPI examples self-explanatory. Keep payloads to ordinary text sizes appropriate for a Worker isolate item call; this tool is for messages and snippets, not multi-megabyte binary blobs.
Integration use cases
Classroom stream-cipher demos
Teachers show that XOR is its own inverse, contrast hex ciphertext with plain text, and pin Hello with key key as a textbook fixture students can recompute by hand.
Pipeline and CI fixtures
Scripts and continuous-integration tests pin deterministic XOR hex outputs and the empty-key error so accidental keystream or hex formatting bugs fail fast.