0

AES暗号化アルゴリズムとSHA1PRNGハッシュを使用して文字列を暗号化および復号化するAndroidデバイスで使用されている次のJavaコードがあります。AndroidデバイスでC#で記述された.NETWCFサービスを呼び出すようにします。私は、Javaコードと同様の方法で暗号化および復号化できる、C#で同等のものを見つけようとあらゆる場所を検索してきましたが、まったく同じ方法を見つけることができませんでした。両方の言語のEncrypt()メソッドは次のとおりです。

Java:

public static String encrypt(String seed, String cleartext) throws Exception 
{
    KeyGenerator kgen = KeyGenerator.getInstance("AES");
    SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
    sr.setSeed(seed);
    kgen.init(128, sr); // 192 and 256 bits may not be available
    SecretKey skey = kgen.generateKey();

    byte[] rawKey = skey.getEncoded();
    SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
    byte[] encrypted = cipher.doFinal(cleartext.getBytes());
    return toHex(encrypted);
}

これに似たものをC#で作成しましたが、これもAESとSHA1を使用しています。

C#:

public static string Encrypt(string seed, string cleartext)
{
  var objAesCrypto = new AesManaged();
  var objHashSha1 = new SHA1Managed();

  var byteHash = objHashSha1.ComputeHash(Encoding.ASCII.GetBytes(seed));
  var truncatedHash = new byte[16];
  Array.Copy(byteHash, truncatedHash, truncatedHash.Length);
  objAesCrypto.Key = truncatedHash;
  objAesCrypto.Mode = CipherMode.ECB;

  var byteBuff = Encoding.ASCII.GetBytes(cleartext);
  return Convert.ToBase64String(objAesCrypto.CreateEncryptor().TransformFinalBlock(byteBuff, 0, byteBuff.Length));
}

ただし、これにはいくつかの問題があります。ご覧のとおり、C#バージョンのSHA1(SHA1Managed)を使用すると、16ではなく20バイトのハッシュが返されます。AESアルゴリズムに渡す唯一の方法は、最初にハッシュを16バイトに切り捨てることです。

2番目の問題は、どちらもそれぞれの環境で正常に機能しますが、Javaから暗号化された文字列をシードと一緒に渡そうとすると、C#コードで正しく復号化できないことです。どちらの場合も、暗号化された文字列は同じようには見えず、長さも異なります。Java側からの典型的な暗号化された文字列は、次のようになります: F7E8758A2E65518FB49C53BC707288FC(32文字の長さ)。C#側からの同じ正確なシードを持つ同じ正確な暗号化文字列は次のようになります: 3VysgnYgNi9OJBxL2FP+rQ==(24文字の長さ)。

私がC#でハッシュを切り捨てているという事実と関係があると確信していますが、それは2つの暗号化された文字列がそれほど大きく異なる理由を説明していません。(私が気付いたもう1つの興味深い点は、C#側で使用する文字列とシードに関係なく、常に24文字の長さで、2つの等号で終わることです。これはなぜですか?)

だから、私の質問は、同じシード値を使用して両方の環境が互いの暗号化された文字列を復号化できるようにするにはどうすればよいですか?C#側でJava側とは異なるアルゴリズムを使用する必要があるかどうかは関係ありません。必要なのは、Javaで暗号化された文字列を読み取れるようにするためのC#コードだけです。

4

3 に答える 3

1

2番目の問題は、どちらもそれぞれの環境で正常に機能しますが、Javaから暗号化された文字列をシードとともに渡そうとすると、C#コードで正しく復号化できないことです。

ハッシュを復号化しようとしてはいけません。ハッシュは一方向です。

Java側からの一般的な暗号化文字列は、次のようになります。F7E8758A2E65518FB49C53BC707288FC(32文字の長さ)。C#側からの同じ正確なシードを持つ同じ正確な暗号化文字列は次のようになります:3VysgnYgNi9OJBxL2FP + rQ ==(24文字の長さ)。

これは、Javaでは16進数に変換しているが、C#ではBase64に変換しているためです。

return toHex(encrypted);

vs

return Convert.ToBase64String(...);

シードの長さの問題については、JavaとC#で異なることを行っています。SecureRandomそのように使用することが、SHA1からのストレートハッシュを使用するのと同じ秘密鍵を生成することを意味することは、私にはまったくわかりません。

ただし、このアプローチを修正しようとするのではなく、再考する必要があることをお勧めします。私にはまったく安全に見えません。あなたがシードと呼んでいるものは単なるシードではありません-それは基本的に完全な鍵です。シードを知っている攻撃者は、システムの「パスワード」を効果的に知っています。生のバイトを使用する方がよいでしょう。

于 2012-08-30T05:45:05.533 に答える
1

AndroidはSHA1PRNGの固定バージョンを使用しているようです。また、SHA1PRNG for .NET / Java/Androidには多くの実装があるようです。

同様の問題と、Androidに存在するSHA1PRNGのC#への移植の可能性については、以下のリンクを参照してください。 AndroidのSHA1PRNG-.NET

于 2012-08-30T07:42:39.687 に答える
0

私の知る限り、あなたtoHex(encrypted);は同じものではありません。Convert.ToBase64String()

于 2012-08-30T05:44:42.723 に答える