将 Java AES CBC 加密转换为 C#
Converting Java AES CBC Encryption to C#
我正在尝试将以下 Java 加密片段转换为 C#
private final String encryptToken(String str) {
byte[] generateIV = generateIV(16);
Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
instance.init(1, new SecretKeySpec(new byte[]{55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55}, "AES/CBC/PKCS5Padding"), new IvParameterSpec(generateIV));
Charset charset = Charsets.UTF_8;
if (str != null) {
byte[] bytes = str.getBytes(charset);
Intrinsics.checkExpressionValueIsNotNull(bytes, "(this as java.lang.String).getBytes(charset)");
byte[] doFinal = instance.doFinal(bytes);
StringBuilder sb = new StringBuilder();
sb.append(new String(generateIV, Charsets.UTF_8));
String encodeToString = Base64.encodeToString(doFinal, 0);
Intrinsics.checkExpressionValueIsNotNull(encodeToString, "Base64.encodeToString(cipherText, Base64.DEFAULT)");
if (encodeToString != null) {
sb.append(StringsKt.trim((CharSequence) encodeToString).toString());
return sb.toString();
}
throw new TypeCastException("null cannot be cast to non-null type kotlin.CharSequence");
}
throw new TypeCastException("null cannot be cast to non-null type java.lang.String");
}
这是我的 C# 代码:
public static string Encrypt(string token)
{
byte[] IV;
byte[] encrypted;
byte[] key = new byte[] { 55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55 };
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = key;
aesAlg.IV = GenerateIv(16);
IV = aesAlg.IV;
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(Encoding.UTF8.GetBytes(token));
}
encrypted = msEncrypt.ToArray();
StringBuilder sb = new StringBuilder();
sb.Append(Encoding.UTF8.GetString(IV));
string encodedString = Convert.ToBase64String(encrypted);
sb.Append(encodedString.ToString());
return sb.ToString();
}
}
}
return "";
}
我正在使用加密例程的结果来授权我无法控制的 API。使用我的 C# 代码,API returns“未授权”是我的加密例程不太正确的结果。
我注意到 Java 方法使用 PKCS5 填充,但 .NET 中唯一可用的选项是 PKCS7。但是阅读 之前的文章,我知道它们是相同的,因此应该无关紧要。
如果我使用 h62PmLFO5Yq4SxQw
的临时 IV 和 4a97adfc-d25c-485b-84af-86c93ff28b20
的 token
,Java 代码 returns:h62PmLFO5Yq4SxQwgoOqOLrjcZmnYbKxEDhl1hsDeCGUmEv8kwwP337JfYyvjuXgaDdND9vqSeAd9NpH
和我的 C# 代码 returns: h62PmLFO5Yq4SxQwfUXW396ss2Pzopk0FHC/7A==
有人能就 Java 代码和我的代码之间的任何明显差异提出建议吗?
原因是您正在使用 StreamWriter
,它旨在将 text 写入流中,但随后您在那里写入了原始字节:
swEncrypt.Write(Encoding.UTF8.GetBytes(token));
此处使用的重载是 Write(object)
,描述为“通过对该对象调用 ToString() 方法,将对象的文本表示形式写入流”。所以基本上你是在加密“System.Byte[]”字符串,而不是令牌。
您可以删除该文本编写器并将字节直接写入加密流(另请注意 leaveOpen: true
):
public static string Encrypt(string token)
{
byte[] IV;
byte[] encrypted;
byte[] key = new byte[] { 55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55 };
using (var aesAlg = new RijndaelManaged())
{
aesAlg.Key = key;
var iv = Encoding.UTF8.GetBytes("h62PmLFO5Yq4SxQw");
aesAlg.IV = iv;
IV = aesAlg.IV;
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write, leaveOpen: true))
{
// just write to stream directly
csEncrypt.Write(Encoding.UTF8.GetBytes(token));
}
// read here, so that we are sure writing is finished
encrypted = msEncrypt.ToArray();
StringBuilder sb = new StringBuilder();
sb.Append(Encoding.UTF8.GetString(IV));
string encodedString = Convert.ToBase64String(encrypted);
sb.Append(encodedString.ToString());
return sb.ToString();
}
}
return "";
}
或者您可以继续使用 StreamWriter
,但请正确使用:
// tell it you need UTF8 without BOM
using (var swEncrypt = new StreamWriter(csEncrypt, new UTF8Encoding(false)))
{
// then write string there, not bytes
swEncrypt.Write(token);
}
我正在尝试将以下 Java 加密片段转换为 C#
private final String encryptToken(String str) {
byte[] generateIV = generateIV(16);
Cipher instance = Cipher.getInstance("AES/CBC/PKCS5Padding");
instance.init(1, new SecretKeySpec(new byte[]{55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55}, "AES/CBC/PKCS5Padding"), new IvParameterSpec(generateIV));
Charset charset = Charsets.UTF_8;
if (str != null) {
byte[] bytes = str.getBytes(charset);
Intrinsics.checkExpressionValueIsNotNull(bytes, "(this as java.lang.String).getBytes(charset)");
byte[] doFinal = instance.doFinal(bytes);
StringBuilder sb = new StringBuilder();
sb.append(new String(generateIV, Charsets.UTF_8));
String encodeToString = Base64.encodeToString(doFinal, 0);
Intrinsics.checkExpressionValueIsNotNull(encodeToString, "Base64.encodeToString(cipherText, Base64.DEFAULT)");
if (encodeToString != null) {
sb.append(StringsKt.trim((CharSequence) encodeToString).toString());
return sb.toString();
}
throw new TypeCastException("null cannot be cast to non-null type kotlin.CharSequence");
}
throw new TypeCastException("null cannot be cast to non-null type java.lang.String");
}
这是我的 C# 代码:
public static string Encrypt(string token)
{
byte[] IV;
byte[] encrypted;
byte[] key = new byte[] { 55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55 };
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = key;
aesAlg.IV = GenerateIv(16);
IV = aesAlg.IV;
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
{
using (var swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(Encoding.UTF8.GetBytes(token));
}
encrypted = msEncrypt.ToArray();
StringBuilder sb = new StringBuilder();
sb.Append(Encoding.UTF8.GetString(IV));
string encodedString = Convert.ToBase64String(encrypted);
sb.Append(encodedString.ToString());
return sb.ToString();
}
}
}
return "";
}
我正在使用加密例程的结果来授权我无法控制的 API。使用我的 C# 代码,API returns“未授权”是我的加密例程不太正确的结果。
我注意到 Java 方法使用 PKCS5 填充,但 .NET 中唯一可用的选项是 PKCS7。但是阅读
如果我使用 h62PmLFO5Yq4SxQw
的临时 IV 和 4a97adfc-d25c-485b-84af-86c93ff28b20
的 token
,Java 代码 returns:h62PmLFO5Yq4SxQwgoOqOLrjcZmnYbKxEDhl1hsDeCGUmEv8kwwP337JfYyvjuXgaDdND9vqSeAd9NpH
和我的 C# 代码 returns: h62PmLFO5Yq4SxQwfUXW396ss2Pzopk0FHC/7A==
有人能就 Java 代码和我的代码之间的任何明显差异提出建议吗?
原因是您正在使用 StreamWriter
,它旨在将 text 写入流中,但随后您在那里写入了原始字节:
swEncrypt.Write(Encoding.UTF8.GetBytes(token));
此处使用的重载是 Write(object)
,描述为“通过对该对象调用 ToString() 方法,将对象的文本表示形式写入流”。所以基本上你是在加密“System.Byte[]”字符串,而不是令牌。
您可以删除该文本编写器并将字节直接写入加密流(另请注意 leaveOpen: true
):
public static string Encrypt(string token)
{
byte[] IV;
byte[] encrypted;
byte[] key = new byte[] { 55, 66, 53, 68, 55, 66, 67, 50, 52, 66, 53, 67, 52, 69, 51, 65, 56, 48, 70, 66, 66, 67, 50, 65, 49, 49, 53, 54, 69, 52, 51, 55 };
using (var aesAlg = new RijndaelManaged())
{
aesAlg.Key = key;
var iv = Encoding.UTF8.GetBytes("h62PmLFO5Yq4SxQw");
aesAlg.IV = iv;
IV = aesAlg.IV;
aesAlg.Mode = CipherMode.CBC;
aesAlg.Padding = PaddingMode.PKCS7;
var encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
using (var msEncrypt = new MemoryStream())
{
using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write, leaveOpen: true))
{
// just write to stream directly
csEncrypt.Write(Encoding.UTF8.GetBytes(token));
}
// read here, so that we are sure writing is finished
encrypted = msEncrypt.ToArray();
StringBuilder sb = new StringBuilder();
sb.Append(Encoding.UTF8.GetString(IV));
string encodedString = Convert.ToBase64String(encrypted);
sb.Append(encodedString.ToString());
return sb.ToString();
}
}
return "";
}
或者您可以继续使用 StreamWriter
,但请正确使用:
// tell it you need UTF8 without BOM
using (var swEncrypt = new StreamWriter(csEncrypt, new UTF8Encoding(false)))
{
// then write string there, not bytes
swEncrypt.Write(token);
}