加密AES与C#匹配Java加密 [英] Encrypt AES with C# to match Java encryption

查看:157
本文介绍了加密AES与C#匹配Java加密的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经获得了Java实现加密,但不幸的是,我们是一个.net商店,我无法将Java并入我们的解决方案。可悲的是,我也不是一个Java的家伙,所以我一直在和这几天进行斗争,并且以为我终于转过身来寻求帮助。



ve搜索高低,以达到Java加密方式正常运行的方式,我已经来到了我需要在c#中使用RijndaelManaged的分辨率。我其实真的很近我在c#中返回的字符串与上半部分匹配,但下半部分是不同的。



这是一个Java实现的代码片段:

  private static String EncryptBy16(String str,String theKey)throws异常
{

if(str = = null || str.length()> 16)
{
throw new NullPointerException();
}
int len = str.length();
byte [] pidBytes = str.getBytes();
byte [] pidPaddedBytes = new byte [16]; (int x = 0; x <16; x ++)

如果(x {
pidPaddedBytes [x] = pidBytes [X];
}
else
{
pidPaddedBytes [x] =(byte)0x0;
}

}

byte [] raw = asBinary(theKey);
SecretKeySpec myKeySpec = new SecretKeySpec(raw,AES);
密码myCipher = Cipher.getInstance(AES / ECB / NoPadding);
cipher.init(Cipher.ENCRYPT_MODE,myKeySpec);
byte [] encrypted = myCipher.doFinal(pidPaddedBytes);
return(ByteToString(encrypted));
}

public static String Encrypt(String stringToEncrypt,String key)throws异常
{

if(stringToEncrypt == null){
抛出新的NullPointerException();
}
String str = stringToEncrypt;

StringBuffer result = new StringBuffer();
do {
String s = str;
if(s.length()> 16){
str = s.substring(16);
s = s.substring(0,16);
} else {
str = null;
}
result.append(EncryptBy16(s,key));
} while(str!= null);

return result.toString();
}

我不完全确定为什么他们只传递16个字符一个时间,但是,我使用字符串构建器尝试了我的c#实现,并且一次只发送16个字符,并且得到与我一次传递整个字符串相同的结果。



这是一个我的c#实现的代码片段,主要是针对RijndaelManaged的MS站点的复制和粘贴:

  public static string Encrypt(string stringToEncrypt,string key)
{
using(RijndaelManaged myRijndael = new RijndaelManaged())
{
myRijndael.Key = StringToByte(key);
myRijndael.IV = new byte [16];
return EncryptStringToBytes(stringToEncrypt,myRijndael.Key,myRijndael.IV);



static string EncryptStringToBytes(string plainText,byte [] Key,byte [] IV)
{
if(plainText == null || plainText.Length< = 0)
throw new ArgumentNullException(plainText);
if(Key == null || Key.Length< = 0)
throw new ArgumentNullException(Key);
if(IV == null || IV.Length <= 0)
throw new ArgumentNullException(Key);
byte []加密;
using(RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key,rijAlg.IV);
using(MemoryStream msEncrypt = new MemoryStream())
{
using(CryptoStream csEncrypt = new CryptoStream(msEncrypt,encryptor,CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{
swEncrypt.Write(plainText);
}
encrypted = msEncrypt.ToArray();
}
}
}
返回ByteToString(加密);
}

如上所述,加密字符串的前半部分是相同的看下面的例子),但是下半场是关闭的。我在下面的输出中添加了空格,以便更好地说明差异在哪里。我不了解加密和Java知道哪里下一个。任何指导将不胜感激



Java输出:


49a85367ec8bc387bb44963b54528c97 8026d7eaeff9e4cb7cf74f8227f80752


C#输出:


49a85367ec8bc387bb44963b54528c97 718f574341593be65034627a6505f13c


根据Chris下面的建议更新:

  static string EncryptStringToBytes(string plainText,byte [] Key,byte [] IV)
{
if(plainText == null || plainText.Length< = 0)
throw新的ArgumentNullException(plainText);
if(Key == null || Key.Length< = 0)
throw new ArgumentNullException(Key);
if(IV == null || IV.Length <= 0)
throw new ArgumentNullException(Key);
byte []加密;
using(RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Key = Key;
rijAlg.IV = IV;
rijAlg.Padding = PaddingMode.None;
rijAlg.Mode = CipherMode.ECB;
ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key,rijAlg.IV);
using(MemoryStream msEncrypt = new MemoryStream())
{
using(CryptoStream csEncrypt = new CryptoStream(msEncrypt,encryptor,CryptoStreamMode.Write))
{
using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
{

swEncrypt.Write(plainText);
if(plainText.Length< 16)
{
for(int i = plainText.Length; i< 16; i ++)
{
swEncrypt.Write ((字节)为0x0);
}
}
}
encrypted = msEncrypt.ToArray();
}
}
}
返回ByteToString(加密);
}


解决方案

您的C#翻译看起来像做大部分正确的事情,因为第一个块匹配。最后一个块不匹配,这是因为Java代码是零填充最后一个块来填补它,而您的C#代码不会这样做,所以默认使用PKCS#5填充。



当然,PKCS#5填充比零填充好多了,但由于后者是Java代码使用的,所以你必须做同样的事情。 (这意味着,再调用一次 swEncrypt.Write((byte)0),直到字节数为16的倍数。)



