def create_sig(msg, key_file, nonce=None, nonce_len=CONFIG.NONCE_SIZE): """Create signature with nonce prepended to the message. TODO: Change this over to M2Crypto... PKey Protect against crypto unicode errors... is this sufficient? Do I need to escape? :param msg: Message to be signed :type msg: Anything with __str__ method :param key_file: Path to a file containing RSA key. Accepted formats are the same as for `Crypto.PublicKey.RSA.importKey`. :type key_file: str :param nonce: Nonce to be used. If None, nonce of `nonce_len` size will be randomly genereted. :type nonce: str or None :param nonce_len: Size of the automaticaly generated nonce. :type nonce_len: int :returns: Signature. :rtype: dict """ msg = str(msg) key = Crypto.PublicKey.RSA.importKey(open(key_file).read()) nonce = Random.get_random_bytes(nonce_len) if nonce is None else nonce msg_with_nonce = nonce + msg hashed = Crypto.Hash.SHA256.new(msg_with_nonce) signature = Crypto.Signature.PKCS1_v1_5.new(key).sign(hashed) logger.debug('%s signed as %s' % (msg_with_nonce, signature)) n_bytes = binascii.unhexlify(leading_zeros(hex(key.n)[2:].rstrip("L"))) e_bytes = binascii.unhexlify(leading_zeros(hex(key.e)[2:].rstrip("L"))) return { "nonce": le_util.jose_b64encode(nonce), "alg": "RS256", "jwk": { "kty": "RSA", "n": le_util.jose_b64encode(n_bytes), "e": le_util.jose_b64encode(e_bytes), }, "sig": le_util.jose_b64encode(signature), }
def dvsni_gen_cert(filepath, name, r_b64, nonce, key): """Generate a DVSNI cert and save it to filepath. :param str filepath: destination to save certificate. This will overwrite any file that is currently at the location. :param str name: domain to validate :param str dvsni_r: jose base64 encoded dvsni r value :param str nonce: hex value of nonce :param key: Key to perform challenge :type key: :class:`letsencrypt.client.client.Client.Key` :returns: dvsni s value jose base64 encoded :rtype: str """ # Generate S dvsni_s = Random.get_random_bytes(CONFIG.S_SIZE) dvsni_r = le_util.jose_b64decode(r_b64) # Generate extension ext = _dvsni_gen_ext(dvsni_r, dvsni_s) cert_pem = crypto_util.make_ss_cert( key.pem, [nonce + CONFIG.INVALID_EXT, name, ext]) with open(filepath, 'w') as chall_cert_file: chall_cert_file.write(cert_pem) return le_util.jose_b64encode(dvsni_s)
def test_standard(self): """Basic test for straightline code.""" # This is a helper function that can be used for handling # open context managers more elegantly. It avoids dealing with # __enter__ and __exit__ calls. # http://www.voidspace.org.uk/python/mock/helpers.html#mock.mock_open m_open = mock.mock_open() with mock.patch("letsencrypt.client.challenge_util.open", m_open, create=True): domain = "example.com" dvsni_r = "r_value" r_b64 = le_util.jose_b64encode(dvsni_r) pem = pkg_resources.resource_string( __name__, os.path.join("testdata", "rsa256_key.pem")) key = client.Client.Key("path", pem) nonce = "12345ABCDE" s_b64 = self._call("tmp.crt", domain, r_b64, nonce, key) self.assertTrue(m_open.called) self.assertEqual(m_open.call_args[0], ("tmp.crt", 'w')) self.assertEqual(m_open().write.call_count, 1) # pylint: disable=protected-access ext = challenge_util._dvsni_gen_ext( dvsni_r, le_util.jose_b64decode(s_b64)) self._standard_check_cert( m_open().write.call_args[0][0], domain, nonce, ext)
def test_standard(self): """Basic test for straightline code.""" # This is a helper function that can be used for handling # open context managers more elegantly. It avoids dealing with # __enter__ and __exit__ calls. # http://www.voidspace.org.uk/python/mock/helpers.html#mock.mock_open m_open = mock.mock_open() with mock.patch("letsencrypt.client.challenge_util.open", m_open, create=True): domain = "example.com" dvsni_r = "r_value" r_b64 = le_util.jose_b64encode(dvsni_r) pem = pkg_resources.resource_string( __name__, os.path.join("testdata", "rsa256_key.pem")) key = client.Client.Key("path", pem) nonce = "12345ABCDE" s_b64 = self._call("tmp.crt", domain, r_b64, nonce, key) self.assertTrue(m_open.called) self.assertEqual(m_open.call_args[0], ("tmp.crt", 'w')) self.assertEqual(m_open().write.call_count, 1) # pylint: disable=protected-access ext = challenge_util._dvsni_gen_ext(dvsni_r, le_util.jose_b64decode(s_b64)) self._standard_check_cert(m_open().write.call_args[0][0], domain, nonce, ext)
def certificate_request(csr_der, key): """Create ACME "certificateRequest" message. :param str csr_der: DER encoded CSR. :param key: TODO :returns: ACME "certificateRequest" message. :rtype: dict """ return { "type": "certificateRequest", "csr": le_util.jose_b64encode(csr_der), "signature": crypto_util.create_sig(csr_der, key), }
def revocation_request(cert_der, key, nonce=None): """Create ACME "revocationRequest" message. :param str cert_der: DER encoded certificate. :param str key: Key in string form. Accepted formats are the same as for `Crypto.PublicKey.RSA.importKey`. :param str nonce: Nonce used for signature. Useful for testing. :returns: ACME "revocationRequest" message. :rtype: dict """ return { "type": "revocationRequest", "certificate": le_util.jose_b64encode(cert_der), "signature": crypto_util.create_sig(cert_der, key, nonce), }
def revocation_request(key_file, cert_der): """Create ACME "revocationRequest" message. :param str key_file: Path to a file containing RSA key. Accepted formats are the same as for `Crypto.PublicKey.RSA.importKey`. :param str cert_der: DER encoded certificate. :returns: ACME "revocationRequest" message. :rtype: dict """ return { "type": "revocationRequest", "certificate": le_util.jose_b64encode(cert_der), "signature": crypto_util.create_sig(cert_der, key_file), }
def certificate_request(csr_der, key): """Create ACME "certificateRequest" message. :param csr_der: DER encoded CSR. :type csr_der: str :param key: TODO :type key: TODO :returns: ACME "certificateRequest" message. :rtype: dict """ return { "type": "certificateRequest", "csr": le_util.jose_b64encode(csr_der), "signature": crypto_util.create_sig(csr_der, key), }
def revocation_request(key_file, cert_der): """Create ACME "revocationRequest" message. :param key_file: Path to a file containing RSA key. Accepted formats are the same as for `Crypto.PublicKey.RSA.importKey`. :type key_file: str :param cert_der: DER encoded certificate. :type cert_der: str :returns: ACME "revocationRequest" message. :rtype: dict """ return { "type": "revocationRequest", "certificate": le_util.jose_b64encode(cert_der), "signature": crypto_util.create_sig(cert_der, key_file), }
def _call(self, data): from letsencrypt.client.le_util import jose_b64encode return jose_b64encode(data)