Skip to content

Instantly share code, notes, and snippets.

@b0bu
Last active July 23, 2021 22:56
Show Gist options
  • Save b0bu/d5cbacc63cd5605322010cde9dfcafb4 to your computer and use it in GitHub Desktop.
Save b0bu/d5cbacc63cd5605322010cde9dfcafb4 to your computer and use it in GitHub Desktop.
cryptography part 1

c, m, e, n and d

Once you have a mathematically linked key pair understanding the relationship doesn't start with CAs or root trust trees or ascii armoured blobs, that's essential and usually a person's first and only experience with certificates. It starts by understanding that a key pair's purpose is primarily for one to reverse the other. PKI specifically uses this property of asymmetric behaviour (typically rsa) to generate and establish a secure symmetrically encrypted session (typically aes). The key pair such as they are are wrapped up a notion of standards of trust, issuance and revocation but it's the numbers within that are the vehicle to achieve a symmetrically encrypted session. And it starts with the letters c, m, e, n and d.

enciphering

Cipher text c is generated from a function f(x) that takes m the message data to be enciphered and the public key or (modulus, exponent) which we'll call (n, e) from here on out. n and e are what's called "public numbers". Key pairs are mathematically coupled and made up of public and private numbers.

c = f(m, n, e)

By loading a private key into a python interpreter we can see these numbers on the private key (no public key has been generated)

from OpenSSL import crypto
    with open("some_key.pem", "r") as some_key:
        private_key = crypto.load_privatekey(crypto.FILETYPE_PEM, some_key.read(), b'some_password')
        exponent_as_integer = private_key.to_cryptography_key().private_numbers().public_numbers.e
        modulus_as_integer = private_key.to_cryptography_key().private_numbers().public_numbers.n

public_numbers.n and e, returns a base16 encoded int of the ans.1 encoded pem's modulus and exponent respectively. i.e. 241,928,393,982,963... etc

# truncated for brevity
241928393982963904582881279157253491953...

When you dump a pem with openssl the modulus is in colon separated hex format and is prefixed with "00", so 00 + hex_number is what you see. We can verify our returned int is the modulus by pulling the hex modulus out of the key and converting it to base16. Here's how to see that in bash and python.

The return from bc should match the number above taken from public_numbers.n

n=$(openssl rsa -in account_key.pem -check -noout -modulus)
printf "ibase=16;%s\n" $(echo ${n: 8} | awk "NR==1") | bc

And in python

>>> n = public_numbers.n
>>> n
# truncated for brevity
241928393982963904582881279157253491953...
# the hex output matches the openssl output minus the ':'
>>> h = hex(n)
# truncated for brevity
'0xbfa4ea8430688600054223f37c421d98495eac8...'
# We can convert this hex number back into an int object as returned by crypto, just proving that it's base16
>>> int(h, 16)
# truncated for brevity
241928393982963904582881279157253491953...

This detail might seem irrelevant but it won't be if you ever need to base64 encode an int(n) or e.

deciphering

The original message data deciphered using the private key (n, d).

m = f(c, n, d) 

private_number().d again returns a base16 encoded int of the ans.1 encoded pem.

private_key.to_cryptography_key().private_numbers().d
# truncated for brevity
1189578786960192231134069185474234978...

n is common to both the public and private key, the mathematical relationship between n e and d is the secure part. A message or data stream can be both enciphered with the public key (n, e) or the private key (n, d) but if you encipher with (n, d) it can only be diciphered by (n, e) and vice versa, this is the asymmetic portion of public key cryptography, server and client certificate behaviour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment