mirror of
https://github.com/Noratrieb/website.git
synced 2026-01-14 17:05:02 +01:00
move submodules in-tree
This commit is contained in:
parent
57c4a239da
commit
830045ee2f
238 changed files with 41955 additions and 187 deletions
222
blog/content/posts/fake-openssh-keys/index.md
Normal file
222
blog/content/posts/fake-openssh-keys/index.md
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
+++
|
||||
title = "Having fun with OpenSSH private keys"
|
||||
date = "2024-09-13"
|
||||
author = "Noratrieb"
|
||||
tags = ["ssh"]
|
||||
keywords = ["SSH"]
|
||||
description = "An interactive way to have fun with OpenSSH private keys"
|
||||
showFullContent = false
|
||||
readingTime = true
|
||||
hideComments = false
|
||||
draft = false
|
||||
+++
|
||||
|
||||
you likely have an SSH private key.
|
||||
and unless you're doing something *seriously* wrong, only you have this key.
|
||||
that's the entire point, after all.
|
||||
|
||||
this private key is used for authenticating with an SSH server.
|
||||
you sign a message with your private key and the server then verifies it with the public key.
|
||||
this ensures that it's you who authenticated to the server, and not your friends or enemies.
|
||||
or me. don't let me into your servers.
|
||||
|
||||
but how are these keys encoded and can we have fun with them?
|
||||
let's use `ssh-keygen -t ed25519` to generate a test key and find out.
|
||||
|
||||
```
|
||||
$ ssh-keygen -t ed25519
|
||||
Generating public/private ed25519 key pair.
|
||||
Enter file in which to save the key (/home/nora/.ssh/id_ed25519): testkey
|
||||
Enter passphrase (empty for no passphrase):
|
||||
Enter same passphrase again:
|
||||
Your identification has been saved in testkey
|
||||
Your public key has been saved in testkey.pub
|
||||
The key fingerprint is:
|
||||
SHA256:IPrdC+4S0ZIzwS1oYN3A78Q29yV6gpDgiEkPwJtj0Wc nora@nixos
|
||||
The key's randomart image is:
|
||||
+--[ED25519 256]--+
|
||||
|=o++o. |
|
||||
|.*o++E. |
|
||||
|=oB B=. |
|
||||
|+* =*B.o . . |
|
||||
|. o ==+ S o |
|
||||
| ..+ + o |
|
||||
| ..o + |
|
||||
| .. . . |
|
||||
| oo . |
|
||||
+----[SHA256]-----+
|
||||
```
|
||||
|
||||
this command has created two files, `testkey` and `testkey.pub`.
|
||||
|
||||
`testkey.pub` contains the public key and looks like this. if you're following along at home it probably looks different.
|
||||
hopefully. unless you have gotten very lucky, which would be terrible and the downfall of the entire cryptographic ecosystem.
|
||||
|
||||
```
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEc5o2i/B1bVs7X2dJjE48l7fqAyMdgrbAItrO8XWwP9 nora@nixos
|
||||
```
|
||||
|
||||
the public key starts with the key type, ed25519.
|
||||
Ed25519 is a signature algorithm that is commonly used for modern SSH keys.
|
||||
it's also the default for modern OpenSSH versions, so passing that `-t` flag was unnecessary.
|
||||
other common algorithms are ECDSA (`ecdsa-sha2-nistp256`) and RSA (`ssh-rsa`).
|
||||
unless you need compatiblity with ancient servers or are bound by outdated regulation, you probably don't need either of those.
|
||||
|
||||
after the key type, we have a base64 encoded blob of the "wire encoding" of the key.
|
||||
this encoding is [standardized](https://datatracker.ietf.org/doc/html/rfc8709) and is sent by the client to the server every time it wants to authenticate, to choose which key to use.
|
||||
the exact details vary by key type but for Ed25519, it contains the following:
|
||||
|
||||
|bytes|meaning|
|
||||
------|-------|
|
||||
|`0000 000b` | name length, 11 |
|
||||
|`7373 682d 6564 3235 3531 39` | ssh-ed25519 |
|
||||
|`0000 0020` | encoded Ed25519 public key length, always 32 |
|
||||
|`4739 a368 bf07 56d5 b3b5 f674 98c4 e3c9 7b7e a032 31d8 2b6c 022d acef 175b 03fd` | encoded Ed25519 public key |
|
||||
|
||||
the last part is the comment. it's automatically set to my username and my hostname (i use nixos btw) and can be set to anything with the `-C` parameter.
|
||||
it's supposed to help us figure out what the key is.
|
||||
|
||||
the public key is fairly boring, so we're gonna take a look at the exciting private key instead.
|
||||
|
||||
```
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACBHOaNovwdW1bO19nSYxOPJe36gMjHYK2wCLazvF1sD/QAAAJCFCe+ShQnv
|
||||
kgAAAAtzc2gtZWQyNTUxOQAAACBHOaNovwdW1bO19nSYxOPJe36gMjHYK2wCLazvF1sD/Q
|
||||
AAAEDmrbLtUasQVBfkJV0ILoxDox64ngUwOASQbc8N0oZzNEc5o2i/B1bVs7X2dJjE48l7
|
||||
fqAyMdgrbAItrO8XWwP9AAAACm5vcmFAbml4b3MBAgM=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
```
|
||||
|
||||
it goes without saying but never share your private key on the internet and this is obviously just a test key!
|
||||
|
||||
the entire key is base64-encoded in the [PEM](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail) format.
|
||||
this makes it easier to copy around compared to raw bytes. not that you're supposed to copy it to random places.
|
||||
|
||||
an OpenSSH private key consists of two areas:
|
||||
- a plaintext area with the public key
|
||||
- a potentially-encrypted area with the private key
|
||||
|
||||
most strings are length-prefixed, i'm not gonna mention the length explicitly for many of the cases here.
|
||||
if it starts with 3 null bytes, the first 4 bytes are probably the length.
|
||||
for an `ssh-ed25519` key, the format looks like this:
|
||||
|
||||
|bytes|meaning|
|
||||
------|-------|
|
||||
`6f70 656e 7373 682d 6b65 792d 7631 00` | openssh-key-v1 (null-terminated) |
|
||||
`0000 0004 6e6f 6e65` | cipher, `none` in this case (`aes256-ctr` is common for encrypted keys) |
|
||||
`0000 0004 6e6f 6e65` | key derivation function, `none` in this case (`bcrypt` is common for encrypted keys) |
|
||||
`0000 0000` | key derivation options, empty here (contains the salt and cost for `bcrypt`) |
|
||||
`0000 0001` | amount of keys, 1 (yes, it could contain multiple) |
|
||||
a bunch of bytes | the full public key, as seen previously
|
||||
`0000 0090` | the length of the encrypted part. the rest is encrypted with the previously mentioned cipher and a password |
|
||||
`8509 ef92 8509 ef92` | two identical 4-byte sequences, to check if decryption was successful |
|
||||
`0000 000b 7373 682d 6564 3235 3531 39` | ssh-ed25519, the algorithm of the first key (which might seem familiar) |
|
||||
`0000 0020 4739 a368 bf07 56d5 b3b5 f674 98c4 e3c9 7b7e a0323 1d8 2b6c 022d acef 175b 03fd` | the raw encoded public key bytes |
|
||||
`0000 0040` | the length of the next part, which contains the... |
|
||||
`e6ad b2ed 51ab 1054 17e4 255d 082e 8c43 a31e b89e 0530 3804 906d cf0d d286 7334` | ...raw private key bytes... |
|
||||
`4739 a368 bf07 56d5 b3b5 f674 98c4 e3c9 7b7e a0323 1d8 2b6c 022d acef 175b 03fd` | ...and the public key bytes. AGAIN. YES.
|
||||
|
||||
the unencrypted public area makes it easy to check which public key a private key belongs to without needing to enter a password to decrypt it.
|
||||
the encrypted area makes sure that even if someone manages to steal your private key, they can't use it unless they know your password.
|
||||
unless you haven't set a password of course. which is why you should set a password for your private key.
|
||||
|
||||
having the public key bytes in there THREE TIMES seems very silly. but the fact that the public key is in there at all is useful.
|
||||
|
||||
maybe you've been in a situation where you've needed to find the public key file of a private key you had around, and just couldn't find it.
|
||||
but as I just mentioned, you don't actually need the `.pub` file for that, as the public key is contained in the private key.
|
||||
`ssh-keygen` can even extract it for you with `-y`!
|
||||
|
||||
```
|
||||
$ ssh-keygen -y -f testkey
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEc5o2i/B1bVs7X2dJjE48l7fqAyMdgrbAItrO8XWwP9 nora@nixos
|
||||
```
|
||||
|
||||
i have a public key.
|
||||
You can find it on <https://github.com/Noratrieb.keys> (this works for any GitHub user that has uploaded SSH keys!) and at the time of writing, it was
|
||||
```
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG0n1ikUG9rYqobh7WpAyXrqZqxQoQ2zNJrFPj12gTpP
|
||||
```
|
||||
|
||||
but you don't care about this, do you? you really want my private key. i know it.
|
||||
well, here it is:
|
||||
```
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz
|
||||
c2gtZWQyNTUxOQAAACBtJ9YpFBva2KqG4e1qQMl66masUKENszSaxT49doE6TwAA
|
||||
AIgQ5LRcEOS0XAAAAAtzc2gtZWQyNTUxOQAAACBtJ9YpFBva2KqG4e1qQMl66mas
|
||||
UKENszSaxT49doE6TwAAAEAoBWfFwPJSZQxTNETJRn40Y2XFP2GbW1aAGX+SzP/o
|
||||
rG0n1ikUG9rYqobh7WpAyXrqZqxQoQ2zNJrFPj12gTpPAAAAAAECAwQF
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
```
|
||||
|
||||
don't believe me? check it yourself!
|
||||
|
||||
```
|
||||
$ ssh-keygen -y -f the-just-posted-public-key
|
||||
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG0n1ikUG9rYqobh7WpAyXrqZqxQoQ2zNJrFPj12gTpP
|
||||
```
|
||||
|
||||
it's true! you indeed have my private key! don't do bad things with it, please.
|
||||
|
||||
well, you probably won't believe me. you know how SSH private keys and `ssh-keygen -y` works,
|
||||
and you know that the private key i posted above is just a random private key with my public key put into the public key part.
|
||||
and you're right. good job!
|
||||
|
||||
but maybe your friends don't know that. or your enemies.
|
||||
posting "your public key" may confuse them and is fun... and we're here for fun!
|
||||
|
||||
you can use the generator below to generate a fake private key for a public key.
|
||||
it only supports `ssh-ed25519` and `ecdsa-sha2-nistp256`.
|
||||
[no `ssh-rsa`, sorry](https://blog.trailofbits.com/2019/07/08/fuck-rsa/).
|
||||
if you have an RSA key, get a better key first.
|
||||
the implementation is based on [cluelessh](https://github.com/Noratrieb/cluelessh), my own SSH toolkit, compiled to WebAssembly.
|
||||
|
||||
## generator
|
||||
|
||||
<label for="public-key-input">public Key</label>
|
||||
<br>
|
||||
|
||||
<textarea id="public-key-input" rows="10" cols="29"></textarea>
|
||||
<button id="convert-button" style="margin-left: 10px;">Generate</button>
|
||||
<div id="public-key-error"></div>
|
||||
|
||||
<label for="fake-key-output">fake private key</label>
|
||||
<textarea id="fake-key-output" rows="10" disabled></textarea>
|
||||
<style>
|
||||
#fake-key-output { width: 90vw; }
|
||||
@media (min-width: 600px) {
|
||||
#fake-key-output { width: 30em; }
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
#fake-key-output { width: 50em; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="module">
|
||||
import init, { generate_fake } from "./fake_openssh_key.js"
|
||||
|
||||
init({
|
||||
module_or_path: new URL('fake_openssh_key_bg.wasm', import.meta.url)
|
||||
});
|
||||
|
||||
const input = document.getElementById("public-key-input");
|
||||
const output = document.getElementById("fake-key-output");
|
||||
const button = document.getElementById("convert-button");
|
||||
const error = document.getElementById("public-key-error");
|
||||
|
||||
button.addEventListener("click", () => {
|
||||
const key = input.value;
|
||||
error.innerText = "loading";
|
||||
try {
|
||||
const result = generate_fake(key);
|
||||
output.value = result;
|
||||
error.innerText = "";
|
||||
} catch(e) {
|
||||
console.log(key);
|
||||
error.innerText = `error: ${e}`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
what are these SSH keys actually used for? SSH of course. but how? oh do i have a blog post for you:
|
||||
Loading…
Add table
Add a link
Reference in a new issue