Golang AES解密机制只输出另一个密文。

huangapple go评论79阅读模式
英文:

Golang AES decryption mechanism outputs another ciphertext only

问题

我正在尝试在我的GO应用程序中使用以下解密方法(在互联网上找到的)。我拥有的cipherKey不是一个直接的32位密钥,而是看起来是base64编码的。因此,我首先对其进行解码,然后应用解码后的值。在整个解密过程中,我没有遇到任何错误,但是decrypt方法的输出似乎是另一个密文。

不确定是否在cipherKey方面漏掉了什么。

以下是GO代码:

func decrypt(key []byte, secure string) (decoded string, err error) {
    //移除base64编码:
    cipherText, err := base64.StdEncoding.DecodeString(secure)

    //如果DecodeString失败,则退出:
    if err != nil {
        return
    }

    //使用密钥和加密消息创建一个新的AES密码器
    block, err := aes.NewCipher(key)

    //如果NewCipher失败,则退出:
    if err != nil {
        return
    }

    //如果cipherText的长度小于16字节:
    if len(cipherText) < aes.BlockSize {
        err = errors.New("ciphertext block size is too short")
        return
    }

    iv := cipherText[:aes.BlockSize]
    cipherText = cipherText[aes.BlockSize:]
    fmt.Println("before deciphering: ", string(cipherText))

    //解密消息
    stream := cipher.NewCFBDecrypter(block, iv)
    stream.XORKeyStream(cipherText, cipherText)

    return string(cipherText), err
}

更新原始问题...

我需要在我的GO应用程序中解密数据。该数据实际上来自一个Java应用程序。以下是Java代码片段:

public static final String KEY_ALGORITHM = "AES";
public static final String AES_ALGORITHM = "AES/CFB8/NoPadding";
public static final String DIGEST_ALGORITHM = "MD5";
public static final byte[] INITIAL_VECTOR = { -25, 9, -119, 91, -90, 112, 98, -40, 65, -106, -1, 96, 118, -13, 88,
            85 }; 

package com.crypto;

import static com.CryptoConstant.AES_ALGORITHM;
import static com.CryptoConstant.DIGEST_ALGORITHM;
import static com.CryptoConstant.INITIAL_VECTOR;
import static com.CryptoConstant.KEY_ALGORITHM;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

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;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;


@Slf4j
@Component
public class MessageCrypto {

    /**
     * 
     */
    @Value("${design.secret.key}")
    private String designSecretKey;

    /**
     * Md 5
     *
     * @param input the input
     * @return the string
     * @throws NoSuchAlgorithmException the no such algorithm exception
     */
    private static String md5(final String input) throws NoSuchAlgorithmException {
        final MessageDigest md = MessageDigest.getInstance(DIGEST_ALGORITHM);
        final byte[] messageDigest = md.digest(input.getBytes());
        // Convert byte array to a string of hex digits
        final BigInteger number = new BigInteger(1, messageDigest);
        // The 0 in the mask does the padding, 32 chars and x indicates lower hex
        // digits.
        return String.format("%032x", number);
    }

    /**
     * Inits the cipher
     *
     * @param mode the mode
     * @return the cipher
     * @throws NoSuchAlgorithmException           the no such algorithm exception
     * @throws NoSuchPaddingException             the no such padding exception
     * @throws InvalidKeyException                the invalid key exception
     * @throws InvalidAlgorithmParameterException the invalid algorithm parameter
     *                                            exception
     */
    private Cipher initCipher(final int mode) throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, InvalidAlgorithmParameterException {

        final SecretKeySpec skeySpec = new SecretKeySpec(md5(designSecretKey).getBytes(), KEY_ALGORITHM);
        final IvParameterSpec initialVector = new IvParameterSpec(INITIAL_VECTOR);
        final Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        cipher.init(mode, skeySpec, initialVector);
        return cipher;
    }

    /**
     * Encrypt
     * 
     * @param dataToEncrypt the data to encrypt
     * @return the string
     */
    public String encrypt(final String dataToEncrypt) {
        log.info("Processing encrypt...");
        byte[] encryptedData = {};

        try {

            // Initialize the cipher
            final Cipher cipher = initCipher(Cipher.ENCRYPT_MODE);
            // Encrypt the data
            final byte[] encryptedByteArray = cipher.doFinal(dataToEncrypt.getBytes());
            // Encode using Base64
            encryptedData = Base64.getEncoder().encode(encryptedByteArray);

        } catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException
                | InvalidAlgorithmParameterException | InvalidKeyException e) {
            log.error("Encryption error: {} ", e);
        }
        log.info("Processed encrypt...");
        return new String(encryptedData);
    }

    /**
     * Decrypt
     *
     * @param encryptedData the encrypted data
     * @return the string
     */
    public String decrypt(final String encryptedData) {
        log.info("Processing decrypt...");
        String decryptedData = "";

        try {

            // Initialize the cipher
            final Cipher cipher = initCipher(Cipher.DECRYPT_MODE);
            // Decode using Base64
            final byte[] encryptedByteArray = Base64.getDecoder().decode(encryptedData.getBytes());
            // Decrypt the data
            final byte[] decryptedByteArray = cipher.doFinal(encryptedByteArray);

            decryptedData = new String(decryptedByteArray, StandardCharsets.UTF_8);

        } catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException
                | InvalidAlgorithmParameterException | InvalidKeyException e) {
            log.error("Decryption error: {} ", e);
        }
        log.info("Processed decrypt...");
        return decryptedData;
    }

}

我已经收到了来自他们那边的cipherkey和ciphertext现在我需要在GO中实现与此Java等效的内容我正在尝试使用看起来很简单的CFB加密/解密机制来实现但是我正在努力弄清楚如何从提供的密钥中检索实际的cipherkey从Java代码来看我似乎需要有与Java中md5()方法等效的东西

这是我尝试的一些内容

```go
key := "94k/IwqJQ5wf4Yt5JZmbW85r2x246rI3g3LZbTI80Vo="
key_decr := md5.Sum([]byte(key))
key = hex.EncodeToString(key_decr[:])
log.Println("key:", key)
decrypt(key, secureText)

然而,这并不起作用。

英文:

I am trying to use below decrypt method(found it over internet) in my GO application. The cipherKey that I have is not a straightforward 32 bit one, but it looks to be base64 encoded. Hence, I decoded that first and then applied. I don't get any error in this whole decryption process, however the output of this decrypt method looks to be another ciphertext.

Not sure if I am missing something as far as the cipherKey is concerned.

func decrypt(key []byte, secure string) (decoded string, err error) {
//Remove base64 encoding:
cipherText, err := base64.StdEncoding.DecodeString(secure)
//IF DecodeString failed, exit:
if err != nil {
return
}
//Create a new AES cipher with the key and encrypted message
block, err := aes.NewCipher(key)
//IF NewCipher failed, exit:
if err != nil {
return
}
//IF the length of the cipherText is less than 16 Bytes:
if len(cipherText) &lt; aes.BlockSize {
err = errors.New(&quot;ciphertext block size is too short&quot;)
return
}
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]
fmt.Println(&quot;before deciphering: &quot;, string(cipherText))
//Decrypt the message
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(cipherText, cipherText)
return string(cipherText), err
}

Updating the original question...

I have got to decrypt a data in my GO application. The data, in turn, is coming from a Java application. Here are the snippets from the same,

public static final String KEY_ALGORITHM = &quot;AES&quot;;
public static final String AES_ALGORITHM = &quot;AES/CFB8/NoPadding&quot;;
public static final String DIGEST_ALGORITHM = &quot;MD5&quot;;
public static final byte[] INITIAL_VECTOR = { -25, 9, -119, 91, -90, 112, 98, -40, 65, -106, -1, 96, 118, -13, 88,
85 }; 
package com.crypto;
import static com.CryptoConstant.AES_ALGORITHM;
import static com.CryptoConstant.DIGEST_ALGORITHM;
import static com.CryptoConstant.INITIAL_VECTOR;
import static com.CryptoConstant.KEY_ALGORITHM;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
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;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class MessageCrypto {
/**
* 
*/
@Value(&quot;${design.secret.key}&quot;)
private String designSecretKey;
/**
* Md 5
*
* @param input the input
* @return the string
* @throws NoSuchAlgorithmException the no such algorithm exception
*/
private static String md5(final String input) throws NoSuchAlgorithmException {
final MessageDigest md = MessageDigest.getInstance(DIGEST_ALGORITHM);
final byte[] messageDigest = md.digest(input.getBytes());
// Convert byte array to a string of hex digits
final BigInteger number = new BigInteger(1, messageDigest);
// The 0 in the mask does the padding, 32 chars and x indicates lower hex
// digits.
return String.format(&quot;%032x&quot;, number);
}
/**
* Inits the cipher
*
* @param mode the mode
* @return the cipher
* @throws NoSuchAlgorithmException           the no such algorithm exception
* @throws NoSuchPaddingException             the no such padding exception
* @throws InvalidKeyException                the invalid key exception
* @throws InvalidAlgorithmParameterException the invalid algorithm parameter
*                                            exception
*/
private Cipher initCipher(final int mode) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException {
final SecretKeySpec skeySpec = new SecretKeySpec(md5(designSecretKey).getBytes(), KEY_ALGORITHM);
final IvParameterSpec initialVector = new IvParameterSpec(INITIAL_VECTOR);
final Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(mode, skeySpec, initialVector);
return cipher;
}
/**
* Encrypt
* 
* @param dataToEncrypt the data to encrypt
* @return the string
*/
public String encrypt(final String dataToEncrypt) {
log.info(&quot;Processing encrypt...&quot;);
byte[] encryptedData = {};
try {
// Initialize the cipher
final Cipher cipher = initCipher(Cipher.ENCRYPT_MODE);
// Encrypt the data
final byte[] encryptedByteArray = cipher.doFinal(dataToEncrypt.getBytes());
// Encode using Base64
encryptedData = Base64.getEncoder().encode(encryptedByteArray);
} catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | InvalidKeyException e) {
log.error(&quot;Encryption error: {} &quot;, e);
}
log.info(&quot;Processed encrypt...&quot;);
return new String(encryptedData);
}
/**
* Decrypt
*
* @param encryptedData the encrypted data
* @return the string
*/
public String decrypt(final String encryptedData) {
log.info(&quot;Processing decrypt...&quot;);
String decryptedData = &quot;&quot;;
try {
// Initialize the cipher
final Cipher cipher = initCipher(Cipher.DECRYPT_MODE);
// Decode using Base64
final byte[] encryptedByteArray = Base64.getDecoder().decode(encryptedData.getBytes());
// Decrypt the data
final byte[] decryptedByteArray = cipher.doFinal(encryptedByteArray);
decryptedData = new String(decryptedByteArray, StandardCharsets.UTF_8);
} catch (IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException
| InvalidAlgorithmParameterException | InvalidKeyException e) {
log.error(&quot;Decryption error: {} &quot;, e);
}
log.info(&quot;Processed decrypt...&quot;);
return decryptedData;
}
}

I have received the cipherkey as well as a ciphertext from their side. Now I need to implement something equivalent to this Java in GO. I am trying to use CFB encrypt/decrypt mechanism which appears to be straightforward to implement. However, I am trying to figure out how to retrieve the actual cipherkey from provided one. From the Java code it looks like, I need to have something equivalent to what md5() method is doing here in Java.

Here is something that I tried,

key := &quot;94k/IwqJQ5wf4Yt5JZmbW85r2x246rI3g3LZbTI80Vo=&quot;
key_decr := md5.Sum([]byte(key))
key = hex.EncodeToString(key_decr[:])
log.Println(&quot;key:&quot;, key)
decrypt(key, secureText)

However, that does not work.

答案1

得分: 2

两个代码在两个方面有所不同:

  • Java代码使用CFB8,Go代码使用CFB128。
  • Java代码在加密过程中不执行IV和密文的连接,但是Go代码假设有这样的连接,因此在解密过程中分离了IV和密文。

为了使两个代码兼容,必须消除这些差异。由于Java代码似乎是参考代码,因此在Go代码中需要进行以下更改:

  • 移除对加密数据的IV和密文的分离。因此,也省略了数据至少为16字节且检查的约束条件。使用Java代码中的静态IV(通过添加256将Java代码中的负二进制补码整数转换为无符号整数):
    iv := []byte{231, 9, 137, 91, 166, 112, 98, 216, 65, 150, 255, 96, 118, 243, 88, 85}
    
  • 在Go代码中使用CFB8。crypto/cipher包实现了固定分段大小的CFB128,因此无法使用。实现CFB8的可能替代方案是github.com/Tnze/gomcbot/CFB8
    stream := CFB8.NewCFB8Decrypt(block, iv)
    

整体代码(为简单起见,省略了异常处理):

import (
	"crypto/aes"
	"crypto/md5"
	"encoding/base64"
	"encoding/hex"
	"log"
	"github.com/Tnze/gomcbot/CFB8"
)

func main() {
	key := "94k/IwqJQ5wf4Yt5JZmbW85r2x246rI3g3LZbTI80Vo="
	keyHashed := md5.Sum([]byte(key))
	keyHex := hex.EncodeToString(keyHashed[:])
	cipherText := "h6OpNEE4g8hjyJl5lk5Qm4ZyXP/j3ADWqREolDL8lwb0LuyDqQdrlLGfsg==" // Java端的密文(使用AES/CFB8创建)
	decryptedText, _ := decrypt([]byte(keyHex), cipherText)
	log.Println(decryptedText)
}

func decrypt(key []byte, cipherTextB64 string) (decrypted string, err error) {
	cipherText, _ := base64.StdEncoding.DecodeString(cipherTextB64)                      // 修正1:不分离IV和密文
	iv := []byte{231, 9, 137, 91, 166, 112, 98, 216, 65, 150, 255, 96, 118, 243, 88, 85} // 修正2:应用Java端的静态IV
	block, _ := aes.NewCipher(key)
	stream := CFB8.NewCFB8Decrypt(block, iv) // 修正3:应用CFB8
	stream.XORKeyStream(cipherText, cipherText)
	return string(cipherText), nil
}

关于安全性:

  • 静态IV是一个漏洞,因为这导致了密钥/IV对的重用。为了避免这种情况,通常在加密过程中生成一个随机IV,并将其与密文连接起来(注意IV不是秘密)。在解密过程中,两部分被分离(顺便说一句,这与原始Go代码中的实现相对应)。
  • 通过像MD5这样的快速且不安全的散列函数从密码派生密钥是一个漏洞。更安全的做法是使用像PBKDF2这样的密钥派生函数。
英文:

Both codes differ in two points:

  • The Java code uses CFB8, the Go code uses CFB128.
  • The Java code does not perform concatenation of IV and ciphertext during encryption, but the Go code assumes such concatenation and therefore separates IV and ciphertext during decryption.

For both codes to be compatible, these differences must be eliminated. Since the Java code seems to be the reference, the following changes are therefore necessary in the Go code:

  • The separation of the encrypted data into IV and ciphertext is to be removed. Thus also the constraint that the data must be at least 16 bytes large and its check are omitted. The static IV from the Java code is to be used (the negative two's complement integers of the Java code are converted into unsigned integers by adding 256):
    iv := []byte{231, 9, 137, 91, 166, 112, 98, 216, 65, 150, 255, 96, 118, 243, 88, 85}
    
  • In the Go code CFB8 is to be used. The crypto/cipher package implements CFB128 with fixed segment size and therefore cannot be used. A possible alternative for an implementation of CFB8 is github.com/Tnze/gomcbot/CFB8:
    stream := CFB8.NewCFB8Decrypt(block, iv)
    

Overall (for simplicity without exception handling):

import (
	&quot;crypto/aes&quot;
	&quot;crypto/md5&quot;
	&quot;encoding/base64&quot;
	&quot;encoding/hex&quot;
	&quot;log&quot;
	&quot;github.com/Tnze/gomcbot/CFB8&quot;
)

func main() {
	key := &quot;94k/IwqJQ5wf4Yt5JZmbW85r2x246rI3g3LZbTI80Vo=&quot;
	keyHashed := md5.Sum([]byte(key))
	keyHex := hex.EncodeToString(keyHashed[:])
	cipherText := &quot;h6OpNEE4g8hjyJl5lk5Qm4ZyXP/j3ADWqREolDL8lwb0LuyDqQdrlLGfsg==&quot; // Ciphertext from the Java side (created with AES/CFB8)
	decryptedText, _ := decrypt([]byte(keyHex), cipherText)
	log.Println(decryptedText)
}

func decrypt(key []byte, cipherTextB64 string) (decrypted string, err error) {
	cipherText, _ := base64.StdEncoding.DecodeString(cipherTextB64)                      // Fix 1: don&#39;t separate IV and ciphertext
	iv := []byte{231, 9, 137, 91, 166, 112, 98, 216, 65, 150, 255, 96, 118, 243, 88, 85} // Fix 2: apply the static IV from the Java side
	block, _ := aes.NewCipher(key)
	stream := CFB8.NewCFB8Decrypt(block, iv) // Fix 3: apply CFB8
	stream.XORKeyStream(cipherText, cipherText)
	return string(cipherText), nil
}

Regarding security:

  • A static IV is a vulnerabilty, since this results in a reuse of key/IV pairs. To avoid this, usually a random IV is generated during encryption and concatenated with the ciphertext (note that the IV is not secret). During decryption, both parts are separated (which, incidentally, corresponds to the implementation in the original Go code).
  • Key derivation from a password via a fast and additionally insecure digest like MD5 is a vulnerability. It is more secure to use a key derivation function like PBKDF2.

huangapple
  • 本文由 发表于 2022年12月8日 01:59:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/74721065.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定