19
19
import saml2 .cryptography .asymmetric
20
20
import saml2 .cryptography .pki
21
21
22
- from tempfile import NamedTemporaryFile
22
+ from tempfile import NamedTemporaryFile , mkdtemp
23
23
from subprocess import Popen
24
24
from subprocess import PIPE
25
25
@@ -483,6 +483,19 @@ def parse_xmlsec_output(output):
483
483
def sha1_digest (msg ):
484
484
return hashlib .sha1 (msg ).digest ()
485
485
486
+ class NamedPipe (object ):
487
+ def __init__ (self ):
488
+ self ._tempdir = mkdtemp ()
489
+ self .name = os .path .join (self ._tempdir , 'fifo' )
490
+
491
+ try :
492
+ os .mkfifo (self .name )
493
+ except :
494
+ os .rmdir (self ._tempdir )
495
+
496
+ def close (self ):
497
+ os .remove (self .name )
498
+ os .rmdir (self ._tempdir )
486
499
487
500
class Signer (object ):
488
501
"""Abstract base class for signing algorithms."""
@@ -651,10 +664,10 @@ def encrypt(self, text, recv_key, template, key_type):
651
664
def encrypt_assertion (self , statement , enc_key , template , key_type , node_xpath ):
652
665
raise NotImplementedError ()
653
666
654
- def decrypt (self , enctext , key_file , id_attr ):
667
+ def decrypt (self , enctext , key_file , id_attr , passphrase = None ):
655
668
raise NotImplementedError ()
656
669
657
- def sign_statement (self , statement , node_name , key_file , node_id , id_attr ):
670
+ def sign_statement (self , statement , node_name , key_file , node_id , id_attr , passphrase = None ):
658
671
raise NotImplementedError ()
659
672
660
673
def validate_signature (self , enctext , cert_file , cert_type , node_name , node_id , id_attr ):
@@ -775,7 +788,7 @@ def encrypt_assertion(self, statement, enc_key, template, key_type='des-192', no
775
788
776
789
return output .decode ('utf-8' )
777
790
778
- def decrypt (self , enctext , key_file , id_attr ):
791
+ def decrypt (self , enctext , key_file , id_attr , passphrase = None ):
779
792
"""
780
793
781
794
:param enctext: XML document containing an encrypted part
@@ -786,6 +799,16 @@ def decrypt(self, enctext, key_file, id_attr):
786
799
logger .debug ('Decrypt input len: %d' , len (enctext ))
787
800
_ , fil = make_temp (enctext , decode = False )
788
801
802
+ named_pipe = None
803
+ if key_file is not None :
804
+ if passphrase is not None :
805
+ named_pipe = NamedPipe ()
806
+ # Decrypt the certificate
807
+ with open (key_file ) as f , open (named_pipe .name , 'wb' ) as g :
808
+ key = crypto .load_privatekey (crypto .FILETYPE_PEM , f .read (), passphrase = passphrase )
809
+ g .write (crypto .dump_privatekey (crypto .FILETYPE_PEM , key ))
810
+ key_file = named_pipe .name
811
+
789
812
com_list = [
790
813
self .xmlsec ,
791
814
'--decrypt' ,
@@ -801,7 +824,7 @@ def decrypt(self, enctext, key_file, id_attr):
801
824
802
825
return output .decode ('utf-8' )
803
826
804
- def sign_statement (self , statement , node_name , key_file , node_id , id_attr ):
827
+ def sign_statement (self , statement , node_name , key_file , node_id , id_attr , passphrase = None ):
805
828
"""
806
829
Sign an XML statement.
807
830
@@ -823,6 +846,16 @@ def sign_statement(self, statement, node_name, key_file, node_id, id_attr):
823
846
delete = self ._xmlsec_delete_tmpfiles ,
824
847
)
825
848
849
+ named_pipe = None
850
+ if key_file is not None :
851
+ if passphrase is not None :
852
+ named_pipe = NamedPipe ()
853
+ # Decrypt the certificate
854
+ with open (key_file ) as f , open (named_pipe .name , 'wb' ) as g :
855
+ key = crypto .load_privatekey (crypto .FILETYPE_PEM , f .read (), passphrase = passphrase )
856
+ g .write (crypto .dump_privatekey (crypto .FILETYPE_PEM , key ))
857
+ key_file = named_pipe .name
858
+
826
859
com_list = [
827
860
self .xmlsec ,
828
861
'--sign' ,
@@ -939,7 +972,7 @@ def version(self):
939
972
# better than static 0.0 here.
940
973
return 'XMLSecurity 0.0'
941
974
942
- def sign_statement (self , statement , node_name , key_file , node_id , id_attr ):
975
+ def sign_statement (self , statement , node_name , key_file , node_id , id_attr , passphrase = None ):
943
976
"""
944
977
Sign an XML statement.
945
978
@@ -955,6 +988,8 @@ def sign_statement(self, statement, node_name, key_file, node_id, id_attr):
955
988
import xmlsec
956
989
import lxml .etree
957
990
991
+ assert passphrase is None , "Encrypted key files is not supported"
992
+
958
993
xml = xmlsec .parse_xml (statement )
959
994
signed = xmlsec .sign (xml , key_file )
960
995
signed_str = lxml .etree .tostring (signed , xml_declaration = False , encoding = "UTF-8" )
@@ -1062,6 +1097,7 @@ def security_context(conf):
1062
1097
tmp_cert_file = conf .tmp_cert_file ,
1063
1098
tmp_key_file = conf .tmp_key_file ,
1064
1099
validate_certificate = conf .validate_certificate ,
1100
+ key_file_passphrase = conf .key_file_passphrase ,
1065
1101
enc_key_files = enc_key_files ,
1066
1102
encryption_keypairs = conf .encryption_keypairs ,
1067
1103
sec_backend = sec_backend ,
@@ -1251,6 +1287,7 @@ def __init__(
1251
1287
generate_cert_info = None ,
1252
1288
tmp_cert_file = None , tmp_key_file = None ,
1253
1289
validate_certificate = None ,
1290
+ key_file_passphrase = None ,
1254
1291
enc_key_files = None , enc_key_type = 'pem' ,
1255
1292
encryption_keypairs = None ,
1256
1293
enc_cert_type = 'pem' ,
@@ -1268,6 +1305,7 @@ def __init__(
1268
1305
1269
1306
# Your private key for signing
1270
1307
self .key_file = key_file
1308
+ self .key_file_passphrase = key_file_passphrase
1271
1309
self .key_type = key_type
1272
1310
1273
1311
# Your public key for signing
@@ -1358,6 +1396,7 @@ def decrypt_keys(self, enctext, keys=None, id_attr=''):
1358
1396
:return: The decrypted text
1359
1397
"""
1360
1398
key_files = []
1399
+ passphrase = self .key_file_passphrase
1361
1400
1362
1401
if not isinstance (keys , list ):
1363
1402
keys = [keys ]
@@ -1370,7 +1409,7 @@ def decrypt_keys(self, enctext, keys=None, id_attr=''):
1370
1409
key_files .append (key_file )
1371
1410
1372
1411
try :
1373
- dectext = self .decrypt (enctext , key_file = key_files , id_attr = id_attr )
1412
+ dectext = self .decrypt (enctext , key_file = key_files , id_attr = id_attr , passphrase = passphrase )
1374
1413
except DecryptError as e :
1375
1414
raise
1376
1415
else :
@@ -1379,12 +1418,15 @@ def decrypt_keys(self, enctext, keys=None, id_attr=''):
1379
1418
for key_file in key_files :
1380
1419
os .unlink (key_file )
1381
1420
1382
- def decrypt (self , enctext , key_file = None , id_attr = '' ):
1421
+ def decrypt (self , enctext , key_file = None , id_attr = '' , passphrase = None ):
1383
1422
""" Decrypting an encrypted text by the use of a private key.
1384
1423
1385
1424
:param enctext: The encrypted text as a string
1386
1425
:return: The decrypted text
1387
1426
"""
1427
+ if passphrase is None :
1428
+ passphrase = self .key_file_passphrase
1429
+
1388
1430
if not id_attr :
1389
1431
id_attr = self .id_attr
1390
1432
@@ -1396,7 +1438,7 @@ def decrypt(self, enctext, key_file=None, id_attr=''):
1396
1438
]
1397
1439
for key_file in key_files :
1398
1440
try :
1399
- dectext = self .crypto .decrypt (enctext , key_file , id_attr )
1441
+ dectext = self .crypto .decrypt (enctext , key_file , id_attr , passphrase = passphrase )
1400
1442
except XmlsecError as e :
1401
1443
continue
1402
1444
else :
@@ -1650,7 +1692,7 @@ def sign_statement_using_xmlsec(self, statement, **kwargs):
1650
1692
""" Deprecated function. See sign_statement(). """
1651
1693
return self .sign_statement (statement , ** kwargs )
1652
1694
1653
- def sign_statement (self , statement , node_name , key = None , key_file = None , node_id = None , id_attr = '' ):
1695
+ def sign_statement (self , statement , node_name , key = None , key_file = None , node_id = None , id_attr = '' , passphrase = None ):
1654
1696
"""Sign a SAML statement.
1655
1697
1656
1698
:param statement: The statement to be signed
@@ -1671,12 +1713,15 @@ def sign_statement(self, statement, node_name, key=None, key_file=None, node_id=
1671
1713
if not key and not key_file :
1672
1714
key_file = self .key_file
1673
1715
1716
+ if not passphrase :
1717
+ passphrase = self .key_file_passphrase
1718
+
1674
1719
return self .crypto .sign_statement (
1675
1720
statement ,
1676
1721
node_name ,
1677
1722
key_file ,
1678
1723
node_id ,
1679
- id_attr )
1724
+ id_attr , passphrase = passphrase )
1680
1725
1681
1726
def sign_assertion_using_xmlsec (self , statement , ** kwargs ):
1682
1727
""" Deprecated function. See sign_assertion(). """
@@ -1709,7 +1754,7 @@ def sign_attribute_query(self, statement, **kwargs):
1709
1754
return self .sign_statement (
1710
1755
statement , class_name (samlp .AttributeQuery ()), ** kwargs )
1711
1756
1712
- def multiple_signatures (self , statement , to_sign , key = None , key_file = None , sign_alg = None , digest_alg = None ):
1757
+ def multiple_signatures (self , statement , to_sign , key = None , key_file = None , sign_alg = None , digest_alg = None , passphrase = None ):
1713
1758
"""
1714
1759
Sign multiple parts of a statement
1715
1760
@@ -1740,7 +1785,7 @@ def multiple_signatures(self, statement, to_sign, key=None, key_file=None, sign_
1740
1785
key = key ,
1741
1786
key_file = key_file ,
1742
1787
node_id = sid ,
1743
- id_attr = id_attr )
1788
+ id_attr = id_attr , passphrase = passphrase )
1744
1789
1745
1790
return statement
1746
1791
0 commit comments