有一個服務器在C#(方法encryptCSharp)上加密密碼,android應用程序接收加密的密碼,salt,passPhrase和initVector。 我必須在Java中解密該密碼。 服務器人向我發送了C#中的加密方法,我需要創建encryptJava和decryptJava方法,這些方法在Java中與C#中的工作方式相同。 要創建在Java中缺席的PasswordDeriveBytes
我使用這裏的示例Encryption Diff Between Java and C#C#和Java之間的加密/解密
所以,我的問題是,我的Java方法出了什麼問題?兩者都沒有工作
更新:我做了片段的一些變化,現在所有的作品!
我調用一個方法:
String encryptedText = encryptJava("12345", "100", "@.erf.net34", "@[email protected]");//it works!!
String decryptedText = decryptJava(encryptedText, "100", "@.erf.net34", "@[email protected]");//it doesn't work!!
這裏是我使用的Java方法和從服務器C#方法。
C#(我不能改變)
public static String encryptCSharp(String plainText, String saltValue, String passPhrase, String initVector) {
String hashAlgorithm = "SHA1";
int passwordIterations = 1;
int keySize = 256;
// Convert strings into byte arrays.
// Let us assume that strings only contain ASCII codes.
// If strings include Unicode characters, use Unicode, UTF7, or UTF8
// encoding.
byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);
// Convert our plaintext into a byte array.
// Let us assume that plaintext contains UTF8-encoded characters.
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
// First, we must create a password, from which the key will be derived.
// This password will be generated from the specified passphrase and
// salt value. The password will be created using the specified hash
// algorithm. Password creation can be done in several iterations.
PasswordDeriveBytes password = new PasswordDeriveBytes(
passPhrase,
saltValueBytes,
hashAlgorithm,
passwordIterations);
// Use the password to generate pseudo-random bytes for the encryption
// key. Specify the size of the key in bytes (instead of bits).
byte[] keyBytes = password.GetBytes(keySize/8);
// Create uninitialized Rijndael encryption object.
RijndaelManaged symmetricKey = new RijndaelManaged();
// It is reasonable to set encryption mode to Cipher Block Chaining
// (CBC). Use default options for other symmetric key parameters.
symmetricKey.Mode = CipherMode.CBC;
// Generate encryptor from the existing key bytes and initialization
// vector. Key size will be defined based on the number of the key
// bytes.
ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
keyBytes,
initVectorBytes);
// Define memory stream which will be used to hold encrypted data.
MemoryStream memoryStream = new MemoryStream();
// Define cryptographic stream (always use Write mode for encryption).
CryptoStream cryptoStream = new CryptoStream(memoryStream,
encryptor,
CryptoStreamMode.Write);
// Start encrypting.
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
// Finish encrypting.
cryptoStream.FlushFinalBlock();
// Convert our encrypted data from a memory stream into a byte array.
byte[] cipherTextBytes = memoryStream.ToArray();
// Close both streams.
memoryStream.Close();
cryptoStream.Close();
// Convert encrypted data into a base64-encoded string.
String cipherText = Convert.ToBase64String(cipherTextBytes);
// Return encrypted string.
return cipherText;
}
的Java 加密的消息,它的工作原理,我得到了相同的結果的方法在C#
private String encryptJava(String plainText, String saltValue, String passPhrase, String initVector) {//working!!!
String result = "";
byte[] initVectorBytes = initVector.getBytes(US_ASCII);
byte[] saltValueBytes = saltValue.getBytes(US_ASCII);
byte[] plainTextBytes = plainText.getBytes(UTF_8);
Cipher cipher;
try {
final com.gmail.example.PasswordDeriveBytes password = new com.gmail.example.PasswordDeriveBytes(passPhrase, saltValueBytes);
final byte[] keyBytes = password.getBytes(256/Byte.SIZE);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initVectorBytes));
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
final byte[] ct = cipher.doFinal(plainTextBytes);
result = Base64.encodeToString(ct, Base64.DEFAULT);//**added this line!**
//result = new String(ct, "US-ASCII");**-- deleted this line!**
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return result;
}
Java方法模擬C#中的相同方法#
public class PasswordDeriveBytes {
private final MessageDigest hash;
private final byte[] firstToLastDigest;
private final byte[] outputBuffer;
private int position = 0;
public PasswordDeriveBytes(String password, byte[] salt) {
try {
this.hash = MessageDigest.getInstance("SHA-1");
this.hash.update(password.getBytes(UTF_8));
this.hash.update(salt);
this.firstToLastDigest = this.hash.digest();
final int iterations = 1;//**changed from 100**
for (int i = 1; i < iterations - 1; i++) {
hash.update(firstToLastDigest);
hash.digest(firstToLastDigest, 0, firstToLastDigest.length);
}
this.outputBuffer = hash.digest(firstToLastDigest);
} catch (NoSuchAlgorithmException | DigestException e) {
throw new IllegalStateException("SHA-1 digest should always be available", e);
}
}
public byte[] getBytes(int requested) {
if (requested < 1) {
throw new IllegalArgumentException(
"You should at least request 1 byte");
}
byte[] result = new byte[requested];
int generated = 0;
try {
while (generated < requested) {
final int outputOffset = position % outputBuffer.length;
if (outputOffset == 0 && position != 0) {
final String counter = String.valueOf(position/outputBuffer.length);
hash.update(counter.getBytes(US_ASCII));
hash.update(firstToLastDigest);
hash.digest(outputBuffer, 0, outputBuffer.length);
}
final int left = outputBuffer.length - outputOffset;
final int required = requested - generated;
final int copy = Math.min(left, required);
System.arraycopy(outputBuffer, outputOffset, result, generated, copy);
generated += copy;
position += copy;
}
} catch (final DigestException e) {
throw new IllegalStateException(e);
}
return result;
}
}
最後Java方法不起作用並嘗試使用erstand我做什麼錯
private String decryptJava(String encryptedText, String saltValue, String passPhrase, String initVector) {
String result = "";
byte[] initVectorBytes = initVector.getBytes(US_ASCII);
byte[] saltValueBytes = saltValue.getBytes(US_ASCII);
byte[] encryptedTexttBytes = Base64.decode(encryptedText, Base64.DEFAULT);
Cipher cipher;
try {
final PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, saltValueBytes);
final byte[] keyBytes = password.getBytes(256/Byte.SIZE);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
try {
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(initVectorBytes));
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
final byte[] ct = cipher.doFinal(encryptedTexttBytes);
//result = Base64.encodeToString(ct, Base64.DEFAULT); - **deleted this line**
try {
result = new String(ct, "US-ASCII");//** added this line**
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return result;
}
我沒有經驗的密碼學,所以我試着理解你的答案。你的意思是,與微軟服務器合作的人必須將方法從'PasswordDeriveBytes'改爲'Rfc2898DeriveBytes'? – LumisD
是的,那肯定是可取的。 'PasswordDeriveBytes'已被'Rfc2898DeriveBytes'取代。據我所知,沒有人完全描述了早期使用的擴展算法(它可能包含一些可怕的緩衝區溢出)。 –