首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用PHP OpenSSL用JAVA密码解密aes-128-gcm编码的内容

使用PHP OpenSSL用JAVA密码解密aes-128-gcm编码的内容
EN

Stack Overflow用户
提问于 2020-06-01 09:51:24
回答 1查看 1.5K关注 0票数 1

我必须解密一些数据发送到我的网站,使用aes-128-gcm加密的JAVA密码.

客户有一个使用Talend的提供者,该提供者必须通过URL参数向我发送一些信息,而我只能尝试使用PHP解密数据。提供者实际上无法告诉我它是如何在他这一方工作的,而且也不太确定在生成的代码中使用的变量(例如IV长度)。

加密方法是在JAVA上使用Talend组件完成的。提供程序向我发送Talend生成的代码作为示例。

代码语言:javascript
复制
private static String encrypt(String data, String mainKey, int ivLength) throws Exception {
  final byte[] dataBytes = data.getBytes(UNICODE_FORMAT);
  byte[] initializationVector = generateInitializationVector(ivLength);
  final Cipher cipher = getAesGcmCipher(Cipher.ENCRYPT_MODE, mainKey, initializationVector);

  final byte[] encryptedData = cipher.doFinal(dataBytes);
  final byte[] encryptedBytes = new byte[encryptedData.length + ivLength];
  System.arraycopy(initializationVector, 0, encryptedBytes, 0, ivLength);
  System.arraycopy(encryptedData, 0, encryptedBytes, ivLength, encryptedData.length);

  return BASE64_ENCODER.apply(encryptedBytes);
}

private static String decrypt(String data, String mainKey, int ivLength) throws Exception {
  final byte[] encryptedBytes = BASE64_DECODER.apply(data.getBytes(UNICODE_FORMAT));

  final byte[] initializationVector = new byte[ivLength];
  System.arraycopy(encryptedBytes, 0, initializationVector, 0, ivLength);

  final Cipher cipher = getAesGcmCipher(Cipher.DECRYPT_MODE, mainKey, initializationVector);
  return new String(cipher.doFinal(encryptedBytes, ivLength, encryptedBytes.length - ivLength),
      UNICODE_FORMAT);
}

他还向我发送了一些用他正在使用的类初始化的常量:

代码语言:javascript
复制
static final String ALGO = "AES"; //$NON-NLS-1$
static final String GCMALGO = "AES/GCM/NoPadding"; //$NON-NLS-1$
static final String UNICODE_FORMAT = "UTF8"; //$NON-NLS-1$
static final String DES_ENCRYPTION_SCHEME = "DES"; //$NON-NLS-1$
private static final int DEFAULT_IV_LENGTH = 16;
public static final String NULL_PARAMETER_MESSAGE = "The parameter should not be null"; //$NON-NLS-1$
public static final String EMPTY_PARAMETER_MESSAGE = "String is empty"; //$NON-NLS-1$
private static final String KEY_GEN_ALGO = "PBKDF2WithHmacSHA256"; //$NON-NLS-1$
static final Random random = new SecureRandom();
static final BASE64Encoder b64Encoder = new DataMasking().new BASE64Encoder();
static final BASE64Decoder b64Dencoder = new DataMasking().new BASE64Decoder();
public static final Function<byte[], String> BASE64_ENCODER = bytes -> Base64.getEncoder().encodeToString(bytes);
public static final Function<byte[], byte[]> BASE64_DECODER = bytes -> Base64.getDecoder().decode(bytes);

正如我所理解的,初始化向量放在字符串的开头,并且我了解到标记是由Java对象自动放置在末尾的。

我尝试用加密()解密()在PHP端加密和解密一些内容,它运行得很好,但我无法解密JAVA应用程序发送给我的数据。

我想知道它是否与对二进制数据的字符串操作有关,或者提供者没有提供我所需要的所有必要信息。

我也不知道用来解密数据的Tag_length是什么。提供者还告诉我IV_Length有16个字符长,但是当我使用openssl_cipher_iv_length('aes-128-gcm')时,它建议使用12个字符。

下面是我现在在PHP端的代码:

代码语言:javascript
复制
/**
 * @param string $str
 *   The URL parameter string
 */
function test_decrypt($str) {
  $key = 'MySuperPassword7';
  $cipher = 'aes-128-gcm';

  $iv_len = 16;
  $tag_length = 16;

  echo $str . '<br>';
  /**
   * Encryption test
   */
//  $tag = ""; 
//  $iv = openssl_random_pseudo_bytes($iv_len);
//  $enc_str = openssl_encrypt('Test of data to send', $cipher, $key, OPENSSL_RAW_DATA, $iv, $tag, "", $tag_length);
//  $encrypt = base64_encode($iv.$enc_str.$tag);

//  echo $key . '::' . $iv . '::' . $tag . '<br>';
//  echo '$enc_str<pre>';
//  var_dump($enc_str);
//  echo '</pre>';
//  echo '$encrypt<pre>';
//  var_dump($encrypt);
//  echo '</pre>';

  /**
   * Decryption part
   */
  $encrypt = base64_decode($str);
  $iv = substr($encrypt, 0, $iv_len);
  $tag = substr($encrypt, - $tag_length);
  $ciphertext = substr($encrypt, $iv_len, -$tag_length);

  $uncrypt = openssl_decrypt($ciphertext, $cipher, $key, 0, $iv, $tag);//OPENSSL_RAW_DATA + OPENSSL_NO_PADDING
  echo $iv_len . '::' . $tag_length . '<br>';
  echo $key . '::' . $iv . '::' . $tag . '<br>';
  echo '$encrypt<pre>';
  var_dump($encrypt);
  echo '</pre>';
  echo '$ciphertext<pre>';
  var_dump($ciphertext);
  echo '</pre>';
  echo '$uncrypt<pre>';
  var_dump($uncrypt);
  echo '</pre>';
  exit;
}

我还试图使用在线工具来解密Java应用程序发送给我的数据,但到目前为止,我没有发现任何有用的东西。

编辑

正如@JohnConde在评论中所问的那样,我可以共享我目前试图解密的加密字符串和密钥。我们决定用与Michael的答案相同的数据进行测试:

  • 键:'1234567890123456‘
  • 纯文本:“TytooF的机密数据”
  • 加密文本: VODKjhFETSxMcaa7x/LIOYCfmqD1iWSCuxX80reQ1KoFhmU8/A5AlH0Pg/ZoK1eNSdhBpUed

长度与加密的答案(P8kU/Gf27LU9qgmLpA8gpfHmdVuW5cC7XeKVIgmb3rgGzyjG0YHX5P/cFfRRoZpUxxTEKzj8).中的一个相同

像以前一样,我无法解密数据.

以下是Talend组件的完整代码:

代码语言:javascript
复制
// ============================================================================
//
// Copyright (C) 2006-2019 Talend Inc. - www.talend.com
//
// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
//
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
//
// ============================================================================

package routines;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PushbackInputStream;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Stream;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;


/**
 * created by talend on 2016-04-08 Detailled comment.
 *
 */
public class DataMasking {

    static final String ALGO = "AES"; //$NON-NLS-1$

    static final String GCMALGO = "AES/GCM/NoPadding"; //$NON-NLS-1$

    static final String UNICODE_FORMAT = "UTF8"; //$NON-NLS-1$

    static final String DES_ENCRYPTION_SCHEME = "DES"; //$NON-NLS-1$

    private static final int DEFAULT_IV_LENGTH = 16;

    public static final String NULL_PARAMETER_MESSAGE = "The parameter should not be null"; //$NON-NLS-1$

    public static final String EMPTY_PARAMETER_MESSAGE = "String is empty"; //$NON-NLS-1$

    private static final String KEY_GEN_ALGO = "PBKDF2WithHmacSHA256"; //$NON-NLS-1$

    static final Random random = new SecureRandom();

    static final BASE64Encoder b64Encoder = new DataMasking().new BASE64Encoder();

    static final BASE64Decoder b64Dencoder = new DataMasking().new BASE64Decoder();

    public static final Function<byte[], String> BASE64_ENCODER = bytes -> Base64.getEncoder().encodeToString(bytes);

    public static final Function<byte[], byte[]> BASE64_DECODER = bytes -> Base64.getDecoder().decode(bytes);

    public static class DataMaskingRoutineException extends RuntimeException {

        private static final long serialVersionUID = -8622896150657449668L;

        public DataMaskingRoutineException() {
            super();
        }

        public DataMaskingRoutineException(String s) {
            super(s);
        }

