Case study: Regular Expressions

Extracting Private and Public SSH Keys from a string.

January 18, 2022 | Not my art.

Let’s say you have a string that contains both a private SSH key, and the public key, both in PEM format. How do you separate them from each other?

const content =
"Private Key:-----BEGIN OPENSSH PRIVATE KEY-----
YourPrivateSSHKeyYourPrivateSSHKeyYourPrivateSSHKey
YourPrivateSSHKeyYourPrivateSSHKeyYourPrivateSSHKey
YourPrivateSSHKeyYourPrivateSSHKeyYourPrivateSSHKey
YourPrivateSSHKeyYourPrivateSSHKeyYourPrivateSSHKey
YourPrivateSSHKeyYourPrivateSSHKeyYourPrivateSSHKey
YourPrivateSSHKeyYourPrivateSSHKeyYourPrivateSSHKey
YourPrivateSSHKeyYourPrivateSSHKeyYourPrivateSSHKey
YourPrivateSSHKeyYourPrivateSSHKeyYourPrivateSSHKey
-----END OPENSSH PRIVATE KEY-----
Public Key:-----BEGIN PUBLIC KEY-----
YourPublicSSHKeyYourPublicSSHKeyYourPublicSSHKey
YourPublicSSHKeyYourPublicSSHKeyYourPublicSSHKey
YourPublicSSHKeyYourPublicSSHKeyYourPublicSSHKey
YourPublicSSHKeyYourPublicSSHKeyYourPublicSSHKey
YourPublicSSHKeyYourPublicSSHKeyYourPublicSSHKey
-----END PUBLIC KEY-----"

One way is to use a regular expression which specifically targets their format. Private keys are almost always contained within a header and footer.

const privateSSHKey =
  /-----BEGIN(.+)PRIVATE KEY(.+)(\n.+)*END(.+)PRIVATE KEY-----/.exec(content);
const publicSSHKey =
  /-----BEGIN(.+)PUBLIC KEY(.+)(\n.+)*END(.+)PUBLIC KEY-----/.exec(content);

This can be cleaned up a bit:

const privateSSHKey =
  /-{5}BEGIN(.+)PRIVATE KEY(.+)(\n.+)*END(.+)PRIVATE KEY-{5}/.exec(content);
const publicSSHKey =
  /-{5}BEGIN(.+)PUBLIC KEY(.+)(\n.+)*END(.+)PUBLIC KEY-{5}/.exec(content);

Depending on the format the header and footer may have a varied string:

------BEGIN OPENSSH PRIVATE KEY------

------END OPENSSH PRIVATE KEY------
------BEGIN RSA PRIVATE KEY------

------END RSA PRIVATE KEY------
------BEGIN PRIVATE KEY------

------END PRIVATE KEY------
------BEGIN OPENSSH PUBLIC KEY------

------END OPENSSH PUBLIC KEY------
------BEGIN RSA PUBLIC KEY------

------END RSA PUBLIC KEY------
------BEGIN PUBLIC KEY------

------END PUBLIC KEY------

This is why we need to include (.+) between BEGIN and PRIVATE KEY .

What if the public key was in the ssh-rsa format?

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC7jdirkf4o6 ...

You could do:

/ssh-rsa(.+)==/;

The values can then be assigned to a key/value pair within an object, for example:

{
	privateKey: privateSSHKey,
	publicKey: publicSSHKey
}

Resources:

https://regex101.com/

https://www.thedigitalcatonline.com/blog/2018/04/25/rsa-keys/