还有另一个细微之处。 Java代码使用 String.getBytes()将字符串转换为字节,它使用Java运行时的默认编码。这意味着如果您的字符串包含非ASCII字符,则会遇到互操作性问题。最好的做法是使用UTF-8,但是看不到你不能改变Java代码,我想你可以做的不多。


I have been given a Java implementation for encryption but unfortunately we are a .net shop and I have no way of incorporating the Java into our solution. Sadly, I'm also not a Java guy so I've been fighting with this for a few days and thought I'd finally turn here for help.

I've searched high and low for a way to match the way the Java encryption is working and I've come to the resolution that I need to use RijndaelManaged in c#. I'm actually really close. The strings that I'm returning in c# are matching the first half, but the second half are different.

Here is a snippet of the java implementation:

private static String EncryptBy16( String str, String theKey) throws Exception
{

    if ( str == null || str.length() > 16)
    {
        throw new NullPointerException();
    }
    int len = str.length();
    byte[] pidBytes = str.getBytes();
    byte[] pidPaddedBytes = new byte[16];

    for ( int x=0; x<16; x++ )
    {
        if ( x<len )
        {
            pidPaddedBytes[x] = pidBytes[x];
        }
        else
        {
            pidPaddedBytes[x] = (byte) 0x0;
        }

    }

    byte[] raw = asBinary( theKey );
    SecretKeySpec myKeySpec = new SecretKeySpec( raw, "AES" );
    Cipher myCipher = Cipher.getInstance( "AES/ECB/NoPadding" );
    cipher.init( Cipher.ENCRYPT_MODE, myKeySpec );
    byte[] encrypted = myCipher.doFinal( pidPaddedBytes );
    return( ByteToString( encrypted ) );
}

public static String Encrypt(String stringToEncrypt, String key) throws Exception
{

    if ( stringToEncrypt == null ){
        throw new NullPointerException();
    }
    String str = stringToEncrypt;

    StringBuffer result = new StringBuffer();
    do{
        String s = str;
        if(s.length() > 16){
            str = s.substring(16);
            s = s.substring(0,16);
        }else {
            str = null;
        }
        result.append(EncryptBy16(s,key));
    }while(str != null);

    return result.toString();
}

I'm not entirely sure why they're only passing in 16 chars at a time, but w/e. I tried the same with my c# implementation using a string builder and only sending in 16 chars at a time and got the same result as I got when I pass the entire string in at once.

Here's a snippet of my c# implementation which is mostly a copy and paste from MS' site for RijndaelManaged:

public static string Encrypt(string stringToEncrypt, string key)
        {
            using (RijndaelManaged myRijndael = new RijndaelManaged())
            {
                myRijndael.Key = StringToByte(key);
                myRijndael.IV = new byte[16];
                return EncryptStringToBytes(stringToEncrypt, myRijndael.Key, myRijndael.IV);
            }
        }

static string EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
        {
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("Key");
            byte[] encrypted;
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = IV;
                ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }
            return ByteToString(encrypted);
        }

As I said above, the first half of the encrypted string is the same (see an example below), but the second half is off. I've added spaces in the outputs below to better illustrate where the difference is. I don't know enough about encryption nor Java to know where to turn next. Any guidance would be greatly appreciated

Java output:

49a85367ec8bc387bb44963b54528c97 8026d7eaeff9e4cb7cf74f8227f80752

C# output:

49a85367ec8bc387bb44963b54528c97 718f574341593be65034627a6505f13c

Update per the suggestion of Chris below:

static string EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
{
    if (plainText == null || plainText.Length <= 0)
        throw new ArgumentNullException("plainText");
    if (Key == null || Key.Length <= 0)
        throw new ArgumentNullException("Key");
    if (IV == null || IV.Length <= 0)
        throw new ArgumentNullException("Key");
    byte[] encrypted;
    using (RijndaelManaged rijAlg = new RijndaelManaged())
    {
        rijAlg.Key = Key;
        rijAlg.IV = IV;
        rijAlg.Padding = PaddingMode.None;
        rijAlg.Mode = CipherMode.ECB;
        ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);
        using (MemoryStream msEncrypt = new MemoryStream())
        {
            using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
            {
                using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                {

                    swEncrypt.Write(plainText);
                    if (plainText.Length < 16)
                    {
                        for (int i = plainText.Length; i < 16; i++)
                        {
                            swEncrypt.Write((byte)0x0);
                        }
                    }
                }
                encrypted = msEncrypt.ToArray();
            }
        }
    }
    return ByteToString(encrypted);
}

解决方案

Your C# translation looks like it's doing the right thing for the most part, because the first block matches. What doesn't match is the last block, and that's because the Java code is zero-padding the last block to fill it out, whereas your C# code doesn't do that, so it'd use PKCS #5 padding by default.

PKCS #5 padding is much better than zero-padding, of course, but since the latter is what the Java code used, you'd have to do the same thing. (That means, call swEncrypt.Write((byte) 0) a few more times until the byte count is a multiple of 16.)

There's yet another subtlety. The Java code translates the string to bytes using String.getBytes(), which uses the "default encoding" of the Java runtime. This means that if your string contains non-ASCII characters, you'd run into interoperability issues. Best practice is to use UTF-8, but seeing as you can't change the Java code, I guess there's not much you can do about that.

这篇关于加密AES与C#匹配Java加密的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