android PHP与Java之间的加密

4ioopgfo  于 11个月前  发布在  Android
关注(0)|答案(5)|浏览(107)

我想在PHP服务器和Java客户端之间加密数据。单独的代码工作正常,我想在PHP服务器上坚持使用OpenSSL。
你们中有人看到我在这里遗漏的东西吗?因为我在尝试解码PHP加密字符串时出错:

PHP:

<?php

$iv = 'fedcba9876543210'; #Same as in JAVA
$key = '0123456789abcdef'; #Same as in JAVA


$ciphers = openssl_get_cipher_methods(FALSE);
$ciphers_and_aliases = openssl_get_cipher_methods(true);
$cipher_aliases = array_diff($ciphers_and_aliases, $ciphers);

print_r($ciphers);

//print_r($cipher_aliases);

// DEFINE our cipher
define('AES_CBC', 'aes-128-cbc');
// Generate a 256-bit encryption key
// This should be stored somewhere instead of recreating it each time
$encryption_key = "test_key";
// Generate an initialization vector
// This *MUST* be available for decryption as well
//$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length(AES_CBC));

// Create some data to encrypt
$data = "Hello World!!!";
$data_b64= base64_encode($data);
echo "Before encryption: $data<br><br>Before Base64: $data_b64<br><br>";
// Encrypt $data using aes-256-cbc cipher with the given encryption key and
// our initialization vector. The 0 gives us the default options, but can
// be changed to OPENSSL_RAW_DATA or OPENSSL_ZERO_PADDING
$encrypted = openssl_encrypt($data_b64, AES_CBC, $encryption_key, 0, $iv);
$len = strlen($encrypted);
echo "Encrypted Len: $len  <br><br>";
$encrypted64 = base64_encode($encrypted);
echo "Encrypted b64: $encrypted64<br><br>";
// If we lose the $iv variable, we can't decrypt this, so:
// - $encrypted is already base64-encoded from openssl_encrypt
// - Append a separator that we know won't exist in base64, ":"
// - And then append a base64-encoded $iv
$encrypted = $encrypted64 . ':' . base64_encode($iv);
echo "Encrypted: $encrypted<br><br>";
// To decrypt, separate the encrypted data from the initialization vector ($iv).
$parts = explode(':', $encrypted);
// $parts[0] = encrypted data
// $parts[1] = base-64 encoded initialization vector
// Don't forget to base64-decode the $iv before feeding it back to
//openssl_decrypt
$decrypted64 = openssl_decrypt(base64_decode($parts[0]), AES_CBC, $encryption_key, 0, base64_decode($parts[1]));
$decrypted = base64_decode($decrypted64);
echo "Decrypted: $decrypted\n";
?>

字符串

PHP输出:

您好,世界!
Base64之前:SGVsbG 8 gV 29 ybGQhISE =
加密长度:44
加密b64:U21 yMVRGQTdROVc 3 TWJ 1 Wm 1HUTBhMmZmenlIN 2 tvdWQ 5SHA 5ekrampUmp 5az 0 =
加密:U21 yMVRGQTdROVc 3 TWJ 1 Wm 1HUTBhMmZmenlIN 2 tvdWQ 5SHA 5ekanderUmp 5az 0 =:ZmVkY 2 JhOTg 3 NjU 0 MzIxMA ==
您好,世界!

Java代码:

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import java.util.Base64;
import java.security.*;

public class Sandbox {

    public static String encrypt(String key, String initVector, String value) {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);

            byte[] encrypted = cipher.doFinal(Base64.getEncoder().encode(value.getBytes()));
            System.out.println("encrypted string: "
                    + Base64.getEncoder().encodeToString(encrypted));

            return Base64.getEncoder().encodeToString(encrypted);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public static String decrypt(String key, String initVector, String encrypted) {
        try {
            IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

            byte[] temp = Base64.getDecoder().decode(encrypted);

            System.out.println((new String(temp)).length());
            byte[] original = cipher.doFinal(temp);
            original = Base64.getDecoder().decode(original);

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public static void main(String[] args) {
        String key = "0123456789abcdef"; // 128 bit key
        String initVector = "fedcba9876543210"; // 16 bytes IV

//        for (Provider provider : Security.getProviders()) {
//            System.out.println(provider.getName());
//            for (String key2 : provider.stringPropertyNames()) {
//                System.out.println("\t" + key2 + "\t" + provider.getProperty(key2));
//            }
//        }
        System.out.println(decrypt(key, initVector,
                encrypt(key, initVector, "Hello World!!!")));

        System.out.println(decrypt(key, initVector, "R090NDcvclAyY2E1cmxLWG9kSGlnUktHdEI5U05sRGxNdWF4NFFjUUV0OD0="));
    }
}


Java输出:

> 
30
Hello World!!!

44

null

javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:913)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
    at javax.crypto.Cipher.doFinal(Cipher.java:2165)
    at Sandbox.decrypt(Sandbox.java:43)
    at Sandbox.main(Sandbox.java:67)
BUILD SUCCESSFUL (total time: 1 second)

4dbbbstv

4dbbbstv1#

可以找到工作版本-https://github.com/chaudhuri-ab/CrossPlatformCiphers
需要记住的是,如果你没有在PHP中指定OPENSSL_RAW_DATA,数据将被加密为base64。
PHP:

class PHP_AES_Cipher {

    private static $OPENSSL_CIPHER_NAME = "aes-128-cbc"; //Name of OpenSSL Cipher 
    private static $CIPHER_KEY_LEN = 16; //128 bits

    /**
     * Encrypt data using AES Cipher (CBC) with 128 bit key
     * 
     * @param type $key - key to use should be 16 bytes long (128 bits)
     * @param type $iv - initialization vector
     * @param type $data - data to encrypt
     * @return encrypted data in base64 encoding with iv attached at end after a :
     */

    static function encrypt($key, $iv, $data) {
        if (strlen($key) < PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = str_pad("$key", PHP_AES_Cipher::$CIPHER_KEY_LEN, "0"); //0 pad to len 16
        } else if (strlen($key) > PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = substr($str, 0, PHP_AES_Cipher::$CIPHER_KEY_LEN); //truncate to 16 bytes
        }

        $encodedEncryptedData = base64_encode(openssl_encrypt($data, PHP_AES_Cipher::$OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, $iv));
        $encodedIV = base64_encode($iv);
        $encryptedPayload = $encodedEncryptedData.":".$encodedIV;

        return $encryptedPayload;

    }

    /**
     * Decrypt data using AES Cipher (CBC) with 128 bit key
     * 
     * @param type $key - key to use should be 16 bytes long (128 bits)
     * @param type $data - data to be decrypted in base64 encoding with iv attached at the end after a :
     * @return decrypted data
     */
    static function decrypt($key, $data) {
        if (strlen($key) < PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = str_pad("$key", PHP_AES_Cipher::$CIPHER_KEY_LEN, "0"); //0 pad to len 16
        } else if (strlen($key) > PHP_AES_Cipher::$CIPHER_KEY_LEN) {
            $key = substr($str, 0, PHP_AES_Cipher::$CIPHER_KEY_LEN); //truncate to 16 bytes
        }

        $parts = explode(':', $data); //Separate Encrypted data from iv.
        $decryptedData = openssl_decrypt(base64_decode($parts[0]), PHP_AES_Cipher::$OPENSSL_CIPHER_NAME, $key, OPENSSL_RAW_DATA, base64_decode($parts[1]));

        return $decryptedData;
    }

}

字符串
Java:

package ciphers;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import java.util.Base64;

public class Java_AES_Cipher {

    private static String CIPHER_NAME = "AES/CBC/PKCS5PADDING";
    private static int CIPHER_KEY_LEN = 16; //128 bits

