Search code examples
encryptioncryptographyrsassh-keysssh-keygen

Why the content of id_rsa and id_rsa.pub files are letters?


I am trying to implement RSA algorithm and as per the algorithm public key and private keys are based on the very large numbers and the resulting, private key and public key are also a number.

For instance to generate the private key and public key

let's choose

p = 7
q = 19

n = p * q = 133
m = (p-1) * (q-1) = 108

e = 5 
d = (1 + i * m) /e
   for i = 0, 1, 2 .. n 
d = 65

Here we get Public key pair (n, e) = (133, 5) Private key pair (n, d) = (133,65)

Encryption: p^e%n Decryption: c^d%n

But. As I know the public key and private key generate by ssh-keygen command is id_rsa and id_rsa.pub.

While inspecting the id_rsa and id_rsa.pub I found the letters instead of a very large number.

How theses id_rsa and id_rsa.pub are used to encrypt the data.


Solution

  • As mentioned in the comments, those files are encoded in base64 format. But if you ask, what is encoded in base64, that is where things start to go in the weeds. The primary motive of files is to store some numbers that are used in the RSA calculations. They are not meant for humans to be read since it's not space friendly. They are meant for machine to process, and fast, so it has to be written in a format that machines can easily load and start using.

    If you look at a standard called JWK or JSON Web Key, you will see how the keys are passed around among applications. eg: A private key

    {
        "kty": "EC",
        "d": "u5uui9SeGmkorTrbXtOGWAaov1cLCZXt3kleSxzH8T0",
        "use": "sig",
        "crv": "P-256",
        "x": "a3JvmNVFxue8gyAdtH_Pca87CKjh82j7mMRzHhS3_IA",
        "y": "JdEC03zi2IatFVjrp1o4ear32gBe4E0xiFf_EDt8unM",
        "alg": "ES256"
    }
    

    and a public key:

    {
        "kty": "EC",
        "use": "sig",
        "crv": "P-256",
        "x": "a3JvmNVFxue8gyAdtH_Pca87CKjh82j7mMRzHhS3_IA",
        "y": "JdEC03zi2IatFVjrp1o4ear32gBe4E0xiFf_EDt8unM",
        "alg": "ES256"
    }
    

    These might a more human readable form, but if you observe, they are in a serialized string format. They will have to go from string to JSON to field extraction and then convert them to objects which can then be used.

    The files that are generated by openssl and openssh are more in the format closer to what machines like. I would recommend to use openssl to play around with openssh since they are much more wide spread. There is a spec, x509, which the world uses for cryptography to share keys around. Openssh is built on a bit different spec and they keys generated are written in a different format.

    If you want to see the big numbers, there are 2 ways:

    But first, lets generate a key to play around with. We will generate a 512-bit key just to keep it small.

    openssl genrsa -out key.pem 512
    
    1. Easy way, ask a tool to help: Given that you have the above file with you, you can ask openssl to parse it dump the info for you.
    $ openssl rsa -in key.pem -noout -text
    RSA Private-Key: (512 bit, 2 primes)
    modulus:
        00:a9:...
    publicExponent: 65537 (0x10001)
    privateExponent:
        00:8c:...
    prime1:
        00:da:...
    prime2:
        00:c6:...
    exponent1:
        4e:0d:...
    exponent2:
        76:66:...
    coefficient:
        00:92:...
    

    The numbers are ellipsis-ed just to keep it small. Another way to look at this is:

    $ openssl asn1parse -in key.pem -inform PEM 
    

    You will see the output with the huge numbers that you are expecting.

    1. The hard way: These numbers are packed in format called ASN1. If you read the binary in the format as encoded, you will be able to see the numbers in the binaries too.

    But first, need to convert the openssh keys into the required format, called the PEM format.

    ssh-keygen -f id_rsa.pub -e -m pem > pubkey.pem
    

    Now, we can do a openssl asn1parse -in pubkey.pem -inform PEM and see the details.

    $ ssh-keygen -f id_rsa.pub -e -m pem | openssl asn1parse -inform PEM
        0:d=0  hl=4 l= 266 cons: SEQUENCE          
        4:d=1  hl=4 l= 257 prim: INTEGER           :EB3F998DBAE8F8AFF59FE51F2A1BCE36D76F71D0DD76FD92B77CB2FFADAF9B7F3EA77FEA40590D7C1BFEEB7DA7F72E780D79784A778761980DA7FE4C320BA513A4849929F92A2185305C379A125080C7CAFA37C53D38AD447A895EC5E3BEC77F323CB818D90F5F8071566A7618ADEA94A4FF472E21BDF782197A07DADB6DEFD6FE27D759775BEC3AFEAF973F861FD5F3A8CB1177304206A79DAFC961F7E4792E76732589BD219742F73630364C3724D5FCE3B0DC1EDD3E498549EE74ED17157E333883FAC498C8EE75F69700E2385A510BF705DE4DD5599806F47F2DDD6EA71CD0ADF50C9B943A30E2A8B6C086699A59413195CC4CA846B40460F767F4D40AE3
      265:d=1  hl=2 l=   3 prim: INTEGER           :010001
    

    Please read this answer to understand how these numbers are encoded into the PEM format: https://stackoverflow.com/a/59235177/6266958