@@ -50,6 +50,9 @@ class XMLSecurityKey
50
50
const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc ' ;
51
51
const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc ' ;
52
52
const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc ' ;
53
+ const AES128_GCM = 'http://www.w3.org/2009/xmlenc11#aes128-gcm ' ;
54
+ const AES192_GCM = 'http://www.w3.org/2009/xmlenc11#aes192-gcm ' ;
55
+ const AES256_GCM = 'http://www.w3.org/2009/xmlenc11#aes256-gcm ' ;
53
56
const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5 ' ;
54
57
const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p ' ;
55
58
const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1 ' ;
@@ -58,6 +61,7 @@ class XMLSecurityKey
58
61
const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384 ' ;
59
62
const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 ' ;
60
63
const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1 ' ;
64
+ const AUTHTAG_LENGTH = 16 ;
61
65
62
66
/** @var array */
63
67
private $ cryptParams = array ();
@@ -142,6 +146,30 @@ public function __construct($type, $params=null)
142
146
$ this ->cryptParams ['keysize ' ] = 32 ;
143
147
$ this ->cryptParams ['blocksize ' ] = 16 ;
144
148
break ;
149
+ case (self ::AES128_GCM ):
150
+ $ this ->cryptParams ['library ' ] = 'openssl ' ;
151
+ $ this ->cryptParams ['cipher ' ] = 'aes-128-gcm ' ;
152
+ $ this ->cryptParams ['type ' ] = 'symmetric ' ;
153
+ $ this ->cryptParams ['method ' ] = 'http://www.w3.org/2009/xmlenc11#aes128-gcm ' ;
154
+ $ this ->cryptParams ['keysize ' ] = 32 ;
155
+ $ this ->cryptParams ['blocksize ' ] = 16 ;
156
+ break ;
157
+ case (self ::AES192_GCM ):
158
+ $ this ->cryptParams ['library ' ] = 'openssl ' ;
159
+ $ this ->cryptParams ['cipher ' ] = 'aes-192-gcm ' ;
160
+ $ this ->cryptParams ['type ' ] = 'symmetric ' ;
161
+ $ this ->cryptParams ['method ' ] = 'http://www.w3.org/2009/xmlenc11#aes192-gcm ' ;
162
+ $ this ->cryptParams ['keysize ' ] = 32 ;
163
+ $ this ->cryptParams ['blocksize ' ] = 16 ;
164
+ break ;
165
+ case (self ::AES256_GCM ):
166
+ $ this ->cryptParams ['library ' ] = 'openssl ' ;
167
+ $ this ->cryptParams ['cipher ' ] = 'aes-256-gcm ' ;
168
+ $ this ->cryptParams ['type ' ] = 'symmetric ' ;
169
+ $ this ->cryptParams ['method ' ] = 'http://www.w3.org/2009/xmlenc11#aes256-gcm ' ;
170
+ $ this ->cryptParams ['keysize ' ] = 32 ;
171
+ $ this ->cryptParams ['blocksize ' ] = 16 ;
172
+ break ;
145
173
case (self ::RSA_1_5 ):
146
174
$ this ->cryptParams ['library ' ] = 'openssl ' ;
147
175
$ this ->cryptParams ['padding ' ] = OPENSSL_PKCS1_PADDING ;
@@ -397,12 +425,22 @@ private function unpadISO10126($data)
397
425
private function encryptSymmetric ($ data )
398
426
{
399
427
$ this ->iv = openssl_random_pseudo_bytes (openssl_cipher_iv_length ($ this ->cryptParams ['cipher ' ]));
400
- $ data = $ this ->padISO10126 ($ data , $ this ->cryptParams ['blocksize ' ]);
401
- $ encrypted = openssl_encrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , $ this ->iv );
428
+ $ authTag = null ;
429
+ if (in_array ($ this ->cryptParams ['cipher ' ], ['aes-128-gcm ' , 'aes-192-gcm ' , 'aes-256-gcm ' ])) {
430
+ if (version_compare (PHP_VERSION , '7.1.0 ' ) < 0 ) {
431
+ throw new Exception ('PHP 7.1.0 is required to use AES GCM algorithms ' );
432
+ }
433
+ $ authTag = openssl_random_pseudo_bytes (self ::AUTHTAG_LENGTH );
434
+ $ encrypted = openssl_encrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA , $ this ->iv , $ authTag );
435
+ } else {
436
+ $ data = $ this ->padISO10126 ($ data , $ this ->cryptParams ['blocksize ' ]);
437
+ $ encrypted = openssl_encrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , $ this ->iv );
438
+ }
439
+
402
440
if (false === $ encrypted ) {
403
441
throw new Exception ('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string ());
404
442
}
405
- return $ this ->iv . $ encrypted ;
443
+ return $ this ->iv . $ encrypted . $ authTag ;
406
444
}
407
445
408
446
/**
@@ -416,11 +454,24 @@ private function decryptSymmetric($data)
416
454
$ iv_length = openssl_cipher_iv_length ($ this ->cryptParams ['cipher ' ]);
417
455
$ this ->iv = substr ($ data , 0 , $ iv_length );
418
456
$ data = substr ($ data , $ iv_length );
419
- $ decrypted = openssl_decrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , $ this ->iv );
457
+ $ authTag = null ;
458
+ if (in_array ($ this ->cryptParams ['cipher ' ], ['aes-128-gcm ' , 'aes-192-gcm ' , 'aes-256-gcm ' ])) {
459
+ if (version_compare (PHP_VERSION , '7.1.0 ' ) < 0 ) {
460
+ throw new Exception ('PHP 7.1.0 is required to use AES GCM algorithms ' );
461
+ }
462
+ // obtain and remove the authentication tag
463
+ $ offset = 0 - self ::AUTHTAG_LENGTH ;
464
+ $ authTag = substr ($ data , $ offset );
465
+ $ data = substr ($ data , 0 , $ offset );
466
+ $ decrypted = openssl_decrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA , $ this ->iv , $ authTag );
467
+ } else {
468
+ $ decrypted = openssl_decrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , $ this ->iv );
469
+ }
470
+
420
471
if (false === $ decrypted ) {
421
472
throw new Exception ('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string ());
422
473
}
423
- return $ this ->unpadISO10126 ($ decrypted );
474
+ return null !== $ authTag ? $ decrypted : $ this ->unpadISO10126 ($ decrypted );
424
475
}
425
476
426
477
/**
0 commit comments