繁体   English   中英

Java的C#加密方法

[英]C# encryption method to Java

我有一种方法,可以在C#中使用键和盐将String转换为编码的String,并尝试在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和整个Web上都尝试了许多不同的解决方案,但都无济于事。 我什至有一个样本:

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

这些都发送到上述方法,并返回以下返回字符串:
"Y5w4A3pDZwTcq+FGyqUMO/mZSr6hSst8qiac9zDbfso9FQQbdTDsKnkKDT7SHl4y"

我尚未在SO中找到与我的问题匹配的任何内容,因此我在这里寻求帮助。 任何线索将不胜感激。 谢谢。

尝试链接到另一个问题,没有发现我所没有的东西。 在我的示例中,没有密码要处理。 这是我为此失败的许多尝试之一:

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());
}

我只能为您提供部分信息,以帮助您继续前进:

使用的算法是256位AesManaged 模式是CBC ,填充是PKCS7 ,密钥大小= 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 little-endian

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

从这里开始,我无法继续。 我能告诉你的最后一件事是加密的字节数组(在base-64编码之前的最后一步)是什么样的:

// 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]

多亏了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);
  }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM