当前位置: 首页 > 面试题库 >

解密C#RIJNDAEL编码的文本

景星光
2023-03-14
问题内容

我正在用Java实现与第三方应用程序的通信。作为登录过程的一部分,第三方应用程序正在发送一个加密的字符串,我必须对其进行解码并发回。我已经花了将近2天的时间进行糊涂和阅读文章,但是我找不到实现此目的的正确方法。

我有一个测试用例,其中加密的字符串为“ c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY =“,使用密码“
GAT”解密的字符串必须返回“ 101714994”。

我在文档中指出:授权字符串已使用以下设置加密:

  • 输入数据的填充:PKCS * 7
  • 密码字节数组的长度为32个字节。密码字符串将转换为UTF-16编码的字节数组,然后用零填充字节数组,最大长度为32个字节。较长的密码将被截断。

这是如何解密授权字符串的C#示例:

/// <summary> 
/// Decrypts a string. 
/// </summary> 
/// <param name="content">The string to decrypt.</param> 
/// <param name="password">The password to use.</param> 
/// <returns>The decrypted string.</returns> 
private static string DecryptString(string content, string password) { 
    Rijndael aes; 
    byte[] retVal = null; 
    byte[] contentBytes; 
    byte[] passwordBytes; 
    byte[] ivBytes;
    try { 
        //Get the content as byte[] 
        contentBytes = Convert.FromBase64String(content);

        //Create the password and initial vector bytes 
        passwordBytes = new byte[32]; 
        ivBytes = new byte[16]; 
        Array.Copy(Encoding.Unicode.GetBytes(password), passwordBytes, Encoding.Unicode.GetBytes(password).Length); 
        Array.Copy(passwordBytes, ivBytes, 16);

        //Create the cryptograpy 
        object aes = Rijndael.Create(); 
        aes.Key = passwordBytes; 
        aes.IV = ivBytes; 
        aes.Padding = PaddingMode.PKCS7;

        //Decrypt 
        retVal = aes.CreateDecryptor().TransformFinalBlock(contentBytes, 0, contentBytes.Length); 
    } 
    catch { 
    }
    aes = null; 
    contentBytes = null; 
    passwordBytes = null; 
    ivBytes = null;
    return Encoding.Unicode.GetString(retVal) 
}

这是我解密字符串的Java过程:

private String decryptAuthorizationString(String authString, String password) {
  try {
    //Force the test string
    authString = "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY=";
    //Force the test password
    password = "GAT";

    //Create the password and initial vector bytes
    byte[] passwordBytes= new byte[32];      
    byte[] b= password.getBytes("UTF-8");      
    int len= b.length;
    if (len > passwordBytes.length) len = passwordBytes.length;
      System.arraycopy(b, 0, passwordBytes, 0, len);

    byte[] ivBytes= new byte[16];
    System.arraycopy(passwordBytes, 0, ivBytes, 0, 16);

    //Get the authString as byte[]
    byte[] authBytes = new BASE64Decoder().decodeBuffer(authString);

    InputStream inputStream = new ByteArrayInputStream(authBytes);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    // If you have Bouncycastle library installed, you can use
    // Rijndael/CBC/PKCS7PADDING directly.
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7PADDING", "BC");

    // convertedSecureString and initVector must be byte[] with correct length
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(passwordBytes, "AES"), new IvParameterSpec(ivBytes));

    CipherInputStream cryptoStream = new CipherInputStream(inputStream, cipher);
    byte[] buffer = new byte[1024];
    len = cryptoStream.read(buffer, 0, buffer.length);
    while (len > 0) {
      outputStream.write(buffer, 0, len);
      len = cryptoStream.read(buffer, 0, buffer.length);
    }

    outputStream.flush();
    cryptoStream.close();
    String resStr = outputStream.toString("UTF-8");       
    return resStr; //<<--- resStr must be "101714994"
  } catch (Throwable t) {

  }
  return null;
}

程序运行没有任何错误,但结果不是我所需要的。任何帮助将不胜感激。


问题答案:

您无需为此提供BouncyCastleProvider,因为AES已经包含在Java中。但是,PKCS#7填充错误地由指示"PKCS5Padding",因此"AES/CBC/PKCS7Padding"如果没有Bouncy
Castle ,则无法指示。

Unicode.NET 的默认编码实际上与UTF-16LE更兼容。将其留给Microsoft不要遵循标准名称(尽管它们可能早于标准名称)。

Java JCE并不是真正像C#类那样围绕流构建的,因此最好完全避免流。

我已经重写了您的示例代码,以显示如何在Java中正确进行编码(不过您需要与Java
7兼容)。不要将异常推到桌子底下,将它们变成AssertErrorRuntimeException

我使用的是Bouncy Castle Base 64解码器,因为这是您和我都可以使用的解码器(但除此之外,它独​​立于Bouncy)。Java
8包含一个基数64类。

因此,事不宜迟:

import static java.nio.charset.StandardCharsets.UTF_16LE;

import java.security.GeneralSecurityException;
import java.util.Arrays;

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

import org.bouncycastle.util.encoders.Base64;

public class AuthenticationStringDecrypter {

    private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING";
    private static final int KEY_SIZE = 256;

    public static void main(final String[] args) throws Exception {
        System.out.println(decryptAuthorizationString(
                "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY=", "GAT"));
    }

    private static String decryptAuthorizationString(final String authString,
            final String password) {
        try {
            // --- check if AES-256 is available
            if (Cipher.getMaxAllowedKeyLength(AES_CBC_PKCS5PADDING) < KEY_SIZE) {
                throw new IllegalStateException("Unlimited crypto files not present in this JRE");
            }

            // --- create cipher
            final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);

            // --- create the key and initial vector bytes
            final byte[] passwordEncoded = password.getBytes(UTF_16LE);
            final byte[] keyData = Arrays.copyOf(passwordEncoded, KEY_SIZE
                    / Byte.SIZE);
            final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());

            // --- init cipher
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"),
                    new IvParameterSpec(ivBytes));

            // --- decode & decrypt authentication string
            final byte[] authBytes = Base64.decode(authString);
            final byte[] decryptedData = cipher.doFinal(authBytes);

            // WARNING: may still decrypt to wrong string if
            // authString or password are incorrect - 
            // BadPaddingException may *not* be thrown
            return new String(decryptedData, UTF_16LE);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            // failure to authenticate
            return null;
        } catch (final GeneralSecurityException e) {
            throw new IllegalStateException(
                    "Algorithms or unlimited crypto files not available", e);
        }
    }
}


 类似资料:
  • 我想把我的C#(托管)解密方法转换成Android NDK, C/C(NO JAVA) 我看到有加密。在JAVA方面,但我想远离任何JNI,我还看到有mcrypt和crypt,但找不到适用于android的编译库。 这里有一个C#的例子,我想把它翻译成C/C 更新到目前为止,我发现最好的方法是使用openSSL AES,我已经下载了一个预编译的Android库,我只是在努力让它与一些已经发布的示例

  • 问题内容: 更新 我已经对C#代码进行了更改,因此它使用的块大小为256。但是现在,您好世界看起来像这样http://pastebin.com/5sXhMV11,我无法弄清楚应该使用rtrim()获得什么一团糟的最后。 另外,当您说IV应该是随机的时,您的意思是不要再使用一次相同的IV,否则我编码的方式错误吗? 再次感谢! 你好 我正在尝试使用在C#中加密的PHP解密字符串。我似乎无法让PHP使用

  • 我曾在互联网上寻找解决问题的有效方法,但我尝试的一切都不奏效。我在两种语言之间得到了不同的结果。 在PHP中,我有以下代码: 和C#中的以下代码: 我有另一个CalcMd5函数,它接受一个byte[](与上面的函数类似,但没有GetBytes部分)。 需要加密的密钥和字符串在PHP和C#中都是相同的: 键:“24ACD2FCC7B20B8BD33FF45176F03061A09B729487E10

  • 我正在尝试使用 Rijndael 从 php 到 Delphi 再返回加密/解密字符串。 如果我从Delphi解密PHP字符串…工作正常。 如果我使用 Delphi 加密字符串,结果字符串还可以,但更短 为了测试,我使用了一个62个字符的字符串。带有delphi的加密字符串比PHP少4个字符 这些是琴弦...最后的字符: PHP: GyLWj1anBJRmE8mBsaO5cvTrcbvvA== D

  • ===编辑=== 作为对注释的回答,no,这两个实现似乎不计算相同的加密数据,给定相同的密钥和civ值。呼叫 产生一个完全不同的加密字符串,即使考虑到CIV对加密文本的预置,它仍然比delphi端的输出长8个字节。 再次感谢。