17

Is it possible to convert a public key to a certificate using the public key only?

I've seen the following, which takes a private key to form a cert request, which can then be self-signed:

openssl req -new -key privkey.pem -out cert.csr

But, given a public key only (such as in SSH, OpenSSL, PEM, PKCS8 format), how do I make a self-signed certificate (of, say, PKCS12 format) containing the public key?

thanks

Basically, I want to take my friend's public PGP key, convert it to a cert, and use that cert in my email program for encrypting emails with him without my having to use Enigma mail in Thunderbird.

2 Answers 2

15

First of all, what you are attempting to do will not work. PGP and S/MIME are fundamentally incompatible. Putting your friend's PGP public key into an x.509 certificate will not allow you to use S/MIME tools to communicate with him. Either you need to both use S/MIME, or both use PGP. You cannot mix.

As far as whether or not this can be done: Certificates have to be signed by someone. So if you have a public key, you can embed that into a certificate that gets signed by someone else, but you can't create a self-signed certificate without the private key.

But here's the catch: user-oriented tools (such a openssl command line) will only create certificates either (a) self-signed using a private key, or (b) by signing a request.

And signing requests are themselves signed using the key they embed. The idea is that if you're going to create a certificate for someone, you want to verify that they actually have the key you're certifying.

But... when you get down to the internals, such as using the openSSL API functions, you're allowed to do pretty much whatever you want. You can create unsigned requests and certificates[1] (which may not be honored anywhere) and you can create certificates using only the public key and some attributes.

You may want to look at using either the openssl C api directly, or perhaps one of the fine libraries built for other languages.

Here's an example in Ruby: This example provided as an edit by grawity. I have not tested this code.

#!/usr/bin/env ruby
require 'openssl'

pkey_data = File.read("id_rsa")
pkey_password = ""
subject = "/O=Honest Achmed's Used Cars and Certificates/OU=Self-made/CN=Geremia's cert"
expiry_years = 1

# Fill basic v1 fields
cert = OpenSSL::X509::Certificate.new
cert.version = 2  # for X.509 v3
cert.serial = 1
cert.subject = OpenSSL::X509::Name.parse(subject)
cert.issuer = cert.subject
cert.not_before = Time.now
cert.not_after = Time.now + (expiry_years * 365 * 86400)

# Add v3 extensions
extf = OpenSSL::X509::ExtensionFactory.new
extf.subject_certificate = cert
extf.issuer_certificate = cert
cert.extensions = [
    # basicConstraints is marked as 'critical' here
    extf.create_extension("basicConstraints", "CA:FALSE", true),
    extf.create_extension("subjectKeyIdentifier", "hash"),
    # 'copy' means needed information will be taken from subjectKeyIdentifier
    extf.create_extension("authorityKeyIdentifier",
        "keyid:copy, issuer:copy"),
    extf.create_extension("keyUsage",
        "digitalSignature, keyEncipherment, nonRepudiation"),
    extf.create_extension("extendedKeyUsage",
        "clientAuth, serverAuth, emailProtection"),
]

# Add public key and self-sign – if you omit the cert.sign()
# call, you WILL have an unsigned certificate made just with the public key.
pkey = OpenSSL::PKey::RSA.new(pkey_data, pkey_password)
cert.public_key = pkey
cert.sign(pkey, OpenSSL::Digest::SHA1.new)

# Output in PEM format (aka base64-encoded DER)
puts cert.to_pem
  • [1] About a decade ago I created an OpenSSL interop library for .NET which did these things by accident. Luckily nobody ever saw this project but me.
4

PKCS#12 is a generic archive format for about everything, but, in practice, it is used to store a certificate along with its private key. You do not have your friend's private key, only the public key, so there is little point in making a PKCS#12 archive.

For the same reason, you won't be able to make a self-signed certificate because generating a signature entails using the private key, which you do not have. However, there is nothing magical about self signatures in certificates. An X.509 certificate is normally signed by a Certification Authority (the CA is said to "issue the certificate"), so the certificate format includes a field for the signature. When a certificate is not produced by a CA, the field must be populated with some value, and it is traditional to use a self-signature -- but that signature does not contain much in terms of useful information, and most software won't bother verify it at all; they just need some blob of approximately the right size so that encoding and decoding routines work.

Therefore, if you just want a public key "as a certificate", you can just stuff the values together with some random junk in lieu of the signature from the CA. As @tylerl explains, it will be easier if you use some actual CA software (e.g. OpenSSL) to make the certificate, because the structure is intricate in all the ASN.1 details. Most full-blown CA products will want the public key to be provided as a PKCS#10 request, which will include the public key but also be signed with the corresponding private key, as a definite indication that the private key owner really wishes to obtain a certificate. However, this is not inherent to the signature system; it is technically feasible for any CA to build and sign a certificate with arbitrary data, including a public key obtained from any source. Direct usages of some certificate-aware library can do the trick.


Now for the "PGP" part. The OpenPGP format is quite distinct from X.509, and this also applies to how emails are encrypted; when X.509 certificates are involved, the encoding relies on S/MIME instead of OpenPGP. For what you are trying to do, this has two consequences:

  • It is not guaranteed that your friend's public key will fit in the X.509 format. RSA keys ought to be fine; however, OpenPGP often uses ElGamal keys for which there is no widespread standard encoding as per X.509 rules. Your S/MIME software (e.g. Thunderbird) will not be able to understand and use an ElGamal key found in an X.509 certificate.

  • Even if you do the encryption, your friend will have the same issue on his side. He will have to convince his software to apply S/MIME and its decryption rules using a private key which he only has stored in OpenPGP format.

The latter point, in particular, means that your friend will most probably have to also extract his private key and obtain some sort of X.509 certificate for the public key. In that case, it is simpler to let him do the work: once he has built or obtained a certificate for his public key, let him send it to you.


Note that the main point of PKI is to convey some trust about bindings between identities and public keys. By building your own X.509 certificates from data from the PGP world, you somehow disregard this trust. So you will need some other way to make sure that you are using the correct public key. This is not necessarily wrong, but be aware that you are doing things "manually", which requires that you understand what is going on, down to the fine cryptographic details.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .