c#加密方法到Java

本文关键字:Java 方法 加密 | 更新日期: 2023-09-27 18:05:14

我有一个方法用于将字符串转换为c#中带有键和盐的编码字符串,我正试图在Java中创建一个等效的方法。

c#的方法如下:
    public static string Encrypt<T>(string Value, string Key, string Salt) where T : SymmetricAlgorithm, new()
    {
        DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Key, Encoding.Unicode.GetBytes(Salt));
        SymmetricAlgorithm algorithm = new T();
        byte[] keyBytes = deriveBytes.GetBytes(algorithm.KeySize >> 3);
        byte[] ivBytes = deriveBytes.GetBytes(algorithm.BlockSize >> 3);
        ICryptoTransform transform = algorithm.CreateEncryptor(keyBytes, ivBytes);
        using (MemoryStream buffer = new MemoryStream())
        {
            using (CryptoStream stream = new CryptoStream(buffer, transform, CryptoStreamMode.Write))
            {
                using (StreamWriter writer = new StreamWriter(stream, Encoding.Unicode))
                {
                    writer.Write(Value);
                }
            }
            return Convert.ToBase64String(buffer.ToArray());
        }
    }

我在整个SO和整个网络上尝试了许多不同的解决方案,但无济于事。我甚至有一个样本:

value("YourId|YourFacId")
key("6JxI1HOSg7KQj4fJ1Xb3L1T6AVdLZLBAPFSqOjh2UoA="),
salt("FPSJxiSMpAavjKqyGvVe1A==")

这些都被发送到上面的方法,并返回字符串
"Y5w4A3pDZwTcq+FGyqUMO/mZSr6hSst8qiac9zDbfso9FQQbdTDsKnkKDT7SHl4y" .

我还没有找到任何符合我的问题,所以我在这里寻求帮助。任何线索将不胜感激。谢谢。

试图链接到另一个问题没有显示我没有看到的东西。在我的示例中不需要处理密码。以下是我多次失败的尝试之一:

private String encrypt(String user) throws Exception
{
    Cipher deCipher;
    Cipher enCipher;
    SecretKeySpec key;
    IvParameterSpec ivSpec;
    String plainKey = "6JxI1HOSg7KQj4fJ1Xb3L1T6AVdLZLBAPFSqOjh2UoA=";
    String salt = "FPSJxiSMpAavjKqyGvVe1A==";
    String result = "";
    ivSpec = new IvParameterSpec(salt.getBytes());
    key = new SecretKeySpec(plainkey.getBytes(), "AES");
    enCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] input = convertToByteArray(user);
    enCipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
    return new String(enCipher.doFinal(input).toString());
}

c#加密方法到Java

多亏了Stefan和一些摆弄,我发现c#代码在编码消息值时包含字节顺序标记,但在编码salt值时没有。等效的Java代码如下所示:

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
class SO39257791
{
  private static final int KEY_LEN = 256 / 8, BLOCK_LEN = 16, ITERATIONS = 1000;
  public static void main(String... argv)
    throws Exception
  {
    String value = "YourId|YourFacId";
    String key = "6JxI1HOSg7KQj4fJ1Xb3L1T6AVdLZLBAPFSqOjh2UoA=";
    String salt = "FPSJxiSMpAavjKqyGvVe1A==";
    String good = "Y5w4A3pDZwTcq+FGyqUMO/mZSr6hSst8qiac9zDbfso9FQQbdTDsKnkKDT7SHl4y";
    String output = encrypt(value, key, salt);
    if (output.equals(good))
      System.out.println("strings are equal");
    else
      System.out.println("strings are NOT equal!");
  }
  static final String encrypt(String value, String key, String salt)
    throws GeneralSecurityException, UnsupportedEncodingException
  {
    /* Derive the key, given password and salt. */
    byte[] s = salt.getBytes(StandardCharsets.UTF_16LE);
    int dkLen = (KEY_LEN + BLOCK_LEN) * 8;
    KeySpec spec = new PBEKeySpec(key.toCharArray(), s, ITERATIONS, dkLen);
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    byte[] dk = factory.generateSecret(spec).getEncoded();
    SecretKey secret = new SecretKeySpec(Arrays.copyOfRange(dk, 0, KEY_LEN), "AES");
    byte[] iv = Arrays.copyOfRange(dk, KEY_LEN, KEY_LEN + BLOCK_LEN);
    /* Encrypt the message. */
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
    byte[] plaintext = value.getBytes("UnicodeLittle"); /* Use Byte Order Mark */
    byte[] ciphertext = cipher.doFinal(plaintext);
    return Base64.getEncoder().encodeToString(ciphertext);
  }
}