    /**
     * Encrypt data using AES Cipher (CBC) with 128 bit key
     * 
     * 
     * @param key  - key to use should be 16 bytes long (128 bits)
     * @param iv - initialization vector
     * @param data - data to encrypt
     * @return encryptedData data in base64 encoding with iv attached at end after a :
     */
    public static String encrypt(String key, String iv, String data) {
        try {
            if (key.length() < Java_AES_Cipher.CIPHER_KEY_LEN) {
                int numPad = Java_AES_Cipher.CIPHER_KEY_LEN - key.length();

                for(int i = 0; i < numPad; i++){
                    key += "0"; //0 pad to len 16 bytes
                }

            } else if (key.length() > Java_AES_Cipher.CIPHER_KEY_LEN) {
                key = key.substring(0, CIPHER_KEY_LEN); //truncate to 16 bytes
            }

            IvParameterSpec initVector = new IvParameterSpec(iv.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance(Java_AES_Cipher.CIPHER_NAME);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, initVector);

            byte[] encryptedData = cipher.doFinal((data.getBytes()));

            String base64_EncryptedData = Base64.getEncoder().encodeToString(encryptedData);
            String base64_IV = Base64.getEncoder().encodeToString(iv.getBytes("UTF-8"));

            return base64_EncryptedData + ":" + base64_IV;

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    /**
     * Decrypt data using AES Cipher (CBC) with 128 bit key
     * 
     * @param key - key to use should be 16 bytes long (128 bits)
     * @param data - encrypted data with iv at the end separate by :
     * @return decrypted data string
     */

    public static String decrypt(String key, String data) {
        try {

            String[] parts = data.split(":");

            IvParameterSpec iv = new IvParameterSpec(Base64.getDecoder().decode(parts[1]));
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");

            Cipher cipher = Cipher.getInstance(Java_AES_Cipher.CIPHER_NAME);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);

            byte[] decodedEncryptedData = Base64.getDecoder().decode(parts[0]);

            byte[] original = cipher.doFinal(decodedEncryptedData);

            return new String(original);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

}

fdbelqdn

fdbelqdn2#

我不明白为什么你的方法失败了。顺便说一下,我是这样做的,

** java **

import com.sun.org.apache.xml.internal.security.utils.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;

public class MyClass {

    public static void main(String[] args) {
        String data = "Arnab C";
        final String enc = DarKnight.getEncrypted(data);
        System.out.println("Encrypted : " + enc);
        System.out.println("Decrypted : " + DarKnight.getDecrypted(enc));
    }

    static class DarKnight {

        private static final String ALGORITHM = "AES";

        private static final byte[] SALT = "tHeApAcHe6410111".getBytes();// THE KEY MUST BE SAME
        private static final String X = DarKnight.class.getSimpleName();

        static String getEncrypted(String plainText) {

            if (plainText == null) {
                return null;
            }

            Key salt = getSalt();

            try {
                Cipher cipher = Cipher.getInstance(ALGORITHM);
                cipher.init(Cipher.ENCRYPT_MODE, salt);
                byte[] encodedValue = cipher.doFinal(plainText.getBytes());
                return Base64.encode(encodedValue);
            } catch (Exception e) {
                e.printStackTrace();
            }

            throw new IllegalArgumentException("Failed to encrypt data");
        }

        public static String getDecrypted(String encodedText) {

            if (encodedText == null) {
                return null;
            }

            Key salt = getSalt();
            try {
                Cipher cipher = Cipher.getInstance(ALGORITHM);
                cipher.init(Cipher.DECRYPT_MODE, salt);
                byte[] decodedValue = Base64.decode(encodedText);
                byte[] decValue = cipher.doFinal(decodedValue);
                return new String(decValue);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        static Key getSalt() {
            return new SecretKeySpec(SALT, ALGORITHM);
        }

    }
}

字符串

PHP

<?php

$key = "tHeApAcHe6410111";

function encrypt($text,$key){
     $block = mcrypt_get_block_size('rijndael_128', 'ecb');
     $pad = $block - (strlen($text) % $block);
     $text .= str_repeat(chr($pad), $pad);
     return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $text, MCRYPT_MODE_ECB));
}

function decrypt($str, $key){ 
     $str = base64_decode($str);
     $str = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
     $block = mcrypt_get_block_size('rijndael_128', 'ecb');
     $pad = ord($str[($len = strlen($str)) - 1]);
     $len = strlen($str);
     $pad = ord($str[$len-1]);
     return substr($str, 0, strlen($str) - $pad);
}

$enc =  encrypt("Arnab C",$GLOBALS['key']);
echo "Encrypted : ".$enc."</br>";
$dec = decrypt($enc,$GLOBALS['key']);
echo "Decrypted : ".$dec;

Java输出

加密:PJG1Uu6SjJuuVGf7ApuHAw==
英文名:Arnab C

PHP输出

加密:PJG1Uu6SjJuuVGf7ApuHAw==
英文名:Arnab C

9q78igpj

9q78igpj3#

我使用了theapache64的解决方案,但在PHP 7.2中,由于Mcrypt已被弃用并随后被删除,它停止工作。所以我更改了代码,它可以工作:

function encrypt($data, $key) {
    return base64_encode(openssl_encrypt($data, "aes-128-ecb", $key, OPENSSL_RAW_DATA));
}

function decrypt($data, $key) {
    return openssl_decrypt(base64_decode($data), "aes-128-ecb", $key, OPENSSL_RAW_DATA);
}

字符串

7cjasjjr

7cjasjjr4#

在我的情况下,我从谷歌请求“*java php aes加密 *”-或部分工作(本地),或根本没有工作。所以我收集了我在互联网上找到的一切,并作出了这一点,希望它有帮助。
注意事项:这个例子使用标准库,在两边,这就是为什么没有使用AES-256加密,不管PHP本机支持,因为Java不支持AES-256也不支持PKCS 7 Padding没有外部库,如Apache Crypto等。

PHP

/**
 * Encrypt data using AES Cipher (CBC) with 128 bit key
 * 
 * @param $userPassword - password provided by user, which will be transformed to 16 bytes strings no matter what (required length by AES-128-CBC/CFB algorithm)
 * @param $iv           - initialization vector (16 bytes long (128 bits))
 * @param $data         - data to encrypt
 * @return              - encrypted data with iv attached at end (last 16 bytes of data) and encoded in Base64 string
 */
function java_aes_encrypt($userPassword, $data) {
    $key = prepareAES128bitsKey($userPassword);
    $iv = generateRandomVI();
    return base64_encode(hex2bin(bin2hex(openssl_encrypt($data, "AES-128-CBC", $key, OPENSSL_RAW_DATA, $iv)).bin2hex($iv)));
}

/**
 * Decrypt data using AES Cipher (CBC) with 128 bit key
 * 
 * @param $userPassword - password provided by user, which will be transformed to 16 bytes strings no matter what (required length by AES-128-CBC/CFB algorithm)
 * @param $data         - data to be decrypted with iv attached at the end as last 16 bytes and encoded in Base64 string
 * @return              - decrypted data
 */
function java_aes_decrypt($userPassword, $data) {
    $key = prepareAES128bitsKey($userPassword);

    // Convert binary data to a hexadecimal string
    $hexData = bin2hex(base64_decode($data));

    // Get the length of the received data
    $dataLength = strlen($hexData);

    // Check if the data length is at least 32 characters (16 bytes in hexadecimal)
    if ($dataLength >= 32) {
        // Extract the last 16 bytes in hexadecimal
        $last16Bytes = substr($hexData, -32);

        // Extract everything except the last 16 bytes in hexadecimal
        $everythingExceptLast16 = substr($hexData, 0, $dataLength - 32);

        // Convert the extracted data back to binary if needed
        $last16BytesBinary = hex2bin($last16Bytes);
        $everythingExceptLast16Binary = hex2bin($everythingExceptLast16);
        return openssl_decrypt($everythingExceptLast16Binary, "AES-128-CBC", $key, OPENSSL_RAW_DATA, $last16BytesBinary);
    }
    return FALSE;
}

/**
 * Transform user provided password to fixed length (16 symbols) passphrase
 * Must be the same as on Java side!
 * @param $userPassword    - password provided by user, which will be transformed to 16 bytes strings no matter what (required length by AES-128-CBC/CFB algorithm)
 * @param $transformLength - length of return string
 * @return $outPass key passphrase generated from user provided
 */
function prepareAES128bitsKey($userPassword, $transformLength = 16) {
    $hash = hash('sha512', $userPassword);
    $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $chars_num = strlen($characters);
    $acc = 0; $outPass = "";
    for ($i = 0; $i < 16; $i++) { $acc += ord($hash[$i]); }
    for ($ci = 0, $hi = 16; $ci < $transformLength; $ci += 1, $hi += 1) {
        if($hi >= strlen($hash)) { $hi = 0; }
        $acc += ord($hash[$hi]);
        $outPass .= $characters[($acc % $chars_num)];
    }
    return $outPass;
}

function generateRandomVI() {
    return generateOpenSSLRandomString(16);
}

function generateOpenSSLRandomString($length = 8) {
    $characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $chars_num = strlen($characters);
    $randomString = "";
    $b = openssl_random_pseudo_bytes($length * $length);
    for ($ci = 0, $bi = 0; $ci < $length; $ci += 1, $bi += 2) {
        $randomString .= $characters[(ord($b[$bi]) % $chars_num)];
    }
    return $randomString;
}

字符串

Java:

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class main {

    public static void main(String[] args) {
        
        String userPasswordWhichCanBeAnyLength = "yhqzEgtGquYCHpUlJi0BpSCIVBdGxMl";
        
        String userProvidedDataAsString = 
        "Lorem Ipsum is simply dummy text of the printing and typesetting industry. " +
        "Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, " +
        "when an unknown printer took a galley of type and scrambled it to make a " +
        "type specimen book. It has survived not only five centuries, but also the " +
        "leap into electronic typesetting, remaining essentially unchanged. It was popularized in the " +
        "1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more " +
        "recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
        
        AES.testStringToStringEncryption(userPasswordWhichCanBeAnyLength, userProvidedDataAsString);
    }
    
}

class AES {
    
    //private final SecureRandom random; // need JDK v17+
    private final Random random;
    
    // base64 encoding/decoding
    private final Encoder instance_Base64enc;
    private final Decoder instance_Base64dec;
    
    // AES Encryption variables
    private final char[] allowedChars;
    private String passwordKey;
    private SecretKeySpec keySpec;
    private final Cipher instance_AES_Cipher;

    /**
     * The keeper of local Instances of AES cipher and Random objects, for less identical results
     */
    public AES() {
        this.instance_Base64enc = java.util.Base64.getEncoder();
        this.instance_Base64dec = java.util.Base64.getDecoder();
        
        //this.random = new SecureRandom(); // need JDK v17+
        this.random = new Random();
        
        this.allowedChars = new String("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();
        
        this.instance_AES_Cipher = getInstanceAESCP();
    }
    
    /**
     * Encapsulated String to String (Base64) Encrypting Function
     * @param  data - Supplied user data as String
     * @return encoded as Base64 Encrypted data
     */
    public String encrypt(String data) {
        try {
            return this.Base64enc(encryptRaw(data.getBytes("UTF-8")));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * Plain data to encrypted data Encryption.
     * May be used for internal purposes, or for files encryption.
     * @param  data - any data provided as byte[] array
     * @return encrypted bytes as byte[] array
     */
    public byte[] encryptRaw(byte[] data) {

        // Exceptions handling is good for future integration and error handling,
        // but not possible in current example, so let it be here
        try {
            return AES_encrypt(data);
        } catch (InvalidKeyException e)                {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e)           {
            e.printStackTrace();
        } catch (NoSuchPaddingException e)             {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e)       {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e)          {
            e.printStackTrace();
        } catch (BadPaddingException e)                {
            e.printStackTrace();
        }
        
        return data;
    }
    
    /**
     * Encapsulated String (Base64) to plain String (with possible useful information) Encrypting Function
     * @param  data - Supplied encoded Base64 and encrypted String
     * @return plain decrypted data as String
     */
    public String decrypt(String data) {
        try {
            return new String(decryptRaw(Base64dec(data.replace("\\", ""))), "UTF-8").trim();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * Expects encrypted byte[] array
     * @param  data - encrypted data provided as byte[] array
     * @return decrypted bytes as byte[] array
     */
    public byte[] decryptRaw(byte[] data) {
        
        // Exceptions handling is good for future integration and error handling,
        // but not possible in current example, so let it be here
        try {
            return AES_decrypt(data);
        } catch (InvalidKeyException e)                {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e)           {
            e.printStackTrace();
        } catch (NoSuchPaddingException e)             {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e)       {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e)          {
            e.printStackTrace();
        } catch (BadPaddingException e)                {
            e.printStackTrace();
        }
        
        return data;
    }
    
    
    /**
     * This function sets password key used to encrypt\decrypt data with AES
     * Must be identical on both sides!
     * @param  password - password provided by user
     * @return instance of current AES object
     */
    public AES setPasswordKey(String password) {
        try {
            byte[] key = prepareAES128bitsKey(password);
            this.keySpec = new SecretKeySpec(key, "AES");
        }
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        this.passwordKey = password;
        return this;
    }
    
    /**
     * User provided password
     * @return stored users password used to encrypt\decrypt data with AES algorithm as String
     */
    public String getPasswordKey() {
        return this.passwordKey;
    }
    
    /**
     * Test of AES Encryption module,
     * @param userPassword - user provided password. Can be any length
     * @param userData     - user provided data. Can be any length
     */
    public static void testStringToStringEncryption(String userPassword, String userData) {
        
        // Make instance
        AES AES = new AES();
        
        // Necessary part! Need to set password for future encrypting/decrypting steps!
        AES.setPasswordKey(userPassword);
        
        // encrypting step
        String encoded_Base64_encrypted_aes_string = AES.encrypt(userData);
        
        // decrypting step
        String decrypted_data = AES.decrypt(encoded_Base64_encrypted_aes_string);
        
        System.out.println("User provided plain String: " + userData);
        System.out.println("Encrypted String: " + encoded_Base64_encrypted_aes_string);
        System.out.println("Decrypted String: " + decrypted_data);
        
        if(userData.equals(decrypted_data)) {
            System.out.println("\nAES Test succeed!");
        }
        else {
            System.out.println("\nAES Test FAIL!");
        }
        
        System.out.println("AES encryption-decryption Test Done! Exiting.");
        System.exit(77);
    }
    
    /**
    * Encrypt data using AES Cipher (CBC) with 128 bit key
    * @param  key  - key to use should be 16 bytes long (128 bits)
    * @param  iv   - initialization vector
    * @param  data - data to encrypt
    * @return encrypted data with iv attached at end as 16 last bytes
    * @throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException */
    private byte[] AES_encrypt(byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        byte[] iv = new byte[16]; this.random.nextBytes(iv);
        IvParameterSpec initVector = new IvParameterSpec(iv);
        this.instance_AES_Cipher.init(Cipher.ENCRYPT_MODE, this.keySpec, initVector);
        byte[] encryptedData = this.instance_AES_Cipher.doFinal(data);
        int dataLength = encryptedData.length;
        byte[] combo = new byte[dataLength + 16];
        for (int i = 0; i < dataLength; i++)   { combo[i] = encryptedData[i]; }
        for (int i = 0, o = dataLength; i < 16; i++, o++) { combo[o] = iv[i]; }
        return combo;
    }

    /**
    * Decrypt data using AES Cipher (CBC) with 128 bit key
    * @param  key  - key to use should be 16 bytes long (128 bits)
    * @param  data - encrypted data with iv at the end as 16 last bytes
    * @return decrypted data
    * @throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException */
    private byte[] AES_decrypt(byte[] dataPlusIV) throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        int fullLength = dataPlusIV.length;
        int dataLength = (fullLength - 16);
        byte[] iv   = new byte[16];
        byte[] data = new byte[dataLength];
        for (int fi = 0, di = 0, vi = 0; fi < fullLength; fi++) {
            if(fi < dataLength) { data[di++] = dataPlusIV[fi]; } else { iv[vi++] = dataPlusIV[fi]; }
        }
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        this.instance_AES_Cipher.init(Cipher.DECRYPT_MODE, this.keySpec, ivSpec);
        byte[] original = this.instance_AES_Cipher.doFinal(data);
        return original;
    }
    
    /**
     * The idea behind this function to generate consistent 128 bits key, on both sides (PHP and Java),
     * no matter what, without paddings or string clamping.
     * 
     * Functionality similar to:
     *    PBEKeySpec pbeKeySpec = new PBEKeySpec(password, salt, 1000, keySize);
     *    SecretKey key = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256").generateSecret(pbeKeySpec);
     * 
     * @param  userPassword - password provided by user, length is irrelevant
     * @return returnKey    - 16 bytes array of generated chars from user provided SHA512 hashed password
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    private byte[] prepareAES128bitsKey(String userPassword) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        char[] hash = hash(userPassword).toCharArray();
        int hashLength = hash.length;
        byte[] returnKey = new byte[16];
        int charsNum = this.allowedChars.length; int acc = 0;
        for (int i = 0; i < 16; i++) { acc += ((int) hash[i]);}
        for(int ci = 0, hi = 16; ci < 16; ci++, hi++) {
            if (hi >= hashLength) { hi = 0; }
            acc += ((int) hash[hi]);
            returnKey[ci] = ((byte) this.allowedChars[(acc % charsNum)]);
        }
        return returnKey;
    }
    
    /**
     * String hashing function
     * @param  str - input string
     * @return SHA512 hash of provided string
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public String hash(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MessageDigest sha512 = MessageDigest.getInstance("SHA-512");
        byte[] hashBytes = sha512.digest(str.getBytes("UTF-8"));
        StringBuilder hexString = new StringBuilder();
        int size = hashBytes.length;
        for (int i = 0; i < size; i++) {
            hexString.append(String.format("%02x", hashBytes[i]));
        }
        return hexString.toString();
    }
    
    /**
     * Retrieve instance of AES Cipher, to reduce system calls
     * @return instance of AES Cipher
     */
    private static Cipher getInstanceAESCP() {
        try { return Cipher.getInstance("AES/CBC/PKCS5PADDING"); }
        catch (NoSuchAlgorithmException e)     {}
        catch (NoSuchPaddingException e)       {}
        return null;
    }
    
    
    /**
     * Local Base64 encoder
     * @param  data - to encode
     * @return encoded data in Base64 String
     */
    public String Base64enc(byte[] data) {
        return instance_Base64enc.encodeToString(data);
    }

    /**
     * Local Base64 decoder
     * @param  base64str - String of encoded data to decode
     * @return decoded data in byte[] array, may need future processing/decoding
     */
    public byte[] Base64dec(String base64str) {
        return instance_Base64dec.decode(base64str);
    }

}


发送到PHP可以这样做:

public void sendToPHP(String userPassword, String userData) {
    // Make instance
    AES AES = new AES();
    
    // Necessary part! Need to set password for future encrypting/decrypting steps!
    AES.setPasswordKey(userPassword);
    
    // encrypting step
    String encoded_Base64_encrypted_aes_string = AES.encrypt(userData);
    
    try {
        // Open a connection to the URL
        HttpURLConnection connection = (HttpURLConnection) new URL("http://ursite.com/api/").openConnection();
        
        // Set the connection timeout (in milliseconds)
        connection.setConnectTimeout(2000);

        // Set the request method
        connection.setRequestMethod("POST");

        // set headers and send data
        connection.setRequestProperty("dataType", "text/plain");
        connection.setRequestProperty("body", encoded_Base64_encrypted_aes_string);

        // Get the response code
        int responseCode = connection.getResponseCode();

        // Read the response from the API
        if (responseCode == HttpURLConnection.HTTP_OK) {
            // process response from PHP here, for example
            
            InputStreamReader isr = new InputStreamReader(connection.getInputStream());
            BufferedReader    bfr = new BufferedReader(isr);
            
            String inputLine;
            StringBuilder response = new StringBuilder();

            while ((inputLine = bfr.readLine()) != null) {
                response.append(inputLine);
            }
            
            // decrypting using password provided at start with 'AES.setPasswordKey(userPassword)' function
            // be aware, this received data encrypted in Base64 string
            // passwords must be the same on both (Java and PHP) sides!
            String decrypted_PHP_response = AES.decrypt(response.toString());
            
            System.out.println("Decrypted PHP answer: " + decrypted_PHP_response);
        }

        // Close the connection
        connection.disconnect();
    } catch (IOException e) {
        e.printStackTrace();
        String exceptionMessage = e.getMessage();
        if(exceptionMessage.contains("Connect timed out")) {
            // time out error here
        }
        else {
            // other type errors here
        }
    }
}


在PHP端接收和发送:

/* Assuming You transfer from Java to PHP users password in some (secure) way */
$userPassword = "yhqzEgtGquYCHpUlJi0BpSCIVBdGxMl";

// data sent from Java
$bodyRequest = file_get_contents('php://input');

// decrypt data on PHP side
$remote_decrypted_aes_text = java_aes_decrypt($userPassword, $bodyRequest);

// locally process it here
$transformed_plain_text = $remote_decrypted_aes_text." - WOHO! LIKE IT! APPROVED BY PHP!";

// encrypt it by PHP
$localy_encrypted_aes_text = java_aes_encrypt($userPassword, $transformed_plain_text);

// send it back
echo $localy_encrypted_aes_text; exit();

mpgws1up

mpgws1up5#

在Java 8中,您不能使用

import com.sun.org.apache.xml.internal.security.utils.Base64;

字符串
相反,您可以使用

import android.util.Base64;


然后,您还需要更改Base64.decode和Base64.encode行。
完整的代码如下所示(考虑到了petrnohejl关于PHP 7中已弃用的MSPOT的评论):
Java:

import android.util.Base64;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class MyClass {
    public static void main(String[] args) {
        String data = "Arnab C";
        final String enc = DarKnight.getEncrypted(data);
        System.out.println("Encrypted : " + enc);
        System.out.println("Decrypted : " + DarKnight.getDecrypted(enc));
    }

    static class DarKnight {
        private static final String ALGORITHM = "AES";
        private static final byte[] SALT = "tHeApAcHe6410111".getBytes();// THE KEY MUST BE SAME
        private static final String X = DarKnight.class.getSimpleName();
        static String getEncrypted(String plainText) {
            if (plainText == null) {
                return null;
            }

            Key salt = getSalt();

            try {
                Cipher cipher = Cipher.getInstance(ALGORITHM);
                cipher.init(Cipher.ENCRYPT_MODE, salt);
                byte[] encodedValue = cipher.doFinal(plainText.getBytes());
                return Base64.encodeToString(encodedValue,Base64.DEFAULT);
            } catch (Exception e) {
                e.printStackTrace();
            }
            throw new IllegalArgumentException("Failed to encrypt data");
        }

        public static String getDecrypted(String encodedText) {
            if (encodedText == null) {
                return null;
            }

            Key salt = getSalt();
            try {
                Cipher cipher = Cipher.getInstance(ALGORITHM);
                cipher.init(Cipher.DECRYPT_MODE, salt);
                byte[] decodedValue = Base64.decode(encodedText, Base64.DEFAULT);
                byte[] decValue = cipher.doFinal(decodedValue);
                return new String(decValue);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        static Key getSalt() {
            return new SecretKeySpec(SALT, ALGORITHM);
        }
    }
}


PHP:

<?php

$key = "tHeApAcHe6410111";

function encrypt($data, $key) {
    return base64_encode(openssl_encrypt($data, "aes-128-ecb", $key, OPENSSL_RAW_DATA));
}

function decrypt($data, $key) {
    return openssl_decrypt(base64_decode($data), "aes-128-ecb", $key, OPENSSL_RAW_DATA);
}

$enc =  encrypt("Arnab C",$GLOBALS['key']);
echo "Encrypted : ".$enc."</br>";
$dec = decrypt($enc,$GLOBALS['key']);
echo "Decrypted : ".$dec;

?>


不要给予我的学分,我只是信使,谁结合了几个职位。

相关问题