Skip to content

Instantly share code, notes, and snippets.

@magnuswatn
Created July 2, 2017 09:31
Show Gist options
  • Save magnuswatn/6ce0017c1f8ef18ed04115583b4f79f8 to your computer and use it in GitHub Desktop.
Save magnuswatn/6ce0017c1f8ef18ed04115583b4f79f8 to your computer and use it in GitHub Desktop.
"""
A script to re-sign a certificate signing request
Can be useful if it has been tampered with,
e.g. by using the excellent DER ASCII tool
(https://github.com/google/der-ascii)
Magnus Watn <[email protected]>
"""
import base64
import binascii
import argparse
from pyasn1_modules import rfc2314
from pyasn1.codec.der import encoder, decoder
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.asymmetric import padding
class UnsupportedKeyTypeError(Exception):
"""Signifies that the key was of an unsupported type"""
pass
def _update_signature(csr, private_key):
"""Updates the signature on the csr"""
raw_signature, signature_algorithm = _sign(private_key, csr.tbs_certrequest_bytes)
signature = rfc2314.univ.BitString(hexValue=binascii.hexlify(raw_signature).decode('ascii'))
certification_request_info, _ = decoder.decode(csr.tbs_certrequest_bytes,
asn1Spec=rfc2314.CertificationRequestInfo())
certification_request = rfc2314.CertificationRequest()
certification_request.setComponentByName('certificationRequestInfo', certification_request_info)
certification_request.setComponentByName('signatureAlgorithm', signature_algorithm)
certification_request.setComponentByName('signature', signature)
return encoder.encode(certification_request)
def _pem_encode_csr(csr):
"""Encodes the CSR in PEM format"""
b64_csr = base64.b64encode(csr).decode('ascii')
b64rn_csr = '\r\n'.join(b64_csr[pos:pos+64] for pos in range(0, len(b64_csr), 64))
pem_csr = '-----BEGIN NEW CERTIFICATE REQUEST-----\r\n'
pem_csr += b64rn_csr
pem_csr += '\r\n-----END NEW CERTIFICATE REQUEST-----'
return pem_csr
def _sign(key, payload):
"""Signs the payload with the specified key"""
signature_algorithm = rfc2314.AlgorithmIdentifier()
if isinstance(key, rsa.RSAPrivateKey):
# sha256WithRSAEncryption. MUST have ASN.1 NULL in the parameters field
signature_algorithm.setComponentByName('algorithm', (1, 2, 840, 113549, 1, 1, 11))
signature_algorithm.setComponentByName('parameters', '\x05\x00')
signature = key.sign(
payload,
padding.PKCS1v15(),
hashes.SHA256()
)
elif isinstance(key, ec.EllipticCurvePrivateKey):
# ecdsaWithSHA256. MUST omit the parameters field
signature_algorithm.setComponentByName('algorithm', (1, 2, 840, 10045, 4, 3, 2))
signature = key.sign(
payload,
ec.ECDSA(hashes.SHA256())
)
else:
raise UnsupportedKeyTypeError
return signature, signature_algorithm
def main():
parser = argparse.ArgumentParser(
description='Re-signs a CSR to make the signature valid again after tampering. '\
'Prints the finished CSR to standard out')
parser.add_argument('csr', help="The CSR to be re-signed")
parser.add_argument('key', help="The private key beloging to the CSR (in PEM format)")
args = parser.parse_args()
with open(args.key, 'rb') as open_file:
key = serialization.load_pem_private_key(
open_file.read(),
password=None,
backend=default_backend()
)
with open(args.csr, 'rb') as open_file:
csr_bytes = open_file.read()
try:
original_csr = x509.load_pem_x509_csr(csr_bytes, default_backend())
except ValueError:
original_csr = x509.load_der_x509_csr(csr_bytes, default_backend())
new_csr = _update_signature(original_csr, key)
print(_pem_encode_csr(new_csr))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment