mirror of https://github.com/PurpleI2P/pyseeder
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
3.3 KiB
90 lines
3.3 KiB
import os |
|
import random |
|
import sys |
|
import datetime |
|
|
|
from pyseeder.utils import PyseederException |
|
|
|
from cryptography.hazmat.backends import default_backend |
|
from cryptography.hazmat.primitives import serialization |
|
from cryptography.hazmat.primitives.asymmetric import rsa |
|
from cryptography import x509 |
|
from cryptography.x509.oid import NameOID |
|
from cryptography.hazmat.primitives import hashes |
|
from cryptography.hazmat.primitives.asymmetric import padding |
|
|
|
def keygen(pub_key, priv_key, user_id, priv_key_password=None): |
|
"""Generate new private key and certificate RSA_SHA512_4096""" |
|
# Generate our key |
|
key = rsa.generate_private_key(public_exponent=65537, key_size=4096, |
|
backend=default_backend()) |
|
|
|
if priv_key_password: |
|
ea = serialization.BestAvailableEncryption(priv_key_password) |
|
else: |
|
ea = serialization.NoEncryption() |
|
|
|
# Write our key to disk for safe keeping |
|
with open(priv_key, "wb") as f: |
|
f.write(key.private_bytes( |
|
encoding=serialization.Encoding.PEM, |
|
format=serialization.PrivateFormat.TraditionalOpenSSL, |
|
encryption_algorithm=ea, |
|
)) |
|
|
|
# Various details about who we are. For a self-signed certificate the |
|
# subject and issuer are always the same. |
|
subject = issuer = x509.Name([ |
|
x509.NameAttribute(NameOID.COUNTRY_NAME, "XX"), |
|
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "XX"), |
|
x509.NameAttribute(NameOID.LOCALITY_NAME, "XX"), |
|
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "I2P Anonymous Network"), |
|
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "I2P"), |
|
x509.NameAttribute(NameOID.COMMON_NAME, user_id), |
|
]) |
|
|
|
cert = x509.CertificateBuilder() \ |
|
.subject_name(subject) \ |
|
.issuer_name(issuer) \ |
|
.public_key(key.public_key()) \ |
|
.not_valid_before(datetime.datetime.utcnow()) \ |
|
.not_valid_after( |
|
datetime.datetime.utcnow() + datetime.timedelta(days=365*10) |
|
) \ |
|
.serial_number(random.randrange(1000000000, 2000000000)) \ |
|
.add_extension( |
|
x509.SubjectKeyIdentifier.from_public_key(key.public_key()), |
|
critical=False, |
|
).sign(key, hashes.SHA512(), default_backend()) |
|
|
|
with open(pub_key, "wb") as f: |
|
f.write(cert.public_bytes(serialization.Encoding.PEM)) |
|
|
|
|
|
def get_signature(contents, priv_key, priv_key_password=None): |
|
"""Calculate signature for prepared reseed file""" |
|
"""Singing with NoneWithRSA algorithm: https://stackoverflow.com/a/68301530""" |
|
import rsa as pyrsa |
|
|
|
with open(priv_key, "rb") as kf: |
|
pk = pyrsa.PrivateKey.load_pkcs1( |
|
serialization.load_pem_private_key( |
|
kf.read(), password=priv_key_password, backend=default_backend() |
|
).private_bytes( |
|
serialization.Encoding.PEM, |
|
serialization.PrivateFormat.TraditionalOpenSSL, |
|
serialization.NoEncryption() |
|
) |
|
) |
|
|
|
digest = hashes.Hash(hashes.SHA512()) |
|
digest.update(contents) |
|
h = digest.finalize() |
|
|
|
keylength = pyrsa.pkcs1.common.byte_size(pk.n) |
|
padded = pyrsa.pkcs1._pad_for_signing(h, keylength) |
|
payload = pyrsa.pkcs1.transform.bytes2int(padded) |
|
encrypted = pk.blinded_encrypt(payload) |
|
sig = pyrsa.pkcs1.transform.int2bytes(encrypted, keylength) |
|
|
|
return sig
|
|
|