Skip to content

Commit 4a9c569

Browse files
authored
Merge pull request #697 from ephraim/master
Removed security policies with Sha-1 algorithm
2 parents b934a49 + 10131b7 commit 4a9c569

10 files changed

Lines changed: 223 additions & 97 deletions

File tree

examples/client_to_prosys.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def event_notification(self, event):
2525
logging.basicConfig(level=logging.DEBUG)
2626
client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/")
2727
#client = Client("opc.tcp://olivier:olivierpass@localhost:53530/OPCUA/SimulationServer/")
28-
#client.set_security_string("Basic256,SignAndEncrypt,certificate-example.der,private-key-example.pem")
28+
#client.set_security_string("Basic256Sha256,SignAndEncrypt,certificate-example.der,private-key-example.pem")
2929
try:
3030
client.connect()
3131
root = client.get_root_node()

examples/client_to_prosys_crypto.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
if __name__ == "__main__":
1111
logging.basicConfig(level=logging.DEBUG)
1212
client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/")
13-
client.set_security_string("Basic256,Sign,certificate-example.der,private-key-example.pem")
13+
client.set_security_string("Basic256Sha256,Sign,certificate-example.der,private-key-example.pem")
1414
try:
1515
client.connect()
1616
root = client.get_root_node()

examples/server-example.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,8 @@ def run(self):
9191
# set all possible endpoint policies for clients to connect through
9292
server.set_security_policy([
9393
ua.SecurityPolicyType.NoSecurity,
94-
ua.SecurityPolicyType.Basic128Rsa15_SignAndEncrypt,
95-
ua.SecurityPolicyType.Basic128Rsa15_Sign,
96-
ua.SecurityPolicyType.Basic256_SignAndEncrypt,
97-
ua.SecurityPolicyType.Basic256_Sign])
94+
ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt,
95+
ua.SecurityPolicyType.Basic256Sha256_Sign])
9896

9997
# setup our own namespace
10098
uri = "http://examples.freeopcua.github.io"

opcua/client/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def set_security_string(self, string):
159159
"""
160160
Set SecureConnection mode. String format:
161161
Policy,Mode,certificate,private_key[,server_private_key]
162-
where Policy is Basic128Rsa15 or Basic256,
162+
where Policy is Basic128Rsa15, Basic256 or Basic256Sha256,
163163
Mode is Sign or SignAndEncrypt
164164
certificate, private_key and server_private_key are
165165
paths to .pem or .der files

opcua/crypto/security_policies.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import logging
2+
13
from abc import ABCMeta, abstractmethod
24
from opcua.ua import CryptographyNone, SecurityPolicy
35
from opcua.ua import MessageSecurityMode
@@ -307,9 +309,63 @@ def encrypted_block_size(self):
307309
def decrypt(self, data):
308310
return uacrypto.cipher_decrypt(self.cipher, data)
309311

312+
class SignerSha256(Signer):
313+
314+
def __init__(self, client_pk):
315+
require_cryptography(self)
316+
self.client_pk = client_pk
317+
self.key_size = self.client_pk.key_size // 8
318+
319+
def signature_size(self):
320+
return self.key_size
321+
322+
def signature(self, data):
323+
return uacrypto.sign_sha256(self.client_pk, data)
324+
325+
class VerifierSha256(Verifier):
326+
327+
def __init__(self, server_cert):
328+
require_cryptography(self)
329+
self.server_cert = server_cert
330+
self.key_size = self.server_cert.public_key().key_size // 8
331+
332+
def signature_size(self):
333+
return self.key_size
334+
335+
def verify(self, data, signature):
336+
uacrypto.verify_sha256(self.server_cert, data, signature)
337+
338+
class SignerHMac256(Signer):
339+
340+
def __init__(self, key):
341+
require_cryptography(self)
342+
self.key = key
343+
344+
def signature_size(self):
345+
return uacrypto.sha256_size()
346+
347+
def signature(self, data):
348+
return uacrypto.hmac_sha256(self.key, data)
349+
350+
351+
class VerifierHMac256(Verifier):
352+
353+
def __init__(self, key):
354+
require_cryptography(self)
355+
self.key = key
356+
357+
def signature_size(self):
358+
return uacrypto.sha256_size()
359+
360+
def verify(self, data, signature):
361+
expected = uacrypto.hmac_sha256(self.key, data)
362+
if signature != expected:
363+
raise uacrypto.InvalidSignature
310364

311365
class SecurityPolicyBasic128Rsa15(SecurityPolicy):
312366
"""
367+
DEPRECATED, do not use anymore!
368+
313369
Security Basic 128Rsa15
314370
A suite of algorithms that uses RSA15 as Key-Wrap-algorithm
315371
and 128-Bit (16 bytes) for encryption algorithms.
@@ -344,6 +400,9 @@ def encrypt_asymmetric(pubkey, data):
344400
return uacrypto.encrypt_rsa15(pubkey, data)
345401