        public DataMaskingRoutineException(String s, Object o) {
            super(s);
            System.out.println(o);
        }

    }


    /**
     * Encrypt String: Encrypts a string using AES 128 .
     * warning: this is not considered a secure function.
     *
     *
     * {talendTypes} String
     *
     * {Category} Data Masking
     *
     * {param} String("foo") encryptString: The string to be encrypted.
     * 
     * {param} byte[](new byte[] { 'T', 'a', 'l', 'e', 'n', 'd', 's', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' })
     * keyValue: the
     * key material of the secret key. The contents of the array are copied to protect against subsequent modification.
     * 
     * {example} encryptAES("foo", new byte[] { 'T', 'a', 'l', 'e', 'n', 'd', 's', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' })
     * result is UQ0VJZq5ymFkMYQeDrPi0A==
     *
     * @deprecated use {@link #encryptAESGCM(String, String, int)} instead of it
     * 
     */

    @Deprecated
    public static String encryptAES(String encryptString, byte[] keyValue) {

        if (encryptString == null || keyValue == null) {
            return NULL_PARAMETER_MESSAGE;
        }
        if (encryptString.length() == 0) {
            return EMPTY_PARAMETER_MESSAGE;
        }
        try {
            Key key = new SecretKeySpec(keyValue, ALGO);
            Cipher cipher = Cipher.getInstance(ALGO);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encVal = cipher.doFinal(encryptString.getBytes());
            String encryptedValue = b64Encoder.encode(encVal);
            return encryptedValue;

        } catch (Exception e) {
            throw new DataMaskingRoutineException(e.getMessage(), e);
        }
    }


    /**
     * Encrypt String: Encrypts a string using AES GCM 128 .
     * 
     * {talendTypes} String
     * 
     * {Category} Data Masking
     * 
     * {param} String("foo") encryptString: The string to be encrypted.
     * 
     * {param} String("TalendMainKey123") the main key used to encrypt the data.
     * 
     * {example} encryptAESGCM("foo","TalendMainKey123") result could be
     * +ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4= (but it should change from an execution to another).
     * 
     */

    public static String encryptAESGCM(String encryptString, String mainKey) {

        return encryptAESGCM(encryptString, mainKey, DEFAULT_IV_LENGTH);
    }

    /**
     * Encrypt String: Encrypts a string using AES GCM 128 .
     * 
     * {talendTypes} String
     * 
     * {Category} Data Masking
     * 
     * {param} String("foo") encryptString: The string to be encrypted.
     * 
     * {param} String("TalendMainKey123") the main key used to encrypt the data.
     * 
     * {param} int the length of initializationVector. must be one of 12/13/14/15/16.
     * 
     * {example} encryptAESGCM("foo","TalendMainKey123",16) result could be
     * +ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4= (but it should change from an execution to another).
     * 
     */

    public static String encryptAESGCM(String encryptString, String mainKey, int ivLength) {

        if (encryptString == null || mainKey == null) {
            return NULL_PARAMETER_MESSAGE;
        }

        if (encryptString.length() == 0) {
            return EMPTY_PARAMETER_MESSAGE;
        }

        try {
            return encrypt(encryptString, mainKey, ivLength);
        } catch (Exception e) {
            throw new DataMaskingRoutineException(e.getMessage(), e);
        }
    }



    /**
     * decrypt String: Decrypts a string using AES 128.
     * warning: this is not considered a secure function.
     *
     *
     * {talendTypes} String
     *
     * {Category} Data Masking
     *
     * {param} String("UQ0VJZq5ymFkMYQeDrPi0A==") encryptedString: The string to be decrypted.
     *
     * {param} byte[](new byte[] { 'T', 'a', 'l', 'e', 'n', 'd', 's', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' })
     * keyValue: the key material of the secret key. The contents of the array are copied to protect against subsequent
     * modification.
     * 
     * {example} decryptAES("UQ0VJZq5ymFkMYQeDrPi0A==",new byte[] { 'T', 'a', 'l', 'e', 'n', 'd', 's', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' })
     * result is "foo"
     * 
     * @deprecated use {@link #decryptAESGCM(String, String, int)} instead of it
     * 
     */

