5
5
6
6
import base64
7
7
import hashlib
8
+ import itertools
8
9
import logging
9
10
import os
10
11
import ssl
@@ -591,10 +592,6 @@ def verify_redirect_signature(saml_msg, crypto, cert=None, sigkey=None):
591
592
return bool (signer .verify (string , _sign , _key ))
592
593
593
594
594
- LOG_LINE = 60 * '=' + '\n %s\n ' + 60 * '-' + '\n %s' + 60 * '='
595
- LOG_LINE_2 = 60 * '=' + '\n %s\n %s\n ' + 60 * '-' + '\n %s' + 60 * '='
596
-
597
-
598
595
def make_str (txt ):
599
596
if isinstance (txt , six .string_types ):
600
597
return txt
@@ -682,10 +679,9 @@ def __init__(self, xmlsec_binary, **kwargs):
682
679
CryptoBackend .__init__ (self , ** kwargs )
683
680
assert (isinstance (xmlsec_binary , six .string_types ))
684
681
self .xmlsec = xmlsec_binary
685
- if os .environ .get ('PYSAML2_KEEP_XMLSEC_TMP' , None ):
686
- self ._xmlsec_delete_tmpfiles = False
687
- else :
688
- self ._xmlsec_delete_tmpfiles = True
682
+ self ._xmlsec_delete_tmpfiles = os .environ .get (
683
+ 'PYSAML2_KEEP_XMLSEC_TMP' , False
684
+ )
689
685
690
686
try :
691
687
self .non_xml_crypto = RSACrypto (kwargs ['rsa_key' ])
@@ -727,11 +723,10 @@ def encrypt(self, text, recv_key, template, session_key_type, xpath=''):
727
723
if xpath :
728
724
com_list .extend (['--node-xpath' , xpath ])
729
725
730
- (_stdout , _stderr , output ) = self ._run_xmlsec (
731
- com_list ,
732
- [template ],
733
- exception = DecryptError ,
734
- validate_output = False )
726
+ try :
727
+ (_stdout , _stderr , output ) = self ._run_xmlsec (com_list , [template ])
728
+ except XmlsecError as e :
729
+ six .raise_from (EncryptError (com_list ), e )
735
730
736
731
return output
737
732
@@ -753,8 +748,9 @@ def encrypt_assertion(self, statement, enc_key, template, key_type='des-192', no
753
748
if isinstance (statement , SamlBase ):
754
749
statement = pre_encrypt_assertion (statement )
755
750
756
- _ , fil = make_temp (_str (statement ), decode = False ,
757
- delete = False )
751
+ _ , fil = make_temp (
752
+ _str (statement ), decode = False , delete = self ._xmlsec_delete_tmpfiles
753
+ )
758
754
_ , tmpl = make_temp (_str (template ), decode = False )
759
755
760
756
if not node_xpath :
@@ -772,15 +768,10 @@ def encrypt_assertion(self, statement, enc_key, template, key_type='des-192', no
772
768
if node_id :
773
769
com_list .extend (['--node-id' , node_id ])
774
770
775
- (_stdout , _stderr , output ) = self ._run_xmlsec (
776
- com_list ,
777
- [tmpl ],
778
- exception = EncryptError ,
779
- validate_output = False )
780
-
781
- os .unlink (fil )
782
- if not output :
783
- raise EncryptError (_stderr )
771
+ try :
772
+ (_stdout , _stderr , output ) = self ._run_xmlsec (com_list , [tmpl ])
773
+ except XmlsecError as e :
774
+ six .raise_from (EncryptError (com_list ), e )
784
775
785
776
return output .decode ('utf-8' )
786
777
@@ -803,11 +794,11 @@ def decrypt(self, enctext, key_file, id_attr):
803
794
ENC_KEY_CLASS ,
804
795
]
805
796
806
- ( _stdout , _stderr , output ) = self . _run_xmlsec (
807
- com_list ,
808
- [ fil ],
809
- exception = DecryptError ,
810
- validate_output = False )
797
+ try :
798
+ ( _stdout , _stderr , output ) = self . _run_xmlsec ( com_list , [ fil ])
799
+ except XmlsecError as e :
800
+ six . raise_from ( DecryptError ( com_list ), e )
801
+
811
802
return output .decode ('utf-8' )
812
803
813
804
def sign_statement (self , statement , node_name , key_file , node_id , id_attr ):
@@ -826,10 +817,11 @@ def sign_statement(self, statement, node_name, key_file, node_id, id_attr):
826
817
statement = str (statement )
827
818
828
819
_ , fil = make_temp (
829
- statement ,
830
- suffix = '.xml' ,
831
- decode = False ,
832
- delete = self ._xmlsec_delete_tmpfiles )
820
+ statement ,
821
+ suffix = '.xml' ,
822
+ decode = False ,
823
+ delete = self ._xmlsec_delete_tmpfiles ,
824
+ )
833
825
834
826
com_list = [
835
827
self .xmlsec ,
@@ -843,20 +835,16 @@ def sign_statement(self, statement, node_name, key_file, node_id, id_attr):
843
835
com_list .extend (['--node-id' , node_id ])
844
836
845
837
try :
846
- (stdout , stderr , signed_statement ) = self ._run_xmlsec (
847
- com_list ,
848
- [fil ],
849
- validate_output = False )
838
+ (stdout , stderr , output ) = self ._run_xmlsec (com_list , [fil ])
839
+ except XmlsecError as e :
840
+ raise SignatureError (com_list )
850
841
851
- # this doesn't work if --store-signatures are used
852
- if stdout == '' :
853
- if signed_statement :
854
- return signed_statement .decode ('utf-8' )
855
-
856
- logger .error ('Signing operation failed :\n stdout : %s\n stderr : %s' , stdout , stderr )
857
- raise SigverError (stderr )
858
- except DecryptError :
859
- raise SigverError ('Signing failed' )
842
+ # this does not work if --store-signatures is used
843
+ if output :
844
+ return output .decode ("utf-8" )
845
+ if stdout :
846
+ return stdout .decode ("utf-8" )
847
+ raise SignatureError (stderr )
860
848
861
849
def validate_signature (self , signedtext , cert_file , cert_type , node_name , node_id , id_attr ):
862
850
"""
@@ -867,17 +855,19 @@ def validate_signature(self, signedtext, cert_file, cert_type, node_name, node_i
867
855
:param cert_type: The file type of the certificate
868
856
:param node_name: The name of the class that is signed
869
857
:param node_id: The identifier of the node
870
- :param id_attr: Should normally be one of 'id', 'Id' or 'ID'
858
+ :param id_attr: The attribute name for the identifier, normally one of
859
+ 'id','Id' or 'ID'
871
860
:return: Boolean True if the signature was correct otherwise False.
872
861
"""
873
862
if not isinstance (signedtext , six .binary_type ):
874
863
signedtext = signedtext .encode ('utf-8' )
875
864
876
865
_ , fil = make_temp (
877
- signedtext ,
878
- suffix = '.xml' ,
879
- decode = False ,
880
- delete = self ._xmlsec_delete_tmpfiles )
866
+ signedtext ,
867
+ suffix = '.xml' ,
868
+ decode = False ,
869
+ delete = self ._xmlsec_delete_tmpfiles ,
870
+ )
881
871
882
872
com_list = [
883
873
self .xmlsec ,
@@ -891,21 +881,19 @@ def validate_signature(self, signedtext, cert_file, cert_type, node_name, node_i
891
881
if node_id :
892
882
com_list .extend (['--node-id' , node_id ])
893
883
894
- ( _stdout , stderr , _output ) = self . _run_xmlsec (
895
- com_list ,
896
- [ fil ],
897
- exception = SignatureError )
884
+ try :
885
+ ( _stdout , stderr , _output ) = self . _run_xmlsec ( com_list , [ fil ])
886
+ except XmlsecError as e :
887
+ six . raise_from ( SignatureError ( com_list ), e )
898
888
899
889
return parse_xmlsec_output (stderr )
900
890
901
- def _run_xmlsec (self , com_list , extra_args , validate_output = True , exception = XmlsecError ):
891
+ def _run_xmlsec (self , com_list , extra_args ):
902
892
"""
903
893
Common code to invoke xmlsec and parse the output.
904
894
:param com_list: Key-value parameter list for xmlsec
905
895
:param extra_args: Positional parameters to be appended after all
906
896
key-value parameters
907
- :param validate_output: Parse and validate the output
908
- :param exception: The exception class to raise on errors
909
897
:result: Whatever xmlsec wrote to an --output temporary file
910
898
"""
911
899
with NamedTemporaryFile (suffix = '.xml' , delete = self ._xmlsec_delete_tmpfiles ) as ntf :
@@ -919,17 +907,12 @@ def _run_xmlsec(self, com_list, extra_args, validate_output=True, exception=Xmls
919
907
p_out = p_out .decode ()
920
908
p_err = p_err .decode ()
921
909
922
- if pof .returncode is not None and pof .returncode < 0 :
923
- logger .error (LOG_LINE , p_out , p_err )
924
- raise XmlsecError ('{err_code}:{err_msg}' .format (
925
- err_code = pof .returncode , err_msg = p_err ))
926
-
927
- try :
928
- if validate_output :
929
- parse_xmlsec_output (p_err )
930
- except XmlsecError as exc :
931
- logger .error (LOG_LINE_2 , p_out , p_err , exc )
932
- raise
910
+ if pof .returncode != 0 :
911
+ errmsg = "returncode={code}\n error={err}\n output={out}" .format (
912
+ code = pof .returncode , err = p_err , out = p_out
913
+ )
914
+ logger .error (errmsg )
915
+ raise XmlsecError (errmsg )
933
916
934
917
ntf .seek (0 )
935
918
return p_out , p_err , ntf .read ()
@@ -1366,55 +1349,60 @@ def decrypt_keys(self, enctext, keys=None, id_attr=''):
1366
1349
""" Decrypting an encrypted text by the use of a private key.
1367
1350
1368
1351
:param enctext: The encrypted text as a string
1352
+ :param keys: Keys to try to decrypt enctext with
1353
+ :param id_attr: The attribute name for the identifier, normally one of
1354
+ 'id','Id' or 'ID'
1369
1355
:return: The decrypted text
1370
1356
"""
1371
- _enctext = None
1372
-
1373
- if not id_attr :
1374
- id_attr = self .id_attr
1357
+ key_files = []
1375
1358
1376
1359
if not isinstance (keys , list ):
1377
1360
keys = [keys ]
1378
1361
1379
- if self .enc_key_files is not None :
1380
- for _enc_key_file in self .enc_key_files :
1381
- _enctext = self .crypto .decrypt (enctext , _enc_key_file , id_attr )
1382
- if _enctext is not None and len (_enctext ) > 0 :
1383
- return _enctext
1384
-
1385
- for _key in keys :
1386
- if _key is not None and len (_key .strip ()) > 0 :
1387
- if not isinstance (_key , six .binary_type ):
1388
- _key = str (_key ).encode ('ascii' )
1389
- _ , key_file = make_temp (_key , decode = False )
1390
- _enctext = self .crypto .decrypt (enctext , key_file , id_attr )
1391
- if _enctext is not None and len (_enctext ) > 0 :
1392
- return _enctext
1362
+ keys = [key for key in keys if key ]
1363
+ for key in keys :
1364
+ if not isinstance (key , six .binary_type ):
1365
+ key = key .encode ("ascii" )
1366
+ _ , key_file = make_temp (key , decode = False , delete = False )
1367
+ key_files .append (key_file )
1393
1368
1394
- return enctext
1369
+ try :
1370
+ dectext = self .decrypt (enctext , key_file = key_files , id_attr = id_attr )
1371
+ except DecryptError as e :
1372
+ raise
1373
+ else :
1374
+ return dectext
1375
+ finally :
1376
+ for key_file in key_files :
1377
+ os .unlink (key_file )
1395
1378
1396
1379
def decrypt (self , enctext , key_file = None , id_attr = '' ):
1397
1380
""" Decrypting an encrypted text by the use of a private key.
1398
1381
1399
1382
:param enctext: The encrypted text as a string
1400
1383
:return: The decrypted text
1401
1384
"""
1402
- _enctext = None
1403
-
1404
1385
if not id_attr :
1405
1386
id_attr = self .id_attr
1406
1387
1407
- if self .enc_key_files is not None :
1408
- for _enc_key_file in self .enc_key_files :
1409
- _enctext = self .crypto .decrypt (enctext , _enc_key_file , id_attr )
1410
- if _enctext is not None and len (_enctext ) > 0 :
1411
- return _enctext
1388
+ if not isinstance (key_file , list ):
1389
+ key_file = [key_file ]
1390
+
1391
+ key_files = [
1392
+ key for key in itertools .chain (key_file , self .enc_key_files ) if key
1393
+ ]
1394
+ for key_file in key_files :
1395
+ try :
1396
+ dectext = self .crypto .decrypt (enctext , key_file , id_attr )
1397
+ except XmlsecError as e :
1398
+ continue
1399
+ else :
1400
+ if dectext :
1401
+ return dectext
1412
1402
1413
- if key_file is not None and len (key_file .strip ()) > 0 :
1414
- _enctext = self .crypto .decrypt (enctext , key_file , id_attr )
1415
- if _enctext is not None and len (_enctext ) > 0 :
1416
- return _enctext
1417
- return enctext
1403
+ errmsg = "No key was able to decrypt the ciphertext. Keys tried: {keys}"
1404
+ errmsg = errmsg .format (keys = key_files )
1405
+ raise DecryptError (errmsg )
1418
1406
1419
1407
def verify_signature (self , signedtext , cert_file = None , cert_type = 'pem' , node_name = NODE_NAME , node_id = None , id_attr = '' ):
1420
1408
""" Verifies the signature of a XML document.
@@ -1424,7 +1412,8 @@ def verify_signature(self, signedtext, cert_file=None, cert_type='pem', node_nam
1424
1412
:param cert_type: The file type of the certificate
1425
1413
:param node_name: The name of the class that is signed
1426
1414
:param node_id: The identifier of the node
1427
- :param id_attr: Should normally be one of 'id', 'Id' or 'ID'
1415
+ :param id_attr: The attribute name for the identifier, normally one of
1416
+ 'id','Id' or 'ID'
1428
1417
:return: Boolean True if the signature was correct otherwise False.
1429
1418
"""
1430
1419
# This is only for testing purposes, otherwise when would you receive
@@ -1466,11 +1455,14 @@ def _check_signature(self, decoded_xml, item, node_name=NODE_NAME, origdoc=None,
1466
1455
1467
1456
for cert in _certs :
1468
1457
if isinstance (cert , six .string_types ):
1469
- certs .append (make_temp (
1470
- pem_format (cert ),
1471
- suffix = '.pem' ,
1472
- decode = False ,
1473
- delete = self ._xmlsec_delete_tmpfiles ))
1458
+ certs .append (
1459
+ make_temp (
1460
+ pem_format (cert ),
1461
+ suffix = '.pem' ,
1462
+ decode = False ,
1463
+ delete = self ._xmlsec_delete_tmpfiles ,
1464
+ )
1465
+ )
1474
1466
else :
1475
1467
certs .append (cert )
1476
1468
else :
@@ -1483,7 +1475,8 @@ def _check_signature(self, decoded_xml, item, node_name=NODE_NAME, origdoc=None,
1483
1475
pem_format (cert ),
1484
1476
suffix = '.pem' ,
1485
1477
decode = False ,
1486
- delete = self ._xmlsec_delete_tmpfiles )
1478
+ delete = self ._xmlsec_delete_tmpfiles ,
1479
+ )
1487
1480
for cert in cert_from_instance (item )
1488
1481
]
1489
1482
else :
@@ -1527,7 +1520,8 @@ def check_signature(self, item, node_name=NODE_NAME, origdoc=None, id_attr='', m
1527
1520
:param item: Parsed entity
1528
1521
:param node_name: The name of the node/class/element that is signed
1529
1522
:param origdoc: The original XML string
1530
- :param id_attr:
1523
+ :param id_attr: The attribute name for the identifier, normally one of
1524
+ 'id','Id' or 'ID'
1531
1525
:param must:
1532
1526
:return:
1533
1527
"""
0 commit comments