mirror of
https://github.com/certbot/certbot.git
synced 2026-01-26 07:41:33 +03:00
133 lines
3.5 KiB
Python
Executable File
133 lines
3.5 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
from chocolate_protocol_pb2 import chocolatemessage
|
|
import M2Crypto
|
|
import urllib2, os, grp, pwd, sys, time, random, sys, hashlib, subprocess
|
|
# It is OK to use the upstream M2Crypto here instead of our modified
|
|
# version.
|
|
|
|
difficulty = 23 # bits of hashcash to generate
|
|
|
|
def sha256(m):
|
|
return hashlib.sha256(m).hexdigest()
|
|
|
|
assert len(sys.argv) > 1 or "CHOCOLATESERVER" in os.environ, "Must specify server via command line or CHOCOLATESERVER environment variable."
|
|
if len(sys.argv) > 1:
|
|
server = sys.argv[1]
|
|
else:
|
|
server = os.environ["CHOCOLATESERVER"]
|
|
|
|
upstream = "https://%s/chocolate.py" % server
|
|
|
|
if len(sys.argv) > 3:
|
|
req_file = sys.argv[2]
|
|
key_file = sys.argv[3]
|
|
else:
|
|
req_file = "req.pem"
|
|
key_file = "key.pem"
|
|
|
|
cert_file = "cert.pem" # we should use getopt to set all of these
|
|
|
|
def rsa_sign(key, data):
|
|
"""
|
|
Sign this data with this private key. For client-side use.
|
|
|
|
@type key: str
|
|
@param key: PEM-encoded string of the private key.
|
|
|
|
@type data: str
|
|
@param data: The data to be signed. Will be hashed (sha256) prior to
|
|
signing.
|
|
|
|
@return: binary string of the signature
|
|
"""
|
|
key = str(key)
|
|
data = str(data)
|
|
privkey = M2Crypto.RSA.load_key_string(key)
|
|
return privkey.sign(hashlib.sha256(data).digest(), 'sha256')
|
|
|
|
def do(m):
|
|
u = urllib2.urlopen(upstream, m.SerializeToString())
|
|
return u.read()
|
|
|
|
def decode(m):
|
|
return (chocolatemessage.FromString(m))
|
|
|
|
def init(m):
|
|
m.chocolateversion = 1
|
|
m.session = ""
|
|
|
|
def drop_privs():
|
|
nogroup = grp.getgrnam("nogroup").gr_gid
|
|
nobody = pwd.getpwnam("nobody").pw_uid
|
|
os.setgid(nogroup)
|
|
os.setgroups([])
|
|
os.setuid(nobody)
|
|
|
|
def make_request(m, csr):
|
|
m.request.recipient = server
|
|
m.request.timestamp = int(time.time())
|
|
m.request.csr = csr
|
|
hashcash_command = "hashcash -P -m -z 12 -b %d -r %s" % (difficulty, server)
|
|
hashcash = subprocess.check_output(hashcash_command.split(), preexec_fn=drop_privs, shell=False).rstrip()
|
|
if hashcash: m.request.clientpuzzle = hashcash
|
|
|
|
def sign(key, m):
|
|
m.request.sig = rsa_sign(key, ("(%d) (%s) (%s)" % (m.request.timestamp, m.request.recipient, m.request.csr)))
|
|
|
|
k=chocolatemessage()
|
|
m=chocolatemessage()
|
|
init(k)
|
|
init(m)
|
|
make_request(m, csr=open(req_file).read())
|
|
sign(open(key_file).read(), m)
|
|
print m
|
|
r=decode(do(m))
|
|
print r
|
|
while r.proceed.IsInitialized():
|
|
if r.proceed.polldelay > 60: r.proceed.polldelay = 60
|
|
print "waiting", r.proceed.polldelay
|
|
time.sleep(r.proceed.polldelay)
|
|
k.session = r.session
|
|
r = decode(do(k))
|
|
print r
|
|
|
|
if r.failure.IsInitialized():
|
|
print "Server reported failure."
|
|
sys.exit(1)
|
|
|
|
sni_todo = []
|
|
for chall in r.challenge:
|
|
print chall
|
|
if chall.type == r.DomainValidateSNI:
|
|
dvsni_nonce, dvsni_y, dvsni_ext = chall.data
|
|
sni_todo.append( (chall.name, dvsni_y, dvsni_nonce, dvsni_ext) )
|
|
|
|
print sni_todo
|
|
import sni_challenge
|
|
|
|
sni_challenge.perform_sni_cert_challenge(sni_todo, req_file, key_file)
|
|
|
|
print "waiting", 3
|
|
time.sleep(3)
|
|
|
|
r=decode(do(k))
|
|
print r
|
|
while r.challenge or r.proceed.IsInitialized():
|
|
print "waiting", 5
|
|
time.sleep(5)
|
|
k.session = r.session
|
|
r = decode(do(k))
|
|
print r
|
|
|
|
# TODO: there should be an unperform_sni_cert_challenge() here.
|
|
# TODO: there should be a deploy_cert() here.
|
|
|
|
if r.success.IsInitialized():
|
|
with open(cert_file, "w") as f:
|
|
f.write(r.success.certificate)
|
|
print "Server issued certificate; certificate written to " + cert_file
|
|
elif r.failure.IsInitialized():
|
|
print "Server reported failure."
|
|
sys.exit(1)
|