    @Deprecated
    public static String decryptAES(String encryptedString, byte[] keyValue) {

        if (encryptedString == null || keyValue == null) {
            return NULL_PARAMETER_MESSAGE;
        }

        if (encryptedString.length() == 0) {
            return EMPTY_PARAMETER_MESSAGE;
        }
        try {
            Key key = new SecretKeySpec(keyValue, ALGO);
            Cipher cipher = Cipher.getInstance(ALGO);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decordedValue = b64Dencoder.decodeBuffer(encryptedString);
            byte[] decValue = cipher.doFinal(decordedValue);
            String decryptedValue = new String(decValue);
            return decryptedValue;

        } catch (Exception e) {
            throw new DataMaskingRoutineException(e.getMessage(), e);
        }
    }

    /**
     * decrypt String: Decrypts a string using AES GCM 128.
     * 
     * {talendTypes} String
     * 
     * {Category} Data Masking
     * 
     * {param} String("+ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4=") encryptedString: The string to be decrypted.
     * 
     * {param} String("TalendMainKey123") the main key used to decrypt the data.
     * 
     * {param} int the length of initializationVector. must be one of 12/13/14/15/16.
     * 
     * {example} decryptAESGCM("+ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4=","TalendMainKey123",16) result
     * is "foo"
     * 
     */

    public static String decryptAESGCM(String encryptedString, String mainKey, int ivLength) {

        if (encryptedString == null || mainKey == null) {
            return NULL_PARAMETER_MESSAGE;
        }

        if (encryptedString.length() == 0) {
            return EMPTY_PARAMETER_MESSAGE;
        }

        try {
            return decrypt(encryptedString, mainKey, ivLength);

        } catch (Exception e) {
            throw new DataMaskingRoutineException(e.getMessage(), e);
        }

    }

    /**
     * decrypt String: Decrypts a string using AES GCM 128.
     * 
     * {talendTypes} String
     * 
     * {Category} Data Masking
     * 
     * {param} String("+ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4=") encryptedString: The string to be decrypted.
     * 
     * {param} String("TalendMainKey123") the main key used to decrypt the data.
     * 
     * {example} decryptAESGCM("+ngs4RohAx8OvInmioRwRYS0pymcNkQJCKqsdUPZqUZppN4=","TalendMainKey123") result
     * is "foo"
     * 
     */

    public static String decryptAESGCM(String encryptedString, String mainKey) {

        return decryptAESGCM(encryptedString, mainKey, DEFAULT_IV_LENGTH);
    }

    /**
     * Encrypt String: Encrypts a string using DES .
     * warning: this is not considered a secure function.
     *
     *
     * {talendTypes} String
     *
     * {Category} Data Masking
     *
     * {param} String("foo") unencryptedString: The string to be encrypted.
     *
     * {param} String("ThisIsSecretEncryptionKey") myEncryptionKey: the string with the DES key material.
     *
     * {example} encryptDES("foo") result is DmNj+x2LUXA=
     *
     * @throws Exception
     */

    public static String encryptDES(String unencryptedString, String myEncryptionKey) {

        if (unencryptedString == null || myEncryptionKey == null) {
            return NULL_PARAMETER_MESSAGE;
        }
        if (unencryptedString.length() == 0) {
            return EMPTY_PARAMETER_MESSAGE;
        }
        try {
            String encryptedString = null;

            String myEncryptionScheme = DES_ENCRYPTION_SCHEME;
            byte[] keyAsBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
            KeySpec myKeySpec = new DESKeySpec(keyAsBytes);
            SecretKeyFactory mySecretKeyFactory = SecretKeyFactory.getInstance(myEncryptionScheme);
            Cipher encipher = Cipher.getInstance(myEncryptionScheme);
            SecretKey key = mySecretKeyFactory.generateSecret(myKeySpec);

            encipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
            byte[] encryptedText = encipher.doFinal(plainText);
            encryptedString = b64Encoder.encode(encryptedText);

            return encryptedString;

        } catch (Exception e) {
            throw new DataMaskingRoutineException(e.getMessage());
        }

    }

    /**
     * Decrypt String: Decrypts a string using DES .
     * warning: this is not considered a secure function.
     *
     *
     * {talendTypes} String
     *
     * {Category} Data Masking
     *
     * {param} String("DmNj+x2LUXA=") encryptedString: the string with the DES key material.
     *
     * {param} String("ThisIsSecretEncryptionKey") myDecryptionKey: The string to be encrypted.
     *
     * {example} decryptDES("DmNj+x2LUXA=") result is "foo"
     *
     */

    public static String decryptDES(String encryptedString, String myDecryptionKey) {

        if (encryptedString == null || myDecryptionKey == null) {
            return NULL_PARAMETER_MESSAGE;
        }

        if (encryptedString.length() == 0) {
            return EMPTY_PARAMETER_MESSAGE;
        }
        try {
            String decryptedText = null;

            String myDecryptionScheme = DES_ENCRYPTION_SCHEME;

            byte[] keyAsBytes = myDecryptionKey.getBytes(UNICODE_FORMAT);

            KeySpec myKeySpec = new DESKeySpec(keyAsBytes);
            Cipher decipher = Cipher.getInstance(myDecryptionScheme);
            SecretKeyFactory mySecretKeyFactory = SecretKeyFactory.getInstance(myDecryptionScheme);
            SecretKey key = mySecretKeyFactory.generateSecret(myKeySpec);

            decipher.init(Cipher.DECRYPT_MODE, key);
            byte[] encryptedText = b64Dencoder.decodeBuffer(encryptedString);
            byte[] plainText = decipher.doFinal(encryptedText);

            StringBuilder stringBuilder = new StringBuilder();
            for (byte element : plainText) {
                stringBuilder.append((char) element);
            }
            decryptedText = stringBuilder.toString();
            return decryptedText;

        } catch (Exception e) {
            throw new DataMaskingRoutineException(e.getMessage(), e);
        }

    }


    /**
     * This method generates a secret Key using the key-stretching algorithm PBKDF2 of
     * <a href="https://docs.oracle.com/javase/7/docs/api/javax/crypto/package-summary.html">javax.crypto</a>.
     * It is basically a hashing algorithm slow by design, in order to increase the time
     * required for an attacker to try a lot of passwords in a bruteforce attack.
     * <br>
     * About the salt :
     * <ul>
     * <li>The salt is not secret, the use of Random is not critical and ensure determinism.</li>
     * <li>The salt is important to avoid rainbow table attacks.</li>
     * <li>The salt should be generated with SecureRandom() in case the passwords are stored.</li>
     * <li>In that case the salt should be stored in plaintext next to the password and a unique user identifier.</li>
     * </ul>
     *
     * @param password a password given as a {@code String}.
     * @param keyLength key length to generate
     * @return a {@code SecretKey} securely generated.
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    private static byte[] generateSecretKeyFromPassword(String password, int keyLength)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] salt = new byte[keyLength];
        new Random(password.hashCode()).nextBytes(salt);
        SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_GEN_ALGO);
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, keyLength << 3);
        return factory.generateSecret(spec).getEncoded();
    }

    private static String encrypt(String data, String mainKey, int ivLength) throws Exception {
        final byte[] dataBytes = data.getBytes(UNICODE_FORMAT);
        byte[] initializationVector = generateInitializationVector(ivLength);
        final Cipher cipher = getAesGcmCipher(Cipher.ENCRYPT_MODE, mainKey, initializationVector);

        final byte[] encryptedData = cipher.doFinal(dataBytes);
        final byte[] encryptedBytes = new byte[encryptedData.length + ivLength];
        System.arraycopy(initializationVector, 0, encryptedBytes, 0, ivLength);
        System.arraycopy(encryptedData, 0, encryptedBytes, ivLength, encryptedData.length);

        return BASE64_ENCODER.apply(encryptedBytes);
    }


    private static String decrypt(String data, String mainKey, int ivLength) throws Exception {
        final byte[] encryptedBytes = BASE64_DECODER.apply(data.getBytes(UNICODE_FORMAT));

        final byte[] initializationVector = new byte[ivLength];
        System.arraycopy(encryptedBytes, 0, initializationVector, 0, ivLength);

        final Cipher cipher = getAesGcmCipher(Cipher.DECRYPT_MODE, mainKey, initializationVector);
        return new String(cipher.doFinal(encryptedBytes, ivLength, encryptedBytes.length - ivLength),
                UNICODE_FORMAT);
    }

}

**编辑2 **

在与提供程序调试了几次之后,我们最终决定使用Michael提供的代码。

我从组件的代码中了解到,区别在于主键的使用方式。但我不是Java开发人员,也许我误解了什么。

以防万一它可以帮助他人,这里是IV生成和秘密密钥生成缺少的额外Java代码。

代码语言:javascript
复制
private static Cipher getAesGcmCipher(int encryptMode, String mainKey, byte[] initializationVector)
            throws Exception {
    int ivLength = initializationVector.length;
    if (Stream.of(12, 13, 14, 15, 16).noneMatch(i -> i == ivLength)) {
        throw new IllegalArgumentException("Invalid IV length"); //$NON-NLS-1$
    }
    final Cipher cipher = Cipher.getInstance(GCMALGO);
    SecretKey key = new SecretKeySpec(generateSecretKeyFromPassword(mainKey, mainKey.length()), ALGO);
    final GCMParameterSpec spec = new GCMParameterSpec(ivLength * 8, initializationVector);
    cipher.init(encryptMode, key, spec);
    return cipher;
}



private static byte[] generateSecretKeyFromPassword(String password, int keyLength)
          throws NoSuchAlgorithmException, InvalidKeySpecException {
      byte[] salt = new byte[keyLength];
      new Random(password.hashCode()).nextBytes(salt);
      SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_GEN_ALGO);
      KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, keyLength << 3);
      return factory.generateSecret(spec).getEncoded();
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-06-01 22:19:09

我用Java设置了您的代码,并使用(固定)密钥和随机初始化向量进行了加密:

代码语言:javascript
复制
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Random;

public class SO_Main_Final {

    static final String ALGO = "AES"; //$NON-NLS-1$
    static final String GCMALGO = "AES/GCM/NoPadding"; //$NON-NLS-1$
    static final String UNICODE_FORMAT = "UTF8"; //$NON-NLS-1$
    static final Random random = new SecureRandom();

    public static void main(String[] args) throws Exception {
        System.out.println("https://stackoverflow.com/questions/62129604/decrypt-aes-128-gcm-encoded-content-with-java-cipher-using-php-openssl");
        String myData = "Secret data for TytooF";
        String myKey = "1234567890123456";
        String encryptString = encrypt(myData, myKey, 16);
        String decryptString = decrypt(encryptString, myKey, 16);
        System.out.println("encryptString:         " + encryptString);
        System.out.println("decryptString:         " + decryptString);

    }

    private static String encrypt(String data, String mainKey, int ivLength) throws Exception {
        final byte[] dataBytes = data.getBytes(UNICODE_FORMAT);
        // byte[] initializationVector = generateInitializationVector(ivLength);
        byte[] initializationVector = new byte[ivLength];
        random.nextBytes(initializationVector);
        SecretKeySpec secretKeySpec = new SecretKeySpec(mainKey.getBytes(UNICODE_FORMAT), ALGO);
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, initializationVector);
        Cipher cipher = Cipher.getInstance(GCMALGO);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);
        final byte[] encryptedData = cipher.doFinal(dataBytes);
        final byte[] encryptedBytes = new byte[encryptedData.length + ivLength];
        System.arraycopy(initializationVector, 0, encryptedBytes, 0, ivLength);
        System.arraycopy(encryptedData, 0, encryptedBytes, ivLength, encryptedData.length);
        System.out.println("data [String]        : " + data);
        System.out.println("data           length: " + dataBytes.length
                + " data: " + bytesToHex(dataBytes));
        System.out.println("mainKey        length: " + mainKey.getBytes(UNICODE_FORMAT).length
                + " data: " + bytesToHex(mainKey.getBytes(UNICODE_FORMAT)));
        System.out.println("initvector     length: " + initializationVector.length
                + " data: " + bytesToHex(initializationVector));
        System.out.println("encryptedBytes length: " + encryptedBytes.length
                + " data: " + bytesToHex(encryptedBytes));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    private static String decrypt(String data, String mainKey, int ivLength) throws Exception {
        final byte[] encryptedBytes = Base64.getDecoder().decode(data.getBytes(UNICODE_FORMAT));
        final byte[] initializationVector = new byte[ivLength];
        System.arraycopy(encryptedBytes, 0, initializationVector, 0, ivLength);
        SecretKeySpec secretKeySpec = new SecretKeySpec(mainKey.getBytes(UNICODE_FORMAT), ALGO);
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, initializationVector);
        Cipher cipher = Cipher.getInstance(GCMALGO);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, gcmParameterSpec);
        return new String(cipher.doFinal(encryptedBytes, ivLength, encryptedBytes.length - ivLength),
                UNICODE_FORMAT);
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuffer result = new StringBuffer();
        for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }
}

最后,我得到了这个结果-- "encryptString“需要转移到您的网站:

代码语言:javascript
复制
https://stackoverflow.com/questions/62129604/decrypt-aes-128-gcm-encoded-content-with-java-cipher-using-php-openssl
data [String]        : Secret data for TytooF
data           length: 22 data: 536563726574206461746120666f72205479746f6f46
mainKey        length: 16 data: 31323334353637383930313233343536
initvector     length: 16 data: 3fc914fc67f6ecb53daa098ba40f20a5
encryptedBytes length: 54 data: 3fc914fc67f6ecb53daa098ba40f20a5f1e6755b96e5c0bb5de29522099bdeb806cf28c6d181d7e4ffdc15f451a19a54c714c42b38fc
encryptString:         P8kU/Gf27LU9qgmLpA8gpfHmdVuW5cC7XeKVIgmb3rgGzyjG0YHX5P/cFfRRoZpUxxTEKzj8
decryptString:         Secret data for TytooF

在webserver/PHP端,我稍微修改了您的代码(“主要更改”是行openssl_decrypt($ciphertext、$cipher、$key、true、$iv、$tag)上的更改),因为我们没有将Base64 64编码的数据显示给解密方法,因为以前做过一些代码行($encrypt = base64_decode($str) )。

以下是PHP代码:

代码语言:javascript
复制
<?php

/**
 * @param string $str
 *   The URL parameter string
 */
function test_decrypt($str)
{
    $key = '1234567890123456';
    $cipher = 'aes-128-gcm';
    $iv_len = 16;
    $tag_length = 16;
    echo $str . '<br>';
    /**
     * Decryption part
     */
    $encrypt = base64_decode($str);
    $iv = substr($encrypt, 0, $iv_len);
    $tag = substr($encrypt, -$tag_length);
    $ciphertext = substr($encrypt, $iv_len, -$tag_length);
    echo "" . "\n";
    $value = unpack('H*', $iv);
    echo '<br>iv:' . $value[1];
    echo "" . "\n";
    $value = unpack('H*', $ciphertext);
    echo '<br>ciphertext:' . $value[1];
    echo "" . "\n";
    $value = unpack('H*', $tag);
    echo '<br>tag:' . $value[1];
    echo "<br>" . "\n";
    $uncrypt = openssl_decrypt($ciphertext, $cipher, $key, true, $iv, $tag);//OPENSSL_RAW_DATA + OPENSSL_NO_PADDING
    echo '<br>DecryptedString: ' . $uncrypt . "\n";
    $value = unpack('H*', $uncrypt);
    echo '<br>DecryptedString [byte[]]:' . $value[1];
    exit;
}

echo '<b>Output for https://stackoverflow.com/questions/62129604/decrypt-aes-128-gcm-encoded-content-with-java-cipher-using-php-openssl</b><br>' . "\n";
echo '' . "\n";
echo 'Start decryption' . "\n";
$receivedData = "P8kU/Gf27LU9qgmLpA8gpfHmdVuW5cC7XeKVIgmb3rgGzyjG0YHX5P/cFfRRoZpUxxTEKzj8";
test_decrypt($receivedData);
?>

这是the服务器上的解密输出:

代码语言:javascript
复制
Output for https://stackoverflow.com/questions/62129604/decrypt-aes-128-gcm-encoded-content-with-java-cipher-using-php-openssl
Start decryption P8kU/Gf27LU9qgmLpA8gpfHmdVuW5cC7XeKVIgmb3rgGzyjG0YHX5P/cFfRRoZpUxxTEKzj8

iv:3fc914fc67f6ecb53daa098ba40f20a5
ciphertext:f1e6755b96e5c0bb5de29522099bdeb806cf28c6d181
tag:d7e4ffdc15f451a19a54c714c42b38fc

DecryptedString: Secret data for TytooF
DecryptedString [byte[]]:536563726574206461746120666f72205479746f6f46

2021年10月10日:编辑:上面的代码使用的是长度为16字节的IV/ code,但是推荐的IV/code长度为12

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62129604

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档