1

I need to encrypt user and password with AES-256-CBC and PKCS7 padding to connect to a .NET server application that it is not mine.

Firstly, I got the public server key using Diffie-Hellman algorithm, following the instructions on this site:

https://doc.developer.milestonesys.com/mipsdkmobile/index.html?base=content_0.html&tree=tree_home.html

The code to get the public key from server is:

private const DH_PRIME = 'F488FD584E49DBCD20B49DE49107366B336C380D451D0F7C88B31C7C5B2D8EF6F3C923C043F0A55B188D8EBB558CB85D38D334FD7C175743A31D186CDE33212CB52AFF3CE1B1294018118D7C84A70A72D686C40319C807297ACA950CD9969FABD00A509B0246D3083D66A45D419F9C7CBD894B221926BAABA25EC355E92F78C7';
private const DH_GENERATOR = '02';

$privateKey = gmp_init(uniqid(), 32);
$publicKey = gmp_powm(self::DH_GENERATOR, $privateKey, base_convert( self::DH_PRIME, 16,10));

Once I got the key, so it is working great here, I must compute the server key with my private key, previously decoding base64 server key, and converting it from binary to hexadecimal:

// Decode server public returned key (encoded in base 64)
$serverPKBase64Decoded = base64_decode($apiResponse['response']['Command']['OutputParams']['Param']['7']['@attributes']['Value']);
$serverPublicKey16 = bin2hex($serverPKBase64Decoded);

// Calculate shared key
$prime = gmp_init(self::DH_PRIME,16);
$serverPk = gmp_init($serverPublicKey, 16);
$localPk = gmp_init($localPrivateKey,32);
$sharedKey = gmp_powm($serverPk, $localPk, $prime);

// Get shared key as hexadecimal value
$sharedKeyHex = gmp_strval($sharedKey, 16);

Now, it comes the part to get the initialization vector (iv) and the key to encrypt the username and password:

$iv = substr($sharedKeyHex,0,16);
$key = strlen($sharedKeyHex)-32,32);
// Encrypt username and password
$encryptedUsername = base64_encode(openssl_encrypt($username,$cipher,$key,OPENSSL_RAW_DATA,$iv));
$encryptedPassword = base64_encode(openssl_encrypt($password,$cipher,$key,OPENSSL_RAW_DATA,$iv));

When I send the data and got the response, it throws an error 16 with the text "Incorrect public key".

Our provider's SDK gives examples (on JS with CryptoJS) where the login process is equivalent. Please, could you confirm the code is equal in JS than in PHP:

JS Code:

/**
     * Encode a string using client and server public keys
     * 
     * @param       str         string      String to encode
     * @access                  public
     * @return                  string      Base64 encoded encrypted string
     */
    this.encodeString = function(str) {
        var secretString = this._sharedKey || this.getSharedKey().substring(0, 96);

        var key = CryptoJS.enc.Hex.parse(secretString.substring(32, 96)); 
        var iv = CryptoJS.enc.Hex.parse( secretString.substring(0,32) ); 
        
        var params = { 'iv': iv };
        if (XPMobileSDKSettings.defaultEncryptionPadding && CryptoJS.pad[XPMobileSDKSettings.defaultEncryptionPadding]) {
            params.padding = CryptoJS.pad[XPMobileSDKSettings.defaultEncryptionPadding];
        }
        return CryptoJS.AES.encrypt(str, key, params).ciphertext.toString(CryptoJS.enc.Base64);
    };

I have tested with many PADDINGS but I am not able to get it working. Any ideas why it is not working or what I must change to get it working?

dual
  • 21
  • 2
  • `$ = strlen($sharedKeyHex)-32,32);` : help, code MIA! – Maarten Bodewes Jul 08 '22 at 09:14
  • It was incomplete due to copy/paste. Now it is OK: ````$key = strlen($sharedKeyHex)-32,32);```` – dual Jul 08 '22 at 12:06
  • It's not clear from the CryptoJS code what `XPMobileSDKSettings.defaultEncryptionPadding` is. Both CryptoJS and `openssl_encrypt()` use PKCS#7 padding by default. If `XPMobileSDKSettings.defaultEncryptionPadding` is different, this should result in a padding issue in the PHP code. At the latest then you should check the specification again. – Topaco Jul 08 '22 at 13:04
  • In your PHP code `$iv` and `$key` seem to be calculated incorrectly. Since `$sharedKeyHex` is hex encoded, you would have to do a hex decoding first: `$sharedKey = hex2bin($sharedKeyHex); $iv = substr($sharedKey, 0, 16); $key = substr($sharedKey, 16, 32);`. – Topaco Jul 08 '22 at 13:11
  • I have tested but it is still not working. From server side, I got "public error authentication" error. So I think, the server is not able to decrypt the data. – dual Jul 14 '22 at 13:32

1 Answers1

0

You should prefix your username & password with your own public key, otherwise the server cannot calculate the shared key.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • The server already has my public key because it has been shared previously on the first request to get its public key and other parameters needed before login. I have edited my first post to include the JS code provided by the developer that works with its Javascript SDK. But I need to do the same with PHP. – dual Jul 08 '22 at 12:11