"preset_directory": "preset",
"preset_newline": "True",
"preset_delimiter": ",",
+ "smime": "False",
+ "root_bundle": "",
"private_key": "",
"certificate": ""
}
"template_directory": "template",
"template": "default",
"preset_directory": "preset",
- "preset_newline": "True",
+ "preset_newline": true,
"preset_delimiter": ",",
+ "smime": false,
+ "root_bundle": "",
"certificate": "",
"private_key": ""
}
"address": "",
"contact": "",
"extra": ""
-}
\ No newline at end of file
+}
f=QFileDialog.getOpenFileName(self, "Select Certificate", os.path.expanduser("~"), "PEM (*.pem);; All Files (*)")[0]
if(f):
self.input_certificate.setText(f)
+ def select_root(self):
+ f=QFileDialog.getOpenFileName(self, "Select Root Bundle", os.path.expanduser("~"), "PEM (*.pem);; All Files (*)")[0]
+ if(f):
+ self.input_root.setText(f)
def load(self):
try:
self.input_prescriber.setText(self.config["prescriber"])
self.input_newline.setChecked(bool(self.config["preset_newline"]))
self.input_delimiter.setCurrentText(self.config["preset_delimiter"])
+ self.input_smime.setChecked(bool(self.config["smime"]))
self.input_key.setText(self.config["private_key"])
self.input_certificate.setText(self.config["certificate"])
+ self.input_root.setText(self.config["root_bundle"])
except Exception as e:
QMessageBox.critical(self,"Failed to load", "Failed to load the data into the application.")
raise(e)
self.config["prescriber"]=self.input_prescriber.text()
self.config["preset_newline"]=self.input_newline.isChecked()
self.config["preset_delimiter"]=self.input_delimiter.currentText()
+ self.config["smime"]=self.input_smime.isChecked()
self.config["private_key"]=self.input_key.text()
self.config["certificate"]=self.input_certificate.text()
+ self.config["root_bundle"]=self.input_root.text()
with open(config_file, "w") as f:
f.write(json.dumps(self.config, indent=4))
QMessageBox.information(self,"Saved", "Configuration saved. Please restart MedScript.")
self.input_delimiter=QComboBox(self)
self.input_delimiter.addItems([",", ";"])
layout.addRow("Preset Delimiter", self.input_delimiter)
+ self.input_smime=QCheckBox("Enable digital signature (experimental)", self)
+ layout.addRow("S/MIME", self.input_smime)
self.input_key=QLineEdit(self)
btn_key=QPushButton("...", self)
btn_key.clicked.connect(self.select_key)
layout_certificate.addWidget(self.input_certificate)
layout_certificate.addWidget(btn_certificate)
layout.addRow("X509 Certificate", layout_certificate)
+ self.input_root=QLineEdit(self)
+ btn_root=QPushButton("...", self)
+ btn_root.clicked.connect(self.select_root)
+ layout_root=QHBoxLayout()
+ layout_root.addWidget(self.input_root)
+ layout_root.addWidget(btn_root)
+ layout.addRow("Root Bundle", layout_root)
button_save=QPushButton("Save")
button_save.clicked.connect(self.save)
button_reset=QPushButton("Reset")
from M2Crypto import BIO, Rand, SMIME, X509
from config import config
from hashlib import sha256
+from datetime import datetime
class Signature():
def sign(data, certificate, privkey, password=""):
def get_password(*args):
- print(password)
return bytes(password, "ascii")
hash=sha256(data.encode()).hexdigest()
smime=SMIME.SMIME()
return(out.read().decode())
def verify(data, certificate, signature):
+ try:
+ if(not Signature.verify_chain(certificate)):
+ return False
+ except Exception as e:
+ print(e)
+ return False
+
hash=sha256(data.encode()).hexdigest()
smime=SMIME.SMIME()
p7=SMIME.smime_load_pkcs7_bio(buf)[0]
try:
- v=smime.verify(p7, BIO.MemoryBuffer(hash.encode()))
+ smime.verify(p7, BIO.MemoryBuffer(hash.encode()))
return(x509.get_subject().as_text())
except SMIME.PKCS7_Error as e:
print(e)
return False
+
+ def verify_chain(cert_chain_path):
+ cert_chain=[]
+ with open(cert_chain_path) as chain_file:
+ cert_data=""
+ for line in chain_file:
+ cert_data=cert_data+line.strip()+"\n"
+ if "----END CERTIFICATE----" in line:
+ cert_chain.append(X509.load_cert_string(cert_data))
+ cert_data=""
+
+ for i in range(len(cert_chain)):
+ cert=cert_chain[i]
+ if(datetime.utcnow().timestamp()>cert.get_not_after().get_datetime().timestamp()):
+ print("Certificate expired")
+ return False
+ if(i>0):
+ prev_cert=cert_chain[i-1]
+ prev_public_key=prev_cert.get_pubkey().get_rsa()
+ if(not prev_cert.verify(cert.get_pubkey())):
+ print("Certificate chain signature verification failed")
+ return False
+ with open(config["root_bundle"]) as root:
+ root_bundle=root.read()
+ if(cert_chain[-1].as_pem().decode() not in root_bundle):
+ print("Certificate not in root bundle")
+ return False
+ return True
def load_interface(self, file="", date=None, id="", name="", age="", sex="", address="", contact="", extra="", mode="", daw="", diagnosis="", note="", report="", advice="", investigation="", medication="", additional=""):
try:
file_msg=self.current_file.file if self.current_file.file else "New file"
- sign_msg="(signed)" if self.current_file.is_signed() else ""
+ sign_msg="(signed)" if config["smime"] and self.current_file.is_signed() else ""
self.statusbar.showMessage(file_msg+" "+sign_msg)
if date is None:
d=QDateTime.currentDateTime()
menu_prepare=menubar.addMenu("Prepare")
menu_prepare.addAction(action_render)
menu_prepare.addAction(action_refresh)
- menu_prepare.addAction(action_sign)
- menu_prepare.addAction(action_unsign)
- menu_prepare.addAction(action_verify)
+ if(config["smime"]):
+ menu_prepare.addAction(action_sign)
+ menu_prepare.addAction(action_unsign)
+ menu_prepare.addAction(action_verify)
menu_settings=menubar.addMenu("Settings")
menu_settings.addAction(action_configuration)
menu_settings.addAction(action_prescriber)