33
33
from Crypto .Util .asn1 import DerSequence
34
34
from Crypto .PublicKey import RSA
35
35
from saml2 .cert import OpenSSLWrapper
36
+ from saml2 .saml import EncryptedAssertion
36
37
from saml2 .samlp import Response
37
38
38
39
import xmldsig as ds
70
71
RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
71
72
RSA_1_5 = "http://www.w3.org/2001/04/xmlenc#rsa-1_5"
72
73
TRIPLE_DES_CBC = "http://www.w3.org/2001/04/xmlenc#tripledes-cbc"
73
-
74
+ XMLTAG = "<?xml version='1.0'?>"
75
+ PREFIX = "<?xml version='1.0' encoding='UTF-8'?>"
74
76
75
77
76
78
class SigverError (SAMLError ):
@@ -97,6 +99,10 @@ class DecryptError(XmlsecError):
97
99
pass
98
100
99
101
102
+ class EncryptError (XmlsecError ):
103
+ pass
104
+
105
+
100
106
class BadSignature (SigverError ):
101
107
"""The signature is invalid."""
102
108
pass
@@ -106,6 +112,25 @@ class CertificateError(SigverError):
106
112
pass
107
113
108
114
115
+ def rm_xmltag (statement ):
116
+ try :
117
+ _t = statement .startswith (XMLTAG )
118
+ except TypeError :
119
+ statement = statement .decode ("utf8" )
120
+ _t = statement .startswith (XMLTAG )
121
+
122
+ if _t :
123
+ statement = statement [len (XMLTAG ):]
124
+ if statement [0 ] == '\n ' :
125
+ statement = statement [1 :]
126
+ elif statement .startswith (PREFIX ):
127
+ statement = statement [len (PREFIX ):]
128
+ if statement [0 ] == '\n ' :
129
+ statement = statement [1 :]
130
+
131
+ return statement
132
+
133
+
109
134
def signed (item ):
110
135
if SIG in item .c_children .keys () and item .signature :
111
136
return True
@@ -228,7 +253,7 @@ def _instance(klass, ava, seccont, base64encode=False, elements_to_sign=None):
228
253
instance = klass ()
229
254
230
255
for prop in instance .c_attributes .values ():
231
- #print "# %s" % (prop)
256
+ #print "# %s" % (prop)
232
257
if prop in ava :
233
258
if isinstance (ava [prop ], bool ):
234
259
setattr (instance , prop , "%s" % ava [prop ])
@@ -290,6 +315,7 @@ def signed_instance_factory(instance, seccont, elements_to_sign=None):
290
315
else :
291
316
return instance
292
317
318
+
293
319
# --------------------------------------------------------------------------
294
320
295
321
@@ -305,7 +331,7 @@ def create_id():
305
331
return ret
306
332
307
333
308
- def make_temp (string , suffix = "" , decode = True ):
334
+ def make_temp (string , suffix = "" , decode = True , delete = True ):
309
335
""" xmlsec needs files in some cases where only strings exist, hence the
310
336
need for this function. It creates a temporary file with the
311
337
string as only content.
@@ -319,7 +345,7 @@ def make_temp(string, suffix="", decode=True):
319
345
close the file) and filename (which is for instance needed by the
320
346
xmlsec function).
321
347
"""
322
- ntf = NamedTemporaryFile (suffix = suffix )
348
+ ntf = NamedTemporaryFile (suffix = suffix , delete = delete )
323
349
if decode :
324
350
ntf .write (base64 .b64decode (string ))
325
351
else :
@@ -428,6 +454,7 @@ def cert_from_instance(instance):
428
454
ignore_age = True )
429
455
return []
430
456
457
+
431
458
# =============================================================================
432
459
433
460
@@ -469,12 +496,14 @@ def key_from_key_value_dict(key_info):
469
496
res .append (key )
470
497
return res
471
498
499
+
472
500
# =============================================================================
473
501
474
502
475
503
#def rsa_load(filename):
476
504
# """Read a PEM-encoded RSA key pair from a file."""
477
- # return M2Crypto.RSA.load_key(filename, M2Crypto.util.no_passphrase_callback)
505
+ # return M2Crypto.RSA.load_key(filename, M2Crypto.util
506
+ # .no_passphrase_callback)
478
507
#
479
508
#
480
509
#def rsa_loads(key):
@@ -594,10 +623,12 @@ def verify_redirect_signature(info, cert):
594
623
_order = RESP_ORDER
595
624
else :
596
625
raise Unsupported (
597
- "Verifying signature on something that should not be signed" )
626
+ "Verifying signature on something that should not be "
627
+ "signed" )
598
628
args = info .copy ()
599
629
del args ["Signature" ] # everything but the signature
600
- string = "&" .join ([urllib .urlencode ({k : args [k ][0 ]}) for k in _order ])
630
+ string = "&" .join (
631
+ [urllib .urlencode ({k : args [k ][0 ]}) for k in _order ])
601
632
_key = extract_rsa_key_from_x509_cert (pem_format (cert ))
602
633
_sign = base64 .b64decode (info ["Signature" ][0 ])
603
634
try :
@@ -660,6 +691,9 @@ def version(self):
660
691
def encrypt (self , text , recv_key , template , key_type ):
661
692
raise NotImplementedError ()
662
693
694
+ def encrypt_assertion (self , statement , recv_key , key_type ):
695
+ raise NotImplementedError ()
696
+
663
697
def decrypt (self , enctext , key_file ):
664
698
raise NotImplementedError ()
665
699
@@ -672,6 +706,10 @@ def validate_signature(self, enctext, cert_file, cert_type, node_name,
672
706
raise NotImplementedError ()
673
707
674
708
709
+ ASSERT_XPATH = '' .join (["/*[local-name()=\" %s\" ]" % v for v in [
710
+ "Response" , "EncryptedAssertion" , "Assertion" ]])
711
+
712
+
675
713
class CryptoBackendXmlSec1 (CryptoBackend ):
676
714
"""
677
715
CryptoBackend implementation using external binary xmlsec1 to sign
@@ -705,6 +743,38 @@ def encrypt(self, text, recv_key, template, key_type):
705
743
validate_output = False )
706
744
return output
707
745
746
+ def encrypt_assertion (self , statement , enc_key , template ,
747
+ key_type = "des-192" ):
748
+ """
749
+ --pubkey-cert-pem ../../example/idp2/pki/mycert.pem \
750
+ --session-key des-192 --xml-data pre_saml2_assertion.xml \
751
+ --node-xpath '/*[local-name()="Response"]/*[local-name(
752
+ )="EncryptedAssertion"]/*[local-name()="Assertion"]' \
753
+ enc-element-3des-kt-rsa1_5.tmpl > enc_3des_rsa_assertion.xml
754
+
755
+ :param statement:
756
+ :param cert_file:
757
+ :param cert_type:
758
+ :return:
759
+ """
760
+ statement = pre_encrypt_assertion (statement )
761
+ _ , fil = make_temp ("%s" % statement , decode = False , delete = False )
762
+ _ , tmpl = make_temp ("%s" % template , decode = False )
763
+
764
+ com_list = [self .xmlsec , "encrypt" , "--pubkey-cert-pem" , enc_key ,
765
+ "--session-key" , key_type , "--xml-data" , fil ,
766
+ "--node-xpath" , ASSERT_XPATH ]
767
+
768
+ (_stdout , _stderr , output ) = self ._run_xmlsec (com_list , [tmpl ],
769
+ exception = EncryptError ,
770
+ validate_output = False )
771
+
772
+ os .unlink (fil )
773
+ if not output :
774
+ raise EncryptError (_stderr )
775
+
776
+ return output
777
+
708
778
def decrypt (self , enctext , key_file ):
709
779
logger .debug ("Decrypt input len: %d" % len (enctext ))
710
780
_ , fil = make_temp ("%s" % enctext , decode = False )
@@ -1005,7 +1075,7 @@ def __init__(self, security_context, cert_file=None, cert_type="pem",
1005
1075
self ._cert_info = None
1006
1076
self ._generate_cert_func_active = False
1007
1077
if generate_cert_info is not None and len (self ._cert_str ) > 0 and \
1008
- len (self ._key_str ) > 0 and tmp_key_file is not \
1078
+ len (self ._key_str ) > 0 and tmp_key_file is not \
1009
1079
None and tmp_cert_file is not None :
1010
1080
self ._generate_cert = True
1011
1081
self ._cert_info = generate_cert_info
@@ -1037,9 +1107,12 @@ def update_cert(self, active=False, client_crt=None):
1037
1107
elif self ._cert_handler_extra_class is not None and \
1038
1108
self ._cert_handler_extra_class .use_generate_cert_func ():
1039
1109
(self ._tmp_cert_str , self ._tmp_key_str ) = \
1040
- self ._cert_handler_extra_class .generate_cert (self ._cert_info , self ._cert_str , self ._key_str )
1110
+ self ._cert_handler_extra_class .generate_cert (
1111
+ self ._cert_info , self ._cert_str , self ._key_str )
1041
1112
else :
1042
- self ._tmp_cert_str , self ._tmp_key_str = self ._osw .create_certificate (self ._cert_info , request = True )
1113
+ self ._tmp_cert_str , self ._tmp_key_str = self ._osw \
1114
+ .create_certificate (
1115
+ self ._cert_info , request = True )
1043
1116
self ._tmp_cert_str = self ._osw .create_cert_signed_certificate (
1044
1117
self ._cert_str , self ._key_str , self ._tmp_cert_str )
1045
1118
valid , mess = self ._osw .verify (self ._cert_str ,
@@ -1123,6 +1196,19 @@ def encrypt(self, text, recv_key="", template="", key_type=""):
1123
1196
1124
1197
return self .crypto .encrypt (text , recv_key , template , key_type )
1125
1198
1199
+ def encrypt_assertion (self , statement , cert_file , cert_type = "pem" ):
1200
+ """
1201
+ --pubkey-cert-pem ../../example/idp2/pki/mycert.pem \
1202
+ --session-key des-192 --xml-data pre_saml2_assertion.xml \
1203
+ --node-xpath '/*[local-name()="Response"]/*[local-name(
1204
+ )="EncryptedAssertion"]/*[local-name()="Assertion"]' \
1205
+ enc-element-3des-kt-rsa1_5.tmpl > enc_3des_rsa_assertion.xml
1206
+ :param statement:
1207
+ :param cert_file:
1208
+ :param cert_type:
1209
+ :return:
1210
+ """
1211
+
1126
1212
def decrypt (self , enctext ):
1127
1213
""" Decrypting an encrypted text by the use of a private key.
1128
1214
@@ -1237,7 +1323,6 @@ def _check_signature(self, decoded_xml, item, node_name=NODE_NAME,
1237
1323
if not self .cert_handler .verify_cert (last_pem_file ):
1238
1324
raise CertificateError ("Invalid certificate!" )
1239
1325
1240
-
1241
1326
return item
1242
1327
1243
1328
def check_signature (self , item , node_name = NODE_NAME , origdoc = None ,
@@ -1390,9 +1475,10 @@ def correctly_signed_response(self, decoded_xml, must=False, origdoc=None):
1390
1475
origdoc )
1391
1476
1392
1477
if isinstance (response , Response ) and (response .assertion or
1393
- response .encrypted_assertion ):
1478
+ response .encrypted_assertion ):
1394
1479
# Try to find the signing cert in the assertion
1395
- for assertion in (response .assertion or response .encrypted_assertion ):
1480
+ for assertion in (
1481
+ response .assertion or response .encrypted_assertion ):
1396
1482
if response .encrypted_assertion :
1397
1483
decoded_xml = self .decrypt (
1398
1484
assertion .encrypted_data .to_string ())
@@ -1551,28 +1637,81 @@ def pre_signature_part(ident, public_key=None, identifier=None):
1551
1637
return signature
1552
1638
1553
1639
1554
- def pre_encryption_part (msg_enc = TRIPLE_DES_CBC , key_enc = RSA_1_5 ):
1640
+ # <?xml version="1.0" encoding="UTF-8"?>
1641
+ # <EncryptedData Id="ED" Type="http://www.w3.org/2001/04/xmlenc#Element"
1642
+ # xmlns="http://www.w3.org/2001/04/xmlenc#">
1643
+ # <EncryptionMethod Algorithm="http://www.w3
1644
+ # .org/2001/04/xmlenc#tripledes-cbc"/>
1645
+ # <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
1646
+ # <EncryptedKey Id="EK" xmlns="http://www.w3.org/2001/04/xmlenc#">
1647
+ # <EncryptionMethod Algorithm="http://www.w3
1648
+ # .org/2001/04/xmlenc#rsa-1_5"/>
1649
+ # <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
1650
+ # <ds:KeyName>my-rsa-key</ds:KeyName>
1651
+ # </ds:KeyInfo>
1652
+ # <CipherData>
1653
+ # <CipherValue>
1654
+ # </CipherValue>
1655
+ # </CipherData>
1656
+ # <ReferenceList>
1657
+ # <DataReference URI="#ED"/>
1658
+ # </ReferenceList>
1659
+ # </EncryptedKey>
1660
+ # </ds:KeyInfo>
1661
+ # <CipherData>
1662
+ # <CipherValue>
1663
+ # </CipherValue>
1664
+ # </CipherData>
1665
+ # </EncryptedData>
1666
+
1667
+ def pre_encryption_part (msg_enc = TRIPLE_DES_CBC , key_enc = RSA_1_5 ,
1668
+ key_name = "my-rsa-key" ):
1555
1669
"""
1556
1670
1557
1671
:param msg_enc:
1558
1672
:param key_enc:
1673
+ :param key_name:
1559
1674
:return:
1560
1675
"""
1561
1676
msg_encryption_method = EncryptionMethod (algorithm = msg_enc )
1562
1677
key_encryption_method = EncryptionMethod (algorithm = key_enc )
1563
- encrypted_key = EncryptedKey (encryption_method = key_encryption_method ,
1678
+ encrypted_key = EncryptedKey (id = "EK" ,
1679
+ encryption_method = key_encryption_method ,
1564
1680
key_info = ds .KeyInfo (
1565
- key_name = ds .KeyName (text = "" )),
1681
+ key_name = ds .KeyName (text = key_name )),
1566
1682
cipher_data = CipherData (
1567
1683
cipher_value = CipherValue (text = "" )))
1568
1684
key_info = ds .KeyInfo (encrypted_key = encrypted_key )
1569
1685
encrypted_data = EncryptedData (
1686
+ id = "ED" ,
1687
+ type = "http://www.w3.org/2001/04/xmlenc#Element" ,
1570
1688
encryption_method = msg_encryption_method ,
1571
1689
key_info = key_info ,
1572
1690
cipher_data = CipherData (cipher_value = CipherValue (text = "" )))
1573
1691
return encrypted_data
1574
1692
1575
1693
1694
+ def pre_encrypt_assertion (response ):
1695
+ """
1696
+ Move the assertion to within a encrypted_assertion
1697
+ :param response: The response with one assertion
1698
+ :return: The response but now with the assertion within an
1699
+ encrypted_assertion.
1700
+ """
1701
+ assertion = response .assertion
1702
+ response .assertion = None
1703
+ response .encrypted_assertion = EncryptedAssertion ()
1704
+ response .encrypted_assertion .add_extension_element (assertion )
1705
+ # txt = "%s" % response
1706
+ # _ass = "%s" % assertion
1707
+ # _ass = rm_xmltag(_ass)
1708
+ # txt.replace(
1709
+ # "<ns1:EncryptedAssertion/>",
1710
+ # "<ns1:EncryptedAssertion>%s</ns1:EncryptedAssertion>" % _ass)
1711
+
1712
+ return response
1713
+
1714
+
1576
1715
def response_factory (sign = False , encrypt = False , ** kwargs ):
1577
1716
response = samlp .Response (id = sid (), version = VERSION ,
1578
1717
issue_instant = instant ())
0 commit comments