346402
def __init__(self, server_cert, client_cert, client_pk, mode):
403+
logger = logging.getLogger(__name__)
404+
logger.warning("DEPRECATED! Do not use SecurityPolicyBasic128Rsa15 anymore!")
405+
347406
require_cryptography(self)
348407
if isinstance(server_cert, bytes):
349408
server_cert = uacrypto.x509_from_der(server_cert)
@@ -376,6 +435,8 @@ def make_symmetric_key(self, nonce1, nonce2):
376435

377436
class SecurityPolicyBasic256(SecurityPolicy):
378437
"""
438+
DEPRECATED, do not use anymore!
439+
379440
Security Basic 256
380441
A suite of algorithms that are for 256-Bit (32 bytes) encryption,
381442
algorithms include:
@@ -410,6 +471,9 @@ def encrypt_asymmetric(pubkey, data):
410471
return uacrypto.encrypt_rsa_oaep(pubkey, data)
411472

412473
def __init__(self, server_cert, client_cert, client_pk, mode):
474+
logger = logging.getLogger(__name__)
475+
logger.warning("DEPRECATED! Do not use SecurityPolicyBasic256 anymore!")
476+
413477
require_cryptography(self)
414478
if isinstance(server_cert, bytes):
415479
server_cert = uacrypto.x509_from_der(server_cert)
@@ -440,14 +504,78 @@ def make_symmetric_key(self, nonce1, nonce2):
440504
self.symmetric_cryptography.Verifier = VerifierAesCbc(sigkey)
441505
self.symmetric_cryptography.Decryptor = DecryptorAesCbc(key, init_vec)
442506

507+
class SecurityPolicyBasic256Sha256(SecurityPolicy):
508+
"""
509+
Security Basic 256Sha256
510+
A suite of algorithms that uses Sha256 as Key-Wrap-algorithm
511+
and 256-Bit (32 bytes) for encryption algorithms.
512+
513+
- SymmetricSignatureAlgorithm_HMAC-SHA2-256
514+
https://tools.ietf.org/html/rfc4634
515+
- SymmetricEncryptionAlgorithm_AES256-CBC
516+
http://www.w3.org/2001/04/xmlenc#aes256-cbc
517+
- AsymmetricSignatureAlgorithm_RSA-PKCS15-SHA2-256
518+
http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
519+
- AsymmetricEncryptionAlgorithm_RSA-OAEP-SHA1
520+
http://www.w3.org/2001/04/xmlenc#rsa-oaep
521+
- KeyDerivationAlgorithm_P-SHA2-256
522+
http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512/dk/p_sha256
523+
- CertificateSignatureAlgorithm_RSA-PKCS15-SHA2-256
524+
http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
525+
- Basic256Sha256_Limits
526+
-> DerivedSignatureKeyLength: 256 bits
527+
-> MinAsymmetricKeyLength: 2048 bits
528+
-> MaxAsymmetricKeyLength: 4096 bits
529+
-> SecureChannelNonceLength: 32 bytes
530+
"""
531+
532+
URI = "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"
533+
signature_key_size = 32
534+
symmetric_key_size = 32
535+
AsymmetricEncryptionURI = "http://www.w3.org/2001/04/xmlenc#rsa-oaep"
536+
537+
@staticmethod
538+
def encrypt_asymmetric(pubkey, data):
539+
return uacrypto.encrypt_rsa_oaep(pubkey, data)
540+
541+
def __init__(self, server_cert, client_cert, client_pk, mode):
542+
require_cryptography(self)
543+
if isinstance(server_cert, bytes):
544+
server_cert = uacrypto.x509_from_der(server_cert)
545+
# even in Sign mode we need to asymmetrically encrypt secrets
546+
# transmitted in OpenSecureChannel. So SignAndEncrypt here
547+
self.asymmetric_cryptography = Cryptography(
548+
MessageSecurityMode.SignAndEncrypt)
549+
self.asymmetric_cryptography.Signer = SignerSha256(client_pk)
550+
self.asymmetric_cryptography.Verifier = VerifierSha256(server_cert)
551+
self.asymmetric_cryptography.Encryptor = EncryptorRsa(
552+
server_cert, uacrypto.encrypt_rsa_oaep, 42)
553+
self.asymmetric_cryptography.Decryptor = DecryptorRsa(
554+
client_pk, uacrypto.decrypt_rsa_oaep, 42)
555+
self.symmetric_cryptography = Cryptography(mode)
556+
self.Mode = mode
557+
self.server_certificate = uacrypto.der_from_x509(server_cert)
558+
self.client_certificate = uacrypto.der_from_x509(client_cert)
559+
560+
def make_symmetric_key(self, nonce1, nonce2):
561+
# specs part 6, 6.7.5
562+
key_sizes = (self.signature_key_size, self.symmetric_key_size, 16)
563+
564+
(sigkey, key, init_vec) = uacrypto.p_sha256(nonce2, nonce1, key_sizes)
565+
self.symmetric_cryptography.Signer = SignerHMac256(sigkey)
566+
self.symmetric_cryptography.Encryptor = EncryptorAesCbc(key, init_vec)
567+
568+
(sigkey, key, init_vec) = uacrypto.p_sha256(nonce1, nonce2, key_sizes)
569+
self.symmetric_cryptography.Verifier = VerifierHMac256(sigkey)
570+
self.symmetric_cryptography.Decryptor = DecryptorAesCbc(key, init_vec)
443571

444572
def encrypt_asymmetric(pubkey, data, policy_uri):
445573
"""
446574
Encrypt data with pubkey using an asymmetric algorithm.
447575
The algorithm is selected by policy_uri.
448576
Returns a tuple (encrypted_data, algorithm_uri)
449577
"""
450-
for cls in [SecurityPolicyBasic256, SecurityPolicyBasic128Rsa15]:
578+
for cls in [SecurityPolicyBasic256Sha256, SecurityPolicyBasic256, SecurityPolicyBasic128Rsa15]:
451579
if policy_uri == cls.URI:
452580
return (cls.encrypt_asymmetric(pubkey, data),
453581
cls.AsymmetricEncryptionURI)

opcua/crypto/uacrypto.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,27 @@ def sign_sha1(private_key, data):
4949
hashes.SHA1()
5050
)
5151

52+
def sign_sha256(private_key, data):
53+
return private_key.sign(
54+
data,
55+
padding.PKCS1v15(),
56+
hashes.SHA256()
57+
)
58+
5259
def verify_sha1(certificate, data, signature):
5360
certificate.public_key().verify(
5461
signature,
5562
data,
5663
padding.PKCS1v15(),
5764
hashes.SHA1())
5865

66+
def verify_sha256(certificate, data, signature):
67+
certificate.public_key().verify(
68+
signature,
69+
data,
70+
padding.PKCS1v15(),
71+
hashes.SHA256())
72+
5973
def encrypt_basic256(public_key, data):
6074
ciphertext = public_key.encrypt(
6175
data,
@@ -124,10 +138,16 @@ def hmac_sha1(key, message):
124138
hasher.update(message)
125139
return hasher.finalize()
126140

141+
def hmac_sha256(key, message):
142+
hasher = hmac.HMAC(key, hashes.SHA256(), backend=default_backend())
143+
hasher.update(message)
144+
return hasher.finalize()
127145

128146
def sha1_size():
129147
return hashes.SHA1.digest_size
130148

149+
def sha256_size():
150+
return hashes.SHA256.digest_size
131151

132152
def p_sha1(secret, seed, sizes=()):
133153
"""
@@ -151,6 +171,27 @@ def p_sha1(secret, seed, sizes=()):
151171
result = result[size:]
152172
return tuple(parts)
153173

174+
def p_sha256(secret, seed, sizes=()):
175+
"""
176+
Derive one or more keys from secret and seed.
177+
(See specs part 6, 6.7.5 and RFC 2246 - TLS v1.0)
178+
Lengths of keys will match sizes argument
179+
"""
180+
full_size = 0
181+
for size in sizes:
182+
full_size += size
183+
184+
result = b''
185+
accum = seed
186+
while len(result) < full_size:
187+
accum = hmac_sha256(secret, accum)
188+
result += hmac_sha256(secret, accum + seed)
189+
190+
parts = []
191+
for size in sizes:
192+
parts.append(result[:size])
193+
result = result[size:]
194+
return tuple(parts)
154195

155196
def x509_name_to_string(name):
156197
parts = ["{0}={1}".format(attr.oid._name, attr.value) for attr in name]
@@ -174,6 +215,6 @@ def x509_to_string(cert):
174215
cert = load_certificate("../examples/server_cert.pem")
175216
#rsa_pubkey = pubkey_from_dercert(der)
176217
rsa_privkey = load_private_key("../examples/mykey.pem")
177-
218+
178219
from IPython import embed
179220
embed()

0 commit comments

Comments
 (0)