Skip to content

Latest commit

 

History

History
1436 lines (1289 loc) · 65 KB

README.org

File metadata and controls

1436 lines (1289 loc) · 65 KB

#

lokey

lokey is a tool that makes it easy to work with and convert between cryptographic key formats.

Named after the shape shifting and mischief-making Trickster from Norse mythology, this tool serves to test the bounds of possibilities and order.

Installing lokey

The fastest way to get lokey is to use pip:

$ pip install lokey

Using lokey

lokey makes it easy to fetch cryptographic keys from public sources like Keybase and PGP keyservers, for example:

Fetch my Keybase key using my Twitter handle:

$ lokey fetch keybase twitter:jf

Search for PGP keys from journalists at the New York Times:

$ lokey fetch pgp nytimes.com

You can see all of the fetching commands that lokey has by typing:

$ lokey fetch

lokey is also a tool for converting between cryptographic key formats, for example

Make it easier to grant your friends access to your SSH server:

$ sudo adduser jf
$ mkdir ~jf/.ssh
$ lokey fetch keybase twitter:jf | lokey to ssh > ~jf/.ssh/authorized_keys

Send an S/MIME encrypted email to an administrator of a TLS secured webserver:

$ echo "A ship journeys from the east, Muspell's people are coming" > message.txt
$ lokey fetch tls example.com > example.com.pem
$ openssl smime -encrypt -des3 -in message.txt example.com.pem > smime.p7m

One of the administrators of the TLS secured webserver could read the message using this command:

openssl smime -decrypt -in smime.p7m -passin pass:[password for key] -inkey /path/to/webserver.key

lokey will also give you information about keys that you pipe into it:

$ cat ~/.ssh/id_rsa.pub | lokey

At the moment, lokey can convert RSA keys between the following formats: openssh, X.509 Certificates, PGP, and JWT.

With lokey you can:

  • Use the public SSL certificate of a website to send an S/MIME encrypted email to any website owner that uses TLS:
  • Grant access to your a server by turning your friend’s keybase key into an openssh key

Inspiration

The genesis of this project comes from the many conversations I had with my dad about his implementation of OpenPGP in Common Lisp. One of those conversations was when I first learned that all RSA keys share the same set of numbers (“e”, “n”, “d”). My dad’s paper ”Why RSA Works” also gave me the grounding I needed to reason about and understand RSA cryptography.

Other inspirations include Zed Shaw’s vulnarb.com project, which was the first time I considered using TLS certificates for encrypting email. The Monkeysphere Project which inspired me to think of using the PGP web of trust for things other than email.

My work at Okta on key pinning introduced me to Python’s outstanding cryptographic library ”cryptography”. And most recently, my work on converting JWK formatted keys to PEM formatted keys proved how useful a command line utility for key conversion could be.

Learn more

$ lokey --help
$ lokey fetch
$ lokey to

What’s missing

This release works, but only barely so. I’d like to know if other people find it useful and have ideas for what they’d want to use this tool for.

That said, I already know of many things that will need to be fixed or corrected soon:

  1. I don’t know if the keys generated by this tool will actually work in The Real World.

    PGP and X.509 certificates in particular have a lot of options, I haven’t done any testing with software like Outlook, Mail.app, GPGTools (for Mac), iOS, etc. Please let me know what needs to be fixed here!

  2. I’d like lokey to be able to genereate X.509 keys that could be used for S/MIME and SSL “client authentication” (where a browser authenticates with a web server).

    I got a “proof of concept” working for this by turning lokey into a very simple “certificate authority”, this didn’t seem ideal though.

  3. I want to add support for all the various fingerprint formats in use.
  4. Passphrases for encrypted private keys shouldn’t be passed via a command line argument.

    This could expose your passphrase!

  5. I need to release my tests for “Eris” the class I use to convert keys.

    Before I do this, I need to make sure all the private keys are just test keys.

  6. Documentation. This should be a literate document.
  7. The text output for keys to match what you get with a tool like pgpdump or openssl x509 -text
  8. Support the various key stores for Java, macOS, GPG, etc
  9. I want to support more key formats (Pu
    • PuTTY
    • SSH Certificates
    • pkcs12?
    • DKIM?
  10. I have no idea how people use key bundles, or how lokey should support them

See also, FIXME notes in the source as well as my planned work in README.org

Work log

Proof of Concept

Playing around

Cool! I got PGP to OpenSSH working!

How do I read a key?

https://github.com/SecurityInnovation/PGPy/blob/f08afed730816e71eafa0dd59ce77d8859ce24b5/pgpy/pgp.py#L1087

“Making openssl generate deterministic key” http://stackoverflow.com/q/22759465

> You can’t, really

generate a certificate with openssl http://stackoverflow.com/a/10176685

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
$ gpg --gen-key
gpg (GnuPG/MacGPG2) 2.0.30; Copyright (C) 2015 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 
Requested keysize is 2048 bits   
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0) 1y
Key expires at Sun Feb  4 00:08:52 2018 PST
Is this correct? (y/N) y
                        
GnuPG needs to construct a user ID to identify your key.

Real name: Testing Key
Email address: testing.key@sargo.com
Comment: Testing                    
You selected this USER-ID:
    "Testing Key (Testing) <testing.key@sargo.com>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.    

We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 0E31BEDC marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2018-02-04
pub   2048R/0E31BEDC 2017-02-04 [expires: 2018-02-04]
      Key fingerprint = 809A 516D 890F E61E B4A5  CBA1 9DBF 138E 0E31 BEDC
uid       [ultimate] Testing Key (Testing) <testing.key@sargo.com>
sub   2048R/3A382837 2017-02-04 [expires: 2018-02-04]

https://github.com/mitchellrj/python-pgp

https://github.com/SecurityInnovation/PGPy

Research creating PGP key

Looks like I’ll just need to create a PGPKey, then a UID, then add the UID to the key

http://pythonhosted.org/PGPy/examples.html#keys

Find more types of keys

Download SSL certificates

echo -n | openssl s_client -connect google.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' 

Try to make a PGP public key

It worked!!!

Look at this!

import pgpy
from pgpy.packet.fields import RSAPub,MPI
from pgpy.packet.packets import PubKeyV4
from pgpy.constants import PubKeyAlgorithm

def custRSAPub(n,e):
    res = RSAPub()
    res.n = MPI(n)
    res.e = MPI(e)
    return res

def custPubKeyV4(custkey):
    res = PubKeyV4()
    res.pkalg = PubKeyAlgorithm.RSAEncryptOrSign
    res.keymaterial = custkey
    res.update_hlen()
    return res

def rsatogpg(e,N,name,**idargs):
    """
    :param e,N: RSA parameters as Python integers or longints
    :param name: Identity name
    :param idargs: PGP Identity parameters, such as comment,email
    :return: PGPy pubkey object
    """
    rsakey = custPubKeyV4(custRSAPub(N,e))
    pgpkey = pgpy.PGPKey()
    pgpkey._key = rsakey

    uid = pgpy.PGPUID.new(name, **idargs)
    uid._parent = pgpkey
    pgpkey._uids.append(uid)
    return pgpkey

http://explo.yt/post/2016/05/20/Parsing-OpenSSH-RSA-keys-in-Python

Try to extract the public key from a private key

Looks like the core stuff is in fields(1099) - I might just need to extend this class myself.Made a trace file:
python -m trace --trace app.py | tee trace.output.`date +%s`

Open that file in less and then search for “app.py”

I’m going to try creating a new key, then see what that makes

Next: Read each packet in PGP public key block and learn what each is. Study them, figure out how I should make one myself

pgpdump -ilmp pgpy-public-key.key 

Things that I tried:

  • Skimming more of the source code to see what I might be missing
  • Looking for a “new()” method for the PubKey or PubKeyV4 classes
  • Loading in a valid public key, but replacing “e” and “m”
https://github.com/SecurityInnovation/PGPy/blob/f08afed730816e71eafa0dd59ce77d8859ce24b5/pgpy/packet/fields.py#L387
Traceback (most recent call last):
 	File "app.py", line 62, in <module>
    compression=[CompressionAlgorithm.ZLIB, CompressionAlgorithm.BZ2, CompressionAlgorithm.ZIP, CompressionAlgorithm.Uncompressed])
 	File "/Users/joel/brew/lib/python2.7/site-packages/pgpy/pgp.py", line 1574, in add_uid
    uid |= self.certify(uid, SignatureType.Positive_Cert, **prefs)
 	File "/Users/joel/brew/lib/python2.7/site-packages/pgpy/decorators.py", line 125, in _action
    with self.usage(key, kwargs.get('user', None)) as _key:
 	File "/Users/joel/brew/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/contextlib.py", line 17, in __enter__
    return self.gen.next()
 	File "/Users/joel/brew/lib/python2.7/site-packages/pgpy/decorators.py", line 87, in usage
    em['keyid'] = key.fingerprint.keyid
 	File "/Users/joel/brew/lib/python2.7/site-packages/pgpy/pgp.py", line 1201, in fingerprint
    return self._key.fingerprint
 	File "/Users/joel/brew/lib/python2.7/site-packages/pgpy/packet/packets.py", line 794, in fingerprint
    plen = self.keymaterial.publen()
 	File "/Users/joel/brew/lib/python2.7/site-packages/pgpy/packet/fields.py", line 362, in publen
    return len(self)
 	File "/Users/joel/brew/lib/python2.7/site-packages/pgpy/packet/fields.py", line 352, in __len__
    return sum(len(getattr(self, i)) for i in self.__pubfields__)
 	File "/Users/joel/brew/lib/python2.7/site-packages/pgpy/packet/fields.py", line 352, in <genexpr>
    return sum(len(getattr(self, i)) for i in self.__pubfields__)
TypeError: object of type 'long' has no len()

Make function for PubKey to PGP key

I got basic tests working. I also read the “man” page for “ssh-keygen”, things I learned:

  • SSH has their own custom certificate format, looks cool
  • You can control how moduli are tested by ssh-keygen (how many “rounds” of tests are done)
  • This tool might want to add support for SSH tests?

Add support for x509 certificates

Spend time reading about other types of keys …

Found this: https://en.wikipedia.org/wiki/X.509#Major_protocols_and_standards_using_X.509_certificates

Accidently read more about Heartbleed

JWK

  • Find a JWK library first

to_jwt() is in the most recent version, not released yet!

mkdir upstream
cd upstream/
git clone 'https://github.com/jpadilla/pyjwt.git'
cd pyjwt/
python setup.py install

Refactor classes

  • “from_x”
  • “to_x”

To convert:

  • [X] openssh
  • [X] jwk
  • [X] pgp
  • [X] x509 pem

Think about new class structure

eris = ErisPublicNumbers()
# Assume we've loaded the SSH key from somehwere
data = ssh_key
eris.read(data)
# This will print out info on SSH key we've just loaded
eris.info()
# This will write it as a JWK
eris.to.jwk()
class ErisPublicNumbers (RSAPublicNumbers):
    def __init__(self):
        '''
        Base class for Eris Public Numbers
        '''
        # No command line tools to display this
        self.pretty_print_cmd = False
        self.name = "ErisPublicNumber"
        self.
     
     def serialize(self):
         pass
     
     def deserialize(self, data):
         pass

     def info(self):
         # Print out the key in some nice format
         if pretty_print_cmd:
             # pipe output through the command!
             pass
     
     def __str__(self):
         # Convert to string!
         pass

     def __repr__(self):
         return "{name} ({fingerprint})".format(
             name=self.name, 
             fingerprint=self.fingerprint())

     def fingerprint(self):
         pass
class JWT (ErisPublicNumbers):
    '''
    JWT (RFC 7519)
    '''
    def __init__(self):
        self.pretty_print_cmd = ['jq']
    
    def seralize(self):
        pass

    def deseralize(self, data):
        pass
class X509 (ErisPublicNumbers):
    '''
    X.509 creator
    '''
    def __init__(self):
        self.pretty_print_cmd = ['openssl', 'x509', '-text']
    
    def seralize(self):
        pass

    def deseralize(self, data):
        pass
class PGP (ErisPublicNumbers):
    '''
    PGP keys
    '''
    def __init__(self):
        self.pretty_print_cmd = ['pgpdump']
    
    def seralize(self):
        pass

    def deseralize(self, data):
        pass

Come up with command examples

cmd --help
cat key.jwk | lokey to openssh
cat key.jwk | lokey to pgp --name="Joel Franusic" --email="joel@franusic.com"
lokey fetch jwk example.okta.com --kid=1b3c5 | lokey to openssh
lokey fetch pgp joel@franusic.com
lokey fetch x509 joel.franusic.com
lokey fetch github jpf
lokey fetch keybase jfranusic

Write load() method

Think about x509 and PGP input some more

Command line libraries

Going to take a look at HHGTP for a bit too

Make command line POC

Awww yiss

I’ll use the name “Eris”Tell me what kind of key we got and print out “e” and “n”

Figure out what I need to do next

Look into how people think about RSA keys

Figure out how hard it will be to use the Keybase api

… trivial: https://github.com/ianchesal/keybase-python

Cleanup work

Write fetching code

Here are commands I’d like to be able to type

lokey fetch twitter jf
lokey fetch facebook jfranusic
lokey fetch coinbase jpf
lokey fetch reddit joelfranusic
lokey fetch hn jpf
lokey fetch keybase jfranusic
lokey fetch keybase --http=joel.franusic.com
lokey fetch keybase --https=joel.franusic.com
lokey fetch ssh chat.shazow.net
lokey fetch ssl gliderlabs.com
lokey fetch pgp 'joel@franusic.com'
lokey fetch jwk example.okta.com
lokey fetch github jpf

SSH server public key

import paramiko
import pprint


class AllowAnythingPolicy(paramiko.MissingHostKeyPolicy):
    def missing_host_key(self, client, hostname, key):
        pprint.pprint(key.__dict__)
        print key.public_numbers.e
        print key.public_numbers.n
        return

client = paramiko.SSHClient()
client.set_missing_host_key_policy(AllowAnythingPolicy())
client.connect('chat.shazow.net', username='lokey')

PGP keyserver

https://github.com/dgladkov/python-hkp/

Search keyservers in order, until one is found:

lokey fetch pgp 'joel@franusic.com'

Search all keyservers

lokey fetch pgp 'joel@franusic.com' --all

Search one keyserver

lokey fetch pgp 'joel@franusic.com' --server=pgp.mit.edu

It would be nice to print out the list of keyservers somehow. Maybe I can use the docstring to store the list of servers?

I could use this, but I don’t think it’s what I want, I’d like people to be able to choose from other wacky (or private) keyservers: http://click.pocoo.org/6/options/#choice-options ?

from hkp import KeyServer
import pprint

# https://en.wikipedia.org/wiki/Key_server_(cryptographic)#Keyserver_examples

servers = ['keys.gnupg.net',
           # 'subkeys.pgp.net',
           'pgp.mit.edu',
           'pool.sks-keyservers.net',
           'zimmermann.mayfirst.org',
           'keyserver.ubuntu.com']

for server in servers:
    addr = 'http://{}'.format(server)
    print 'Searching {}'.format(addr)
    serv = KeyServer(addr)
    rv = serv.search('joel@franusic.com')
    pprint.pprint(rv)

DKIM

DKIM has a concept of a “selector” which is basically the “key id” but doesn’t seem to have a way to enumerate the selectors. Additionally, it occurs to me that most admins will not have access to their DKIM private keys, since Gmail, O365, or similar are likely managing their DKIM keys.

Launchpad

https://pypi.python.org/pypi/ssh-import-id/5.6

Make list of what I need to do before I can launch “v1”

Test JWK > PEM encoding with known good keys from Okta

  • Going to call it “pem”, this is what cryptography.io does (load_pem_public_key)
  • Got it working!!
I don’t know what to call a key that looks like this:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgamsZfaSoeje3ychyFxv
SnVvwLetawpY70HsTpa11m/KvTMChFuD3ApLyRxrTS/iWApTnVTVqP07a5jF49m0
N59CpdYvy/yUiJ0n0Q7obbT6KzL+np4yZqG9wvpqRDjJxo9QMB7C+Yg+/NHmmaNg
QPr1gpkHRdWSlWnHyXDbo/oKO9JbSQoXIDzfgRcvh/orJ3txUWG4/Ah5GXyowFf0
eGPsEB823NlqP5ek1dUs4poWYeeuBz0UP32MIyGGb/Jln7dYBG/vjEdwgu1j9zwp
eM5zHowMlaT2ssswwA6zIYh9ve9uzZK5i0utPtY3mPiWigoSjC5FMqkTEFI0iydt
swIDAQAB
-----END PUBLIC KEY-----
http://stackoverflow.com/q/18039401 https://www.ietf.org/rfc/rfc4870.txt

Upload code to private GitHub repo

Upload to PyPi

Upload to GitHub too

Great tutorial! http://peterdowns.com/posts/first-time-with-pypi.html

Figure out use cases

  • Turn Keybase key into S/MIME certificate sounds like the first one?
  • Give your keybase friend access to your SSH server
  • Send an encrypted email to the owner of an HTTPS service
  • Turn your friend’s keybase key into a GPG key for their email address

Send an encrypted email to:

  • Your keybase friend
  • The owner of an HTTPS website
  • The owner of an email domain secured with DKIM keys
  • Your friend on GitHub

Give SSH access to

  • Your keybase friend
  • Your friend with a PGP key

Provide a secure website that can only be accessed by

  • Your keybase friend
  • Your friend on GitHub
  • Someone in your PGP web of trust

X509 “-text” output

Briefly look for a parser.

It doesn’t look like there is a name for the output format below, should be easy to parse though!

  • See “Example” below
  • Surely there is a parser for this already?

Example

cat test_keys/generated-x509 | openssl x509 -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=CA, L=San Francisco, O=My Company, CN=mysite.com
        Validity
            Not Before: Aug 29 00:00:00 1997 GMT
            Not After : Sep  8 00:00:00 1997 GMT
        Subject: C=US, ST=CA, L=San Francisco, O=My Company, CN=mysite.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (1024 bit)
                Modulus (1024 bit):
                    00:b4:fe:28:03:b7:7d:2d:94:ab:50:92:5f:5f:e5:
                    99:45:af:7b:b7:89:33:cd:b4:56:f4:d1:33:af:10:
                    75:46:48:08:6e:a6:01:7b:04:80:98:31:30:ff:f4:
                    53:41:39:71:6f:c4:99:8c:47:71:bd:2f:23:7c:91:
                    92:b8:10:94:7b:6c:b3:fa:78:0a:08:bc:15:e9:48:
                    eb:45:9b:67:8b:97:a2:e3:1c:b2:b3:69:b7:87:63:
                    06:71:08:10:ab:4f:53:59:28:7a:44:47:58:c8:e4:
                    27:fc:17:20:41:d7:99:8c:c9:17:b3:86:59:15:27:
                    cd:73:5d:1d:12:89:9c:77:4f
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:localhost
    Signature Algorithm: sha256WithRSAEncryption
        4b:db:38:11:c5:02:62:fb:04:59:e6:00:24:78:22:4e:e3:3e:
        9a:47:41:b5:9b:84:9e:94:44:83:5b:a2:75:25:80:95:88:77:
        ca:d2:6a:23:af:fc:2d:cb:17:fc:05:74:5b:82:d7:89:86:20:
        96:84:da:2d:3a:54:ae:a2:59:10:f8:cd:ed:7c:cd:fc:75:21:
        52:07:a7:c2:6a:fb:da:9e:9b:e6:c2:69:0d:96:97:67:0a:a2:
        55:80:6c:21:b6:95:76:48:8a:9a:f1:89:d9:70:0e:42:ca:cf:
        f7:b0:6a:53:1b:05:d3:9e:98:8c:fa:e7:95:78:7d:00:0c:f2:
        b8:00:85:e0:62:94:48:df:f1:c6:32:1f:a8:54:9d:bc:17:59:
        05:a8:20:c4:87:d5:2e:5f:42:f6:9f:ce:dc:b0:91:34:70:89:
        36:2c:ba:50:3f:62:47:6f:e7:68:5f:40:96:8c:fc:6a:36:eb:
        31:94:2b:51:fc:ff:e3:a0:bd:cf:19:d0:88:65:7e:c2:36:2d:
        88:bd:1d:c3:33:d5:ed:6b:bb:d7:ec:e1:8b:83:e7:33:13:21:
        a8:73:84:b4:a5:ee:ce:ba:69:82:5c:5b:7c:8a:69:9e:4b:98:
        3f:56:99:93:be:61:52:b7:73:40:9f:44:56:9a:c8:0c:51:19:
        c2:c0:81:e4
-----BEGIN CERTIFICATE-----
MIICxzCCAa+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBcMQswCQYDVQQGEwJVUzEL
MAkGA1UECAwCQ0ExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEzARBgNVBAoMCk15
IENvbXBhbnkxEzARBgNVBAMMCm15c2l0ZS5jb20wHhcNOTcwODI5MDAwMDAwWhcN
OTcwOTA4MDAwMDAwWjBcMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNV
BAcMDVNhbiBGcmFuY2lzY28xEzARBgNVBAoMCk15IENvbXBhbnkxEzARBgNVBAMM
Cm15c2l0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALT+KAO3fS2U
q1CSX1/lmUWve7eJM820VvTRM68QdUZICG6mAXsEgJgxMP/0U0E5cW/EmYxHcb0v
I3yRkrgQlHtss/p4Cgi8FelI60WbZ4uXouMcsrNpt4djBnEIEKtPU1koekRHWMjk
J/wXIEHXmYzJF7OGWRUnzXNdHRKJnHdPAgMBAAGjGDAWMBQGA1UdEQQNMAuCCWxv
Y2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAS9s4EcUCYvsEWeYAJHgiTuM+mkdB
tZuEnpREg1uidSWAlYh3ytJqI6/8LcsX/AV0W4LXiYYgloTaLTpUrqJZEPjN7XzN
/HUhUgenwmr72p6b5sJpDZaXZwqiVYBsIbaVdkiKmvGJ2XAOQsrP97BqUxsF056Y
jPrnlXh9AAzyuACF4GKUSN/xxjIfqFSdvBdZBaggxIfVLl9C9p/O3LCRNHCJNiy6
UD9iR2/naF9Aloz8ajbrMZQrUfz/46C9zxnQiGV+wjYtiL0dwzPV7Wu71+zhi4Pn
MxMhqHOEtKXuzrppglxbfIppnkuYP1aZk75hUrdzQJ9EVprIDFEZwsCB5A==
-----END CERTIFICATE-----

Come up with criteria for public release

v0.4.0 “pip only, for gliderlabs”

[3/3] Give the command line tool a nice experience

  • Stub out features I want, but don’t have yet

Add “fetch keybase” command

https://keybase.io/docs/api/1.0/call/user/lookup
lokey fetch keybase github:jpf
lokey fetch keybase jfranusic

Allow command line flags for defining fields for

openssl keys
PGP keys
x509 certs

Implement “to” command as a command group

[2/2] Handle error messages for common/expected scenarios

fetch

  • timeout
  • unable to reach server
  • no key found on server

to

  • no x509 CA cert

Figure out why to jwt isn’t working

… Somehow PyJWT just didn’t want to work, so I changed to doing manual JWT decoding

Add help text

  • add examples
  • for blank lokey command with examples

Misc cleanup

[6/6] Add support for private keys

Lets look at what formats cryptography.io supports, looks like it supports:

  • PEM
  • DER

https://cryptography.io/en/latest/_modules/cryptography/hazmat/primitives/serialization/

Though they look the same (PEM formatted) private keys are different from PGP private keys However, SSH private keys seem to be the same as PEM private keys:

OpenSSH key:

$ cat tests/fixtures/id_rsa | openssl rsa -text

X.509 key:

$ cat tests/fixtures/key-b-openssl-key.pem | openssl rsa -text

PGP key:

$ cat tests/fixtures/key-c-gpg-private.key | pgpdump

“to pem” (alias to: “openssh”, “x509”)

I can’t get the password protected version of this code working?

No clear way to force the IV here: https://github.com/pyca/cryptography/blob/3bab4e5e356409920e17e2a0aad1eec4f2135e6a/src/cryptography/hazmat/backends/openssl/backend.py#L1456-L1536 Here is how Cryptography tests this code: https://github.com/pyca/cryptography/blob/6012ccff0d709a80259f93a406eca5d133b40108/tests/hazmat/primitives/test_rsa.py#L2079-L2091

“from pem” (alias to: “openssh”, “x509”)

“to pgp”

“from pgp”

“to jwk”

http://stackoverflow.com/questions/24093272/how-to-load-a-private-key-from-a-jwk-into-openssl https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-6.3.2

“from jwk”

Update Eris code in Lokey

Add debug option to show what input key was detected as

  • Maybe I should just do this if you pipe somethign in without a flag?

Add better error handling for cli

  • I added FIXME comments in code

Add better error handling to “load()”

  • detect private keys
  • properly handle unknown types
    • print out input with error, or some of it?
  • Relevant magic files: /usr/share/file/magic
    • pgp
    • gnu (GPG)
    • ssh
    • apple

(Keychain database files)

  • gnome

(GNOME keyring)

  • java

(Java KeyStore)

  • securitycerts
  • ssl

https://github.com/ahupp/python-magic https://github.com/h2non/filetype.py https://github.com/cdgriffith/puremagic

Next step:

  • I think I’ll use libmagic wrapper until I can port one of the above … if it matters

Improve the “Eris” class

  • Will have:
    • ErisPublic
    • ErisPrivate
  • Move load() to “Eris” namespace
  • Use delegate pattern for “plugins”

Play with Click, how hard to get sub-sub-commands?

SO EASY!

@cli.group()
@click.pass_context
def fetch(ctx):
    """Fetch key from place"""
    pass

@fetch.command()
@click.pass_context
def keybase(ctx):
    """Fetch from keybase"""
    print "KEYBASE"

stdin with “peek”

  • estimate effort to implement STDIN reader with “readback”
  • I should just read() enough to peek, if good, then read() max bytes in and put into StringIO
  • nevermind … this was easy

Package and distribute

python setup.py sdist upload -r pypitest
python setup.py sdist upload -r pypi

v0.8.0 “public”

Fix issue with commands “hanging” forever

Truth Table:
STDINSubcommandExpected Result
NoNoHelp text printed with examples
No“fetch”Key fetched
No“to”Help text printed for “to” subcommand
YesNoKey information printed
Yes“to”Key converted
Yes“fetch”Help text printed for “fetch” subcommand

Simpler?

STDINSubcommandExpected Result
NoNoHelp text printed with examples
NoYesSubcommand run
YesNoKey information printed
YesYesSubcommand run

For variables in code:

interactive_terminalsubcommandExpected Result
TrueNoneHelp text printed with examples
True””Subcommand run
FalseNoneKey information printed
False””Subcommand run

Update README with “What’s missing”

Write introduction

Write “Credits” section

Glider labs folks, Len, Jordan

Write inspiration

https://web.archive.org/web/20110828210530/http://vulnarb.com/

Verify that all private keys in repo are test keys

Make into literate document

v1.0.0

Add test fixtures

JWK

  • [ ] RSA keypair

X.509

  • [ ] CA keypair
  • [ ] RSA HTTPS pair
  • [ ] RSA S/MIME pair
  • [ ] RSA HTTPS CSR
  • [ ] HTTPS key for example.com
  • [ ] HTTPS key for example.net
  • [ ] HTTPS key for example.org
  • [ ] HTTPS key for keybase.io
  • [ ] HTTPS key for google.com
  • [ ] HTTPS key for bing.com
  • [ ] HTTPS key for duckduckgo.com

PGP

  • [ ] GPG rsa keypair
  • [ ] GPG ec keypair
  • [ ] Public key from keybase

openssh

  • [ ] rsa keypair
  • [ ] dsa keypair
  • [ ] ec keypair
  • [ ] public key from GitHub

Write automated tests for use cases

Send/Receive S/MIME encrypted mail

https://www.madboa.com/geek/openssl/
openssl smime -encrypt -des3 -in my-message.txt tests/fixtures/key-b-openssl-cert.pem > message-for-key-b

Decrypt with:

openssl smime -decrypt -in message-for-key-b -passin pass:12345678 -inkey tests/fixtures/key-b-openssl-key.pem 

Lots of good stuff in this answer: https://security.stackexchange.com/a/45294

Send/Receive S/MIME signed mail

Send/Receive GPG encrypted email

Here is the answer:

export GNUPGHOME=$(mktemp -d $HOME/.gnupgXXXXXX)

http://serverfault.com/a/528355

Here is how to script GPG: http://stackoverflow.com/a/23785134

Super frustrating, seems like GPG puts secret keys in some shared keystore, wtfhttps://www.gnupg.org/documentation/manuals/gnupg/GPG-Configuration-Options.html --keyring file Add file to the current list of keyrings. If file begins with a tilde and a slash, these are replaced by the $HOME directory. If the filename does not contain a slash, it is assumed to be in the GnuPG home directory (“~/.gnupg” if –homedir or $GNUPGHOME is not used).

Note that this adds a keyring to the current list. If the intent is to use the specified keyring alone, use --keyring along with --no-default-keyring.

If the the option --no-keyring has been used no keyrings will be used at all.

Send/Receive GPG signed email

Log in to website with Client-Side SSL certificate

Sign/Validate JWT with JWK

SSH into host with your website key

Verify that a web server is the same key as your friend’s PGP key

Verify that a ssh server is the same key as your friend’s PGP key

Implement visualizer for Public Key types

“Here is the data I got, here is the command to view more”

  • Got
    • e
    • n
    • metadata
  • To learn more:
    • | openssl x509 -text
    • | pgpdump

openssl

  • print useful stuff in comment if we have it
    • email if it’s in the PGP input
    • email if its in the x509 input
    • CN if email not in x509, etc

x509

  • x509 certs should have reasonable default for serial number
    • unix time?
  • x509 certs should have reasonable default for issuer and subject

See also:

PGP

  • determine reasonable defaults for inputs

openssl-text

  • estimate effort to implement “openssl-text” as output/input format
  • find code in openssl that implements the -text flag

Findings:

  • Looks like names are called “Long Names”, for example: https://github.com/openssl/openssl/blob/6f0ac0e2f27d9240516edb9a23b7863e7ad02898/include/openssl/objects.h#L634
  • Here is what prints out “Certificate:” https://github.com/openssl/openssl/blob/c4a60150914fc260c3fc2854e13372c870bdde76/crypto/x509/t_x509.c#L66-L71 https://git.io/vD5s7
  • It doesn’t look like there is a single bit of code that handles -text flag? Seems like each thing prints its own output. See below for X509, for example

    Example:

    $ echo -n | openssl s_client -connect example.com:443 2> /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -text
    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number:
                0e:64:c5:fb:c2:36:ad:e1:4b:17:2a:eb:41:c7:8c:b0
            Signature Algorithm: sha256WithRSAEncryption
            Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert SHA2 High Assurance Server CA
            Validity
                Not Before: Nov  3 00:00:00 2015 GMT
                Not After : Nov 28 12:00:00 2018 GMT
            Subject: C=US, ST=California, L=Los Angeles, O=Internet Corporation for Assigned Names and Numbers, OU=Technology, CN=www.example.org
            Subject Public Key Info:
                Public Key Algorithm: rsaEncryption
                RSA Public Key: (2048 bit)
                    Modulus (2048 bit):
                        00:b3:40:96:2f:61:63:3e:25:c1:97:ad:65:45:fb:
                        ef:13:42:b3:2c:99:86:f4:b5:80:0b:76:dc:06:38:
                        2c:1f:a3:62:55:5a:36:76:de:ae:5d:fc:e2:e5:b4:
                        e6:ec:5d:ca:ee:ca:df:50:16:24:2c:ee:fc:9a:b6:
                        8c:f6:a8:b3:ac:7a:08:7b:2a:1f:ad:5f:e7:fa:96:
                        59:25:ab:90:b0:f8:c2:3f:13:04:26:74:68:0f:c6:
                        78:2a:95:8a:5f:42:f2:0e:ed:52:a6:eb:68:23:89:
                        e5:43:f8:6d:12:1b:62:42:7b:a8:05:f3:59:c4:5e:
                        d6:c5:cc:46:c0:4b:19:b9:2d:4a:71:72:24:1e:5e:
                        55:44:93:ab:78:a1:47:4d:a5:dc:07:5a:9c:67:f4:
                        11:68:12:2f:d3:28:71:bc:ad:72:05:3c:16:75:d4:
                        f8:72:58:ba:19:f1:dc:09:ed:f1:18:c6:92:2f:7d:
                        bc:16:0b:37:8d:8a:ef:1b:6f:4f:b9:e0:7a:54:98:
                        bf:b5:b6:cf:bb:aa:93:7f:0a:7f:1f:56:eb:a9:d8:
                        e1:db:d5:39:d8:18:5b:d1:f2:64:33:d0:d6:c4:23:
                        ff:09:ab:6d:71:ce:da:cf:c1:17:9c:23:be:2c:af:
                        2f:92:1c:3f:90:08:89:58:f2:b1:e1:10:6f:83:2e:
                        f7:9f
                    Exponent: 65537 (0x10001)
            X509v3 extensions:
                X509v3 Authority Key Identifier: 
                    keyid:51:68:FF:90:AF:02:07:75:3C:CC:D9:65:64:62:A2:12:B8:59:72:3B
      
                X509v3 Subject Key Identifier: 
                    A6:4F:60:1E:1F:2D:D1:E7:F1:23:A0:2A:95:16:E4:E8:9A:EA:6E:48
                X509v3 Subject Alternative Name: 
                    DNS:www.example.org, DNS:example.com, DNS:example.edu, DNS:example.net, DNS:example.org, DNS:www.example.com, DNS:www.example.edu, DNS:www.example.net
                X509v3 Key Usage: critical
                    Digital Signature, Key Encipherment
                X509v3 Extended Key Usage: 
                    TLS Web Server Authentication, TLS Web Client Authentication
                X509v3 CRL Distribution Points: 
                    URI:http://crl3.digicert.com/sha2-ha-server-g4.crl
                    URI:http://crl4.digicert.com/sha2-ha-server-g4.crl
      
                X509v3 Certificate Policies: 
                    Policy: 2.16.840.1.114412.1.1
                      CPS: https://www.digicert.com/CPS
                    Policy: 2.23.140.1.2.2
      
                Authority Information Access: 
                    OCSP - URI:http://ocsp.digicert.com
                    CA Issuers - URI:http://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt
      
                X509v3 Basic Constraints: critical
                    CA:FALSE
        Signature Algorithm: sha256WithRSAEncryption
            84:a8:9a:11:a7:d8:bd:0b:26:7e:52:24:7b:b2:55:9d:ea:30:
            89:51:08:87:6f:a9:ed:10:ea:5b:3e:0b:c7:2d:47:04:4e:dd:
            45:37:c7:ca:bc:38:7f:b6:6a:1c:65:42:6a:73:74:2e:5a:97:
            85:d0:cc:92:e2:2e:38:89:d9:0d:69:fa:1b:9b:f0:c1:62:32:
            65:4f:3d:98:db:da:d6:66:da:2a:56:56:e3:11:33:ec:e0:a5:
            15:4c:ea:75:49:f4:5d:ef:15:f5:12:1c:e6:f8:fc:9b:04:21:
            4b:cf:63:e7:7c:fc:aa:dc:fa:43:d0:c0:bb:f2:89:ea:91:6d:
            cb:85:8e:6a:9f:c8:f9:94:bf:55:3d:42:82:38:4d:08:a4:a7:
            0e:d3:65:4d:33:61:90:0d:3f:80:bf:82:3e:11:cb:8f:3f:ce:
            79:94:69:1b:f2:da:4b:c8:97:b8:11:43:6d:6a:25:32:b9:b2:
            ea:22:62:86:0d:a3:72:7d:4f:ea:57:3c:65:3b:2f:27:73:fc:
            7c:16:fb:0d:03:a4:0a:ed:01:ab:a4:23:c6:8d:5f:8a:21:15:
            42:92:c0:34:a2:20:85:88:58:98:89:19:b1:1e:20:ed:13:20:
            5c:04:55:64:ce:9d:b3:65:fd:f6:8f:5e:99:39:21:15:e2:71:
            aa:6a:88:82
    -----BEGIN CERTIFICATE-----
    5Oia6m5IMIGBBgNVHREEejB4gg93d3cuZXhhbXBsZS5vcmeCC2V4YW1wbGUuY29t
    ggtleGFtcGxlLmVkdYILZXhhbXBsZS5uZXSCC2V4YW1wbGUub3Jngg93d3cuZXhh
    bXBsZS5jb22CD3d3dy5leGFtcGxlLmVkdYIPd3d3LmV4YW1wbGUubmV0MA4GA1Ud
    DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwdQYDVR0f
    BG4wbDA0oDKgMIYuaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItaGEtc2Vy
    dmVyLWc0LmNybDA0oDKgMIYuaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTIt
    aGEtc2VydmVyLWc0LmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwBATAqMCgGCCsG
    AQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgGBmeBDAECAjCB
    gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
    dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9E
    aWdpQ2VydFNIQTJIaWdoQXNzdXJhbmNlU2VydmVyQ0EuY3J0MAwGA1UdEwEB/wQC
    MAAwDQYJKoZIhvcNAQELBQADggEBAISomhGn2L0LJn5SJHuyVZ3qMIlRCIdvqe0Q
    6ls+C8ctRwRO3UU3x8q8OH+2ahxlQmpzdC5al4XQzJLiLjiJ2Q1p+hub8MFiMmVP
    PZjb2tZm2ipWVuMRM+zgpRVM6nVJ9F3vFfUSHOb4/JsEIUvPY+d8/Krc+kPQwLvy
    ieqRbcuFjmqfyPmUv1U9QoI4TQikpw7TZU0zYZANP4C/gj4Ry48/znmUaRvy2kvI
    l7gRQ21qJTK5suoiYoYNo3J9T+pXPGU7Lydz/HwW+w0DpArtAaukI8aNX4ohFUKS
    wDSiIIWIWJiJGbEeIO0TIFwEVWTOnbNl/faPXpk5IRXicapqiII=
    -----END CERTIFICATE-----
      
        

    Here is the code that prints out the above, apparently:

    if (!(cflag & X509_FLAG_NO_HEADER)) {
        if (BIO_write(bp, "Certificate:\n", 13) <= 0)
            goto err;
        if (BIO_write(bp, "    Data:\n", 10) <= 0)
            goto err;
    }
    if (!(cflag & X509_FLAG_NO_VERSION)) {
        l = X509_get_version(x);
        if (l >= 0 && l <= 2) {
            if (BIO_printf(bp, "%8sVersion: %ld (0x%lx)\n", "", l + 1, (unsigned long)l) <= 0)
                goto err;
        } else {
            if (BIO_printf(bp, "%8sVersion: Unknown (%ld)\n", "", l) <= 0)
                goto err;
        }
    }
        
  • See below for RSA sample:
    $ cat test_keys/1024-key.pem | openssl rsa -text
    Enter pass phrase:
    Private-Key: (1024 bit)
    modulus:
        00:cc:35:4e:a4:cc:9b:05:90:78:01:54:20:91:1c:
        c1:f7:ce:33:78:e0:e0:2d:9e:a3:78:9f:59:93:2a:
        b6:dd:a6:12:53:35:2f:a5:0e:54:63:20:d6:b9:23:
        29:17:93:03:98:3c:88:7a:7a:4c:75:17:63:b1:32:
        8b:da:a6:f7:a6:43:f7:09:af:cf:54:fa:25:20:c7:
        15:da:45:c1:4e:5d:37:a7:9b:73:7a:ef:2e:9e:19:
        3b:06:c5:ef:c5:0e:96:34:e3:3d:44:aa:37:29:f9:
        21:0a:36:2b:0e:fc:14:87:bd:98:ba:5c:6e:1c:4d:
        36:f2:bf:a7:7c:68:54:ae:13
    publicExponent: 65537 (0x10001)
    privateExponent:
        00:b2:4a:3c:7e:90:11:2d:1b:84:5b:0c:9b:5e:76:
        f0:37:2e:36:2c:6c:91:ed:e2:9a:b1:1e:d7:73:31:
        10:f5:96:20:5b:bb:8c:fc:9b:f5:3b:93:19:46:0d:
        17:bc:78:63:e4:0f:bd:cc:c9:68:65:b6:e5:18:79:
        9c:37:6c:1d:bc:e1:a6:6e:b0:56:69:ab:7a:a4:11:
        59:cb:16:e7:c9:b8:51:35:bd:fa:28:b8:db:12:62:
        bf:0a:8d:9d:78:6a:b7:4d:18:29:d0:31:a6:1e:3a:
        09:d3:46:a7:55:7f:1a:e7:72:83:6d:84:22:30:be:
        95:44:7c:c9:95:57:cf:3e:b9
    prime1:
        00:f2:04:26:cf:e9:05:35:35:74:aa:ff:42:cc:cf:
        84:96:25:51:e7:b0:a8:66:db:75:26:ea:3f:8c:fc:
        95:98:50:86:48:a5:75:ec:3d:6d:19:c9:7e:62:5e:
        c4:81:70:70:f3:29:82:ba:52:7e:5a:c3:bf:c0:43:
        a4:cf:32:f9:5f
    prime2:
        00:d8:01:e8:95:a2:77:9d:94:c8:c4:16:c9:5d:21:
        43:8e:56:2e:ec:c3:e3:78:9e:0f:09:c7:ab:83:05:
        a2:dd:50:a6:15:4e:ad:86:56:e7:dc:de:1b:20:ee:
        a3:30:36:ab:62:97:b2:c2:bf:4f:25:97:b9:95:dc:
        4c:f4:75:23:cd
    exponent1:
        2f:7e:4b:13:8f:f9:fa:a8:23:de:0e:bb:d0:2f:85:
        28:65:e8:7d:dc:38:1f:ff:3f:2d:44:d8:f5:d4:f7:
        92:6d:72:fc:bc:a3:b8:99:91:58:77:27:3b:2c:b7:
        c9:63:04:77:63:95:08:f5:79:60:5c:7b:bb:c2:4a:
        8c:72:c7:67
    exponent2:
        79:91:79:68:dd:ab:1c:50:e4:3d:17:e5:f4:38:08:
        61:4f:37:d1:a0:dd:a0:c0:b4:0d:f9:06:1a:13:4d:
        f9:e8:ad:d0:48:1b:79:03:e8:32:71:fe:2a:a6:a3:
        c9:3a:bb:72:1d:96:49:ec:89:8a:ca:bd:74:0e:2c:
        71:58:8b:f1
    coefficient:
        70:63:99:39:b6:38:c6:97:72:fd:55:bf:c5:73:55:
        d8:35:47:eb:c2:58:ac:9f:90:b5:a2:60:65:11:de:
        45:de:43:74:75:d0:18:22:94:19:4d:aa:f4:38:27:
        1f:76:71:44:fe:b2:37:df:31:76:82:20:f0:ad:9e:
        f4:de:4c:ca
    writing RSA key
    -----BEGIN RSA PRIVATE KEY-----
    MIICXAIBAAKBgQDMNU6kzJsFkHgBVCCRHMH3zjN44OAtnqN4n1mTKrbdphJTNS+l
    DlRjINa5IykXkwOYPIh6ekx1F2OxMovapvemQ/cJr89U+iUgxxXaRcFOXTenm3N6
    7y6eGTsGxe/FDpY04z1Eqjcp+SEKNisO/BSHvZi6XG4cTTbyv6d8aFSuEwIDAQAB
    AoGBALJKPH6QES0bhFsMm1528DcuNixske3imrEe13MxEPWWIFu7jPyb9TuTGUYN
    F7x4Y+QPvczJaGW25Rh5nDdsHbzhpm6wVmmreqQRWcsW58m4UTW9+ii42xJivwqN
    nXhqt00YKdAxph46CdNGp1V/Gudyg22EIjC+lUR8yZVXzz65AkEA8gQmz+kFNTV0
    qv9CzM+EliVR57CoZtt1Juo/jPyVmFCGSKV17D1tGcl+Yl7EgXBw8ymCulJ+WsO/
    wEOkzzL5XwJBANgB6JWid52UyMQWyV0hQ45WLuzD43ieDwnHq4MFot1QphVOrYZW
    59zeGyDuozA2q2KXssK/TyWXuZXcTPR1I80CQC9+SxOP+fqoI94Ou9AvhShl6H3c
    OB//Py1E2PXU95Jtcvy8o7iZkVh3Jzsst8ljBHdjlQj1eWBce7vCSoxyx2cCQHmR
    eWjdqxxQ5D0X5fQ4CGFPN9Gg3aDAtA35BhoTTfnordBIG3kD6DJx/iqmo8k6u3Id
    lknsiYrKvXQOLHFYi/ECQHBjmTm2OMaXcv1Vv8VzVdg1R+vCWKyfkLWiYGUR3kXe
    Q3R10BgilBlNqvQ4Jx92cUT+sjffMXaCIPCtnvTeTMo=
    -----END RSA PRIVATE KEY-----
        

    Code used to print the above: https://github.com/openssl/openssl/blob/master/crypto/rsa/rsa_ameth.c#L309-L359

    static int pkey_rsa_print(BIO *bp, const EVP_PKEY *pkey, int off, int priv)
    {
        const RSA *x = pkey->pkey.rsa;
        char *str;
        const char *s;
        int ret = 0, mod_len = 0;
    
        if (x->n != NULL)
            mod_len = BN_num_bits(x->n);
    
        if (!BIO_indent(bp, off, 128))
            goto err;
    
        if (BIO_printf(bp, "%s ", pkey_is_pss(pkey) ?  "RSA-PSS" : "RSA") <= 0)
            goto err;
    
        if (priv && x->d) {
            if (BIO_printf(bp, "Private-Key: (%d bit)\n", mod_len) <= 0)
                goto err;
            str = "modulus:";
            s = "publicExponent:";
        } else {
            if (BIO_printf(bp, "Public-Key: (%d bit)\n", mod_len) <= 0)
                goto err;
            str = "Modulus:";
            s = "Exponent:";
        }
        if (!ASN1_bn_print(bp, str, x->n, NULL, off))
            goto err;
        if (!ASN1_bn_print(bp, s, x->e, NULL, off))
            goto err;
        if (priv) {
            if (!ASN1_bn_print(bp, "privateExponent:", x->d, NULL, off))
                goto err;
            if (!ASN1_bn_print(bp, "prime1:", x->p, NULL, off))
                goto err;
            if (!ASN1_bn_print(bp, "prime2:", x->q, NULL, off))
                goto err;
            if (!ASN1_bn_print(bp, "exponent1:", x->dmp1, NULL, off))
                goto err;
            if (!ASN1_bn_print(bp, "exponent2:", x->dmq1, NULL, off))
                goto err;
            if (!ASN1_bn_print(bp, "coefficient:", x->iqmp, NULL, off))
                goto err;
        }
        if (pkey_is_pss(pkey) && !rsa_pss_param_print(bp, 1, x->pss, off))
            goto err;
        ret = 1;
     err:
        return ret;
    }
        
  • Looks like these are all the signatures of the openssl functions used to print out stuff
    $ find openssl -type f -iname '*.c' | xargs egrep '.*int.*_print\(BIO'
    openssl/apps/x509.c:static int purpose_print(BIO *bio, X509 *cert, X509_PURPOSE *pt);
    openssl/apps/x509.c:static int purpose_print(BIO *bio, X509 *cert, X509_PURPOSE *pt)
    openssl/crypto/asn1/a_gentm.c:int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
    openssl/crypto/asn1/a_print.c:int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
    openssl/crypto/asn1/a_time.c:int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
    openssl/crypto/asn1/a_utctm.c:int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm)
    openssl/crypto/asn1/t_bitst.c:int ASN1_BIT_STRING_name_print(BIO *out, ASN1_BIT_STRING *bs,
    openssl/crypto/asn1/t_pkey.c:int ASN1_buf_print(BIO *bp, const unsigned char *buf, size_t buflen, int indent)
    openssl/crypto/asn1/t_pkey.c:int ASN1_bn_print(BIO *bp, const char *number, const BIGNUM *num,
    openssl/crypto/asn1/t_spki.c:int NETSCAPE_SPKI_print(BIO *out, NETSCAPE_SPKI *spki)
    openssl/crypto/asn1/tasn_prn.c:static int asn1_primitive_print(BIO *out, ASN1_VALUE **fld,
    openssl/crypto/asn1/tasn_prn.c:int ASN1_item_print(BIO *out, ASN1_VALUE *ifld, int indent,
    openssl/crypto/asn1/tasn_prn.c:static int asn1_primitive_print(BIO *out, ASN1_VALUE **fld,
    openssl/crypto/asn1/x_bignum.c:static int bn_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
    openssl/crypto/asn1/x_bignum.c:static int bn_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
    openssl/crypto/asn1/x_long.c:static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
    openssl/crypto/asn1/x_long.c:static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
    openssl/crypto/bn/bn_print.c:int BN_print(BIO *bp, const BIGNUM *a)
    openssl/crypto/dh/dh_ameth.c:static int do_dh_print(BIO *bp, const DH *x, int indent, int ptype)
    openssl/crypto/dh/dh_ameth.c:static int dh_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/dh/dh_ameth.c:static int dh_public_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/dh/dh_ameth.c:static int dh_private_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/dh/dh_ameth.c:int DHparams_print(BIO *bp, const DH *x)
    openssl/crypto/dsa/dsa_ameth.c:static int do_dsa_print(BIO *bp, const DSA *x, int off, int ptype)
    openssl/crypto/dsa/dsa_ameth.c:static int dsa_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/dsa/dsa_ameth.c:static int dsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/dsa/dsa_ameth.c:static int dsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/dsa/dsa_ameth.c:static int dsa_sig_print(BIO *bp, const X509_ALGOR *sigalg,
    openssl/crypto/dsa/dsa_prn.c:int DSA_print(BIO *bp, const DSA *x, int off)
    openssl/crypto/dsa/dsa_prn.c:int DSAparams_print(BIO *bp, const DSA *x)
    openssl/crypto/ec/ec_ameth.c:static int do_EC_KEY_print(BIO *bp, const EC_KEY *x, int off, ec_print_t ktype)
    openssl/crypto/ec/ec_ameth.c:static int eckey_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/ec/ec_ameth.c:static int eckey_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/ec/ec_ameth.c:static int eckey_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/ec/ec_ameth.c:int EC_KEY_print(BIO *bp, const EC_KEY *x, int off)
    openssl/crypto/ec/ec_ameth.c:int ECParameters_print(BIO *bp, const EC_KEY *x)
    openssl/crypto/ec/eck_prn.c:int ECPKParameters_print(BIO *bp, const EC_GROUP *x, int off)
    openssl/crypto/ec/ecx_meth.c:static int ecx_key_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/ec/ecx_meth.c:static int ecx_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/ec/ecx_meth.c:static int ecx_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/ocsp/ocsp_prn.c:static int ocsp_certid_print(BIO *bp, OCSP_CERTID *a, int indent)
    openssl/crypto/ocsp/ocsp_prn.c:int OCSP_REQUEST_print(BIO *bp, OCSP_REQUEST *o, unsigned long flags)
    openssl/crypto/ocsp/ocsp_prn.c:int OCSP_RESPONSE_print(BIO *bp, OCSP_RESPONSE *o, unsigned long flags)
    openssl/crypto/rsa/rsa_ameth.c:static int rsa_pss_param_print(BIO *bp, int pss_key, RSA_PSS_PARAMS *pss,
    openssl/crypto/rsa/rsa_ameth.c:static int pkey_rsa_print(BIO *bp, const EVP_PKEY *pkey, int off, int priv)
    openssl/crypto/rsa/rsa_ameth.c:static int rsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/rsa/rsa_ameth.c:static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
    openssl/crypto/rsa/rsa_ameth.c:static int rsa_sig_print(BIO *bp, const X509_ALGOR *sigalg,
    openssl/crypto/rsa/rsa_prn.c:int RSA_print(BIO *bp, const RSA *x, int off)
    openssl/crypto/ts/ts_rsp_print.c:static int ts_status_map_print(BIO *bio, const struct status_map_st *a,
    openssl/crypto/ts/ts_rsp_print.c:static int ts_status_map_print(BIO *bio, const struct status_map_st *a,
    openssl/crypto/x509/t_crl.c:int X509_CRL_print(BIO *out, X509_CRL *x)
    openssl/crypto/x509/t_req.c:int X509_REQ_print(BIO *bp, X509_REQ *x)
    openssl/crypto/x509/t_x509.c:int X509_print(BIO *bp, X509 *x)
    openssl/crypto/x509/t_x509.c:int X509_ocspid_print(BIO *bp, X509 *x)
    openssl/crypto/x509/t_x509.c:int X509_signature_print(BIO *bp, const X509_ALGOR *sigalg,
    openssl/crypto/x509/t_x509.c:int X509_aux_print(BIO *out, X509 *x, int indent)
    openssl/crypto/x509/x_name.c:static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval,
    openssl/crypto/x509/x_name.c:static int x509_name_ex_print(BIO *out, ASN1_VALUE **pval,
    openssl/crypto/x509/x_name.c:int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase)
    openssl/crypto/x509v3/v3_alt.c:int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen)
    openssl/crypto/x509v3/v3_prn.c:static int unknown_ext_print(BIO *out, const unsigned char *ext, int extlen,
    openssl/crypto/x509v3/v3_prn.c:int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag,
    openssl/crypto/x509v3/v3_prn.c:int X509V3_extensions_print(BIO *bp, const char *title,
    openssl/crypto/x509v3/v3_prn.c:static int unknown_ext_print(BIO *out, const unsigned char *ext, int extlen,
    openssl/ssl/ssl_txt.c:int SSL_SESSION_print(BIO *bp, const SSL_SESSION *x)
        
  • So, I think it’s safe to say that there isn’t some set “Standard”, I’m just going to adopt what I like and just make sure I can match the X509 “standard”

key text

X509

openssh

OpenSSH Public Key (vX):
--or--
Certificate:
  Data:
    RSA Public Key: (2048 bit)
      Modulus (2048 bit):
          00:a8:6b:74:9f:22:e8:52:53:49:8e:ac:2d:0e:2c:
          cb:78:3d:32:6b:54:71:5b:8e:59:3e:ae:69:f2:ed:
          bd:1a:44:56:5e:a3:9b:34:d4:38:1c:35:84:8c:a4:
          6d:33:19:d6:3b:96:1b:50:ab:b9:56:ff:a1:b4:06:
          5d:9b:83:68:43:b4:36:8b:c2:25:97:39:b3:05:58:
          95:0a:8c:49:55:2c:f0:09:8f:1e:1b:c3:5a:64:c2:
          38:7a:88:38:bd:50:14:94:3d:2c:7a:86:18:38:da:
          d2:e8:d0:ed:88:db:27:ad:a9:62:28:65:68:f3:d8:
          5a:e2:18:89:70:3e:c9:14:84:39:fa:ff:c4:9e:7c:
          ad:3d:5c:ba:5f:59:10:86:8a:2d:75:bd:60:57:30:
          9a:cd:7e:5a:d1:a4:70:1f:e0:05:d8:fd:a8:7a:e2:
          47:cb:3c:90:c8:33:78:56:96:45:0c:8e:76:1d:68:
          73:15:62:c4:29:1b:fa:c9:0c:14:e1:48:16:b6:71:
          16:58:ab:08:53:cb:4f:c9:a1:30:b6:06:c2:d8:5b:
          99:25:15:3c:36:64:70:33:8c:93:1e:58:12:a9:8d:
          3c:a8:6c:1b:ca:06:9f:9a:b8:1a:06:b3:ae:ea:5a:
          97:06:d5:78:b5:98:5b:2c:e8:01:cf:f4:3d:76:13:
          96:f1
      Exponent: 65537 (0x10001)
    Comment:
      joel@charon

JWK

PGP

pgpdump

Test keys:

https://github.com/openssl/openssl/tree/master/test

v1.1.0

Convert “load()” to use libmagic?

Support for inputs with multiple sub-keys or bundles of keys

  • JWK arrays
  • PEM packages
  • GPG keys with sub-keys, etc

Support for key stores, like java key store, or mac keychain, or GPG keychain

vNext

Add support for other key formats

pkcs12?

SSH Certificate?

DKIM?

  • This is just SSH format, with text record wrapping
  • Might add a DNS interface for import though! That would be rad.

Putty?