77/**
88 * xmlseclibs.php
99 *
10- * Copyright (c) 2007-2019 , Robert Richards <rrichards@cdatazone.org>.
10+ * Copyright (c) 2007-2020 , Robert Richards <rrichards@cdatazone.org>.
1111 * All rights reserved.
1212 *
1313 * Redistribution and use in source and binary forms, with or without
4040 * POSSIBILITY OF SUCH DAMAGE.
4141 *
4242 * @author Robert Richards <rrichards@cdatazone.org>
43- * @copyright 2007-2019 Robert Richards <rrichards@cdatazone.org>
43+ * @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org>
4444 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
4545 */
4646
@@ -50,14 +50,19 @@ class XMLSecurityKey
5050 const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc ' ;
5151 const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc ' ;
5252 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 ' ;
5356 const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5 ' ;
5457 const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p ' ;
58+ const RSA_OAEP = 'http://www.w3.org/2009/xmlenc11#rsa-oaep ' ;
5559 const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1 ' ;
5660 const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1 ' ;
5761 const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 ' ;
5862 const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384 ' ;
5963 const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 ' ;
6064 const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1 ' ;
65+ const AUTHTAG_LENGTH = 16 ;
6166
6267 /** @var array */
6368 private $ cryptParams = array ();
@@ -142,6 +147,30 @@ public function __construct($type, $params=null)
142147 $ this ->cryptParams ['keysize ' ] = 32 ;
143148 $ this ->cryptParams ['blocksize ' ] = 16 ;
144149 break ;
150+ case (self ::AES128_GCM ):
151+ $ this ->cryptParams ['library ' ] = 'openssl ' ;
152+ $ this ->cryptParams ['cipher ' ] = 'aes-128-gcm ' ;
153+ $ this ->cryptParams ['type ' ] = 'symmetric ' ;
154+ $ this ->cryptParams ['method ' ] = 'http://www.w3.org/2009/xmlenc11#aes128-gcm ' ;
155+ $ this ->cryptParams ['keysize ' ] = 16 ;
156+ $ this ->cryptParams ['blocksize ' ] = 16 ;
157+ break ;
158+ case (self ::AES192_GCM ):
159+ $ this ->cryptParams ['library ' ] = 'openssl ' ;
160+ $ this ->cryptParams ['cipher ' ] = 'aes-192-gcm ' ;
161+ $ this ->cryptParams ['type ' ] = 'symmetric ' ;
162+ $ this ->cryptParams ['method ' ] = 'http://www.w3.org/2009/xmlenc11#aes192-gcm ' ;
163+ $ this ->cryptParams ['keysize ' ] = 24 ;
164+ $ this ->cryptParams ['blocksize ' ] = 16 ;
165+ break ;
166+ case (self ::AES256_GCM ):
167+ $ this ->cryptParams ['library ' ] = 'openssl ' ;
168+ $ this ->cryptParams ['cipher ' ] = 'aes-256-gcm ' ;
169+ $ this ->cryptParams ['type ' ] = 'symmetric ' ;
170+ $ this ->cryptParams ['method ' ] = 'http://www.w3.org/2009/xmlenc11#aes256-gcm ' ;
171+ $ this ->cryptParams ['keysize ' ] = 32 ;
172+ $ this ->cryptParams ['blocksize ' ] = 16 ;
173+ break ;
145174 case (self ::RSA_1_5 ):
146175 $ this ->cryptParams ['library ' ] = 'openssl ' ;
147176 $ this ->cryptParams ['padding ' ] = OPENSSL_PKCS1_PADDING ;
@@ -165,6 +194,18 @@ public function __construct($type, $params=null)
165194 }
166195 }
167196 throw new Exception ('Certificate "type" (private/public) must be passed via parameters ' );
197+ case (self ::RSA_OAEP ):
198+ $ this ->cryptParams ['library ' ] = 'openssl ' ;
199+ $ this ->cryptParams ['padding ' ] = OPENSSL_PKCS1_OAEP_PADDING ;
200+ $ this ->cryptParams ['method ' ] = 'http://www.w3.org/2009/xmlenc11#rsa-oaep ' ;
201+ $ this ->cryptParams ['hash ' ] = 'http://www.w3.org/2009/xmlenc11#mgf1sha1 ' ;
202+ if (is_array ($ params ) && ! empty ($ params ['type ' ])) {
203+ if ($ params ['type ' ] == 'public ' || $ params ['type ' ] == 'private ' ) {
204+ $ this ->cryptParams ['type ' ] = $ params ['type ' ];
205+ break ;
206+ }
207+ }
208+ throw new Exception ('Certificate "type" (private/public) must be passed via parameters ' );
168209 case (self ::RSA_SHA1 ):
169210 $ this ->cryptParams ['library ' ] = 'openssl ' ;
170211 $ this ->cryptParams ['method ' ] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1 ' ;
@@ -347,7 +388,7 @@ public function loadKey($key, $isFile=false, $isCert = false)
347388
348389 case 'symmetric ' :
349390 if (strlen ($ this ->key ) < $ this ->cryptParams ['keysize ' ]) {
350- throw new Exception ('Key must contain at least 25 characters for this cipher ' );
391+ throw new Exception ('Key must contain at least ' . $ this -> cryptParams [ ' keysize ' ]. ' characters for this cipher, contains ' . strlen ( $ this -> key ) );
351392 }
352393 break ;
353394
@@ -397,12 +438,22 @@ private function unpadISO10126($data)
397438 private function encryptSymmetric ($ data )
398439 {
399440 $ 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 );
441+ $ authTag = null ;
442+ if (in_array ($ this ->cryptParams ['cipher ' ], ['aes-128-gcm ' , 'aes-192-gcm ' , 'aes-256-gcm ' ])) {
443+ if (version_compare (PHP_VERSION , '7.1.0 ' ) < 0 ) {
444+ throw new Exception ('PHP 7.1.0 is required to use AES GCM algorithms ' );
445+ }
446+ $ authTag = openssl_random_pseudo_bytes (self ::AUTHTAG_LENGTH );
447+ $ encrypted = openssl_encrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA , $ this ->iv , $ authTag );
448+ } else {
449+ $ data = $ this ->padISO10126 ($ data , $ this ->cryptParams ['blocksize ' ]);
450+ $ encrypted = openssl_encrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , $ this ->iv );
451+ }
452+
402453 if (false === $ encrypted ) {
403454 throw new Exception ('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string ());
404455 }
405- return $ this ->iv . $ encrypted ;
456+ return $ this ->iv . $ encrypted . $ authTag ;
406457 }
407458
408459 /**
@@ -416,11 +467,24 @@ private function decryptSymmetric($data)
416467 $ iv_length = openssl_cipher_iv_length ($ this ->cryptParams ['cipher ' ]);
417468 $ this ->iv = substr ($ data , 0 , $ iv_length );
418469 $ data = substr ($ data , $ iv_length );
419- $ decrypted = openssl_decrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , $ this ->iv );
470+ $ authTag = null ;
471+ if (in_array ($ this ->cryptParams ['cipher ' ], ['aes-128-gcm ' , 'aes-192-gcm ' , 'aes-256-gcm ' ])) {
472+ if (version_compare (PHP_VERSION , '7.1.0 ' ) < 0 ) {
473+ throw new Exception ('PHP 7.1.0 is required to use AES GCM algorithms ' );
474+ }
475+ // obtain and remove the authentication tag
476+ $ offset = 0 - self ::AUTHTAG_LENGTH ;
477+ $ authTag = substr ($ data , $ offset );
478+ $ data = substr ($ data , 0 , $ offset );
479+ $ decrypted = openssl_decrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA , $ this ->iv , $ authTag );
480+ } else {
481+ $ decrypted = openssl_decrypt ($ data , $ this ->cryptParams ['cipher ' ], $ this ->key , OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING , $ this ->iv );
482+ }
483+
420484 if (false === $ decrypted ) {
421485 throw new Exception ('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string ());
422486 }
423- return $ this ->unpadISO10126 ($ decrypted );
487+ return null !== $ authTag ? $ decrypted : $ this ->unpadISO10126 ($ decrypted );
424488 }
425489
426490 /**
0 commit comments