Personally I wrote a generic ASN.1 decoder/encoder tool. It is called DDer. It transforms a DER-encoded object into a structured text representation (with parenthesis, somewhat reminiscent of Lisp). The interesting part is that it comes with a companion tool, MDer, which performs the reverse transform. Thus, you can edit certificate contents with a text editor, and MDer will work out the encoding subtleties.
Now a tricky remaining part is the signatures. Certificates are signed, and changing any bit in a certificate invalidates the signature, so this calls for a signing tool. I happen to have written my own crypto library (in C#, not published yet) that can apply a signature on an arbitrary DER-encoded object, even patching the inner SignatureAlgorithm
in the to-be-signed structure of a certificate.
In a hurry, though, a RSA signature can be computed "by hand" with the help of the dc
utility which is standard issue in Unix-like systems. That calculator can use hexadecimal input and output, process arbitrarily long integers, and compute modular exponentiations with the integrated |
operator. Workflow would be:
- Create the to-be-signed structure in the text representation of DDer, and use MDer to convert it to DER. When dabbling with certificates, you usually start with an existing certificate, so DDer-ize that.
- Use
sha256sum
(another standard command-line tool on Linux) to compute the SHA-256 hash of the to-be-signed.
- Assemble the hash value in a properly padded structure as befits RSA. You want to make it in hexadecimal, in a text editor. Make sure that all your hexadecimal is uppercase with no extra spaces, because that's what
dc
feeds on.
- Use DDer on the private key file to obtain the key elements, specifically the modulus (first big integer) and the private exponent (third big integer in the structure)(the second big integer is the public exponent and is usually not that big).
- Use
dc
to compute the modular exponentiation. You switch dc
to hexadecimal input and output with 16 i 10 o
(which reads as "set input radix to 16, then set output radix to 10"; that "10" is interpreted in hexadecimal, since you just switched the input radix to 16!). You then copy&paste the operands (padded hash, private exponent, modulus, in that order), and compute the modular exponentiation (|
). You use p
to print the result, which will be in hexadecimal.
- Back to the DDer text representation of the to-be-signed, you write the wrapping structure, which is a
SEQUENCE
that contains the to-be-signed, an AlgorithmIdentifier
that identifies the signature algorithm, and a BIT STRING
whose contents are the signature value. MDer will expect the BIT STRING
contents to be in hexadecimal, just like what you just obtained from dc
, so it's a matter of copy&paste (again).
- And a final MDer to produce the final certificate, DER-encoded.
- If you need the new certificate in PEM format, convert with with
openssl
, or directly with the base64
tool (again a usual command-line tool in Linux) and add the -----BEGIN CERTIFICATE-----
and -----END CERTIFICATE-----
lines manually (this may be necessary in case you purposefully made an incorrectly encoded certificate, that openssl
won't accept to convert).
If you can go through all these steps in due order, then you know enough about RSA and ASN.1 to write your own tools to apply signatures. If using C#, you can reuse the ASN.1 library from DDer, as well as the big-integer class (ZInt
) for some very basic and perfunctory RSA implementation (not secure enough for use in production, but usable for making test cases).