我只能给你一些部分的信息来帮助你:

使用的算法是256bit AesManaged。模式为CBC,填充为PKCS7, keysize = 256。

给出的样例值可以用这个小测试程序来证明是正确的

static void Main(string[] args)
{
    string key = "6JxI1HOSg7KQj4fJ1Xb3L1T6AVdLZLBAPFSqOjh2UoA=";
    string salt = "FPSJxiSMpAavjKqyGvVe1A==";
    string value = "YourId|YourFacId";
    string result = Encrypt<AesManaged>(value, key, salt);
    Console.WriteLine(result);
    string expected = "Y5w4A3pDZwTcq+FGyqUMO/mZSr6hSst8qiac9zDbfso9FQQbdTDsKnkKDT7SHl4y";
    if (expected.Equals(result))
    {
        Console.WriteLine("strings are equal");
    }
    else
    {
        Console.WriteLine("strings are NOT equal!");
    }
}

keysalt是普通字符串(不是base-64编码)。要获取字节,在Java中必须使用

  byte[] saltBytes = salt.getBytes("UnicodeLittleUnmarked");
  byte[] keyBytes = key.getBytes("UTF-8");

PBKDF2使用SHA1摘要和1000次迭代(我用BouncyCastle的PKCS5S2ParametersGenerator检查了这一点)

  final int iterations = 1000;
  PKCS5S2ParametersGenerator pbkdf = new PKCS5S2ParametersGenerator(new SHA1Digest());
  pbkdf.init(keyBytes, saltBytes, iterations);
  final int keySize = 32 * 8;
  final int ivSize = 16 * 8;
  CipherParameters cp = pbkdf.generateDerivedParameters(keySize, ivSize);

这给了我以下ivBytes和keyBytes

// Java ivBytes
// [-33, 102, -108, 66, -46, 89, 122, 102, -63, -15, -92, 66, -88, -29, 67, -59]
// Java keyBytes:
// [-127, 125, -40, -123, 60, -70, 16, -6, -15, -116, 127, 93, 46, 80, 26, 31, -36, 47, -120, -37, 57, 21, -94, 44, 98, -119, -109, 48, -71, 15, -36, 80]

这些是我在c#代码中得到的带符号的等价物:

// C# ivBytes:
// [223, 102, 148, 66, 210, 89, 122, 102, 193, 241, 164, 66, 168, 227, 67, 197]
// C# keyBytes:
// [129, 125, 216, 133, 60, 186, 16, 250, 241, 140, 127, 93, 46, 80, 26, 31, 220, 47, 136, 219, 57, 21, 162, 44, 98, 137, 147, 48, 185, 15, 220, 80]

当我阅读c#代码时,值字节也必须作为UTF-16小端

检索。
byte[] valueBytes = value.getBytes("UnicodeLittleUnmarked");

从这里开始,我不能再往前走了。我可以告诉您的最后一件事是加密的字节数组(base64编码之前的最后一步)是什么样子的:

// C# crypted:
// [99, 156, 56, 3, 122, 67, 103, 4, 220, 171, 225, 70, 202, 165, 12, 59, 249, 153, 74, 190, 161, 74, 203, 124, 170, 38, 156, 247, 48, 219, 126, 202, 61, 21, 4, 27, 117, 48, 236, 42, 121, 10, 13, 62, 210, 30, 94, 50]
// or signed:
// [99, -100, 56, 3, 122, 67, 103, 4, -36, -85, -31, 70, -54, -91, 12, 59, -7, -103, 74, -66, -95, 74, -53, 124, -86, 38, -100, -9, 48, -37, 126, -54, 61, 21, 4, 27, 117, 48, -20, 42, 121, 10, 13, 62, -46, 30, 94, 50]