Расшифруйте C # RIJNDAEL кодированный текст

1

Я реализую в Java связь с сторонним приложением. Как часть процесса входа в систему, стороннее приложение отправляет зашифрованную строку, которую я должен декодировать и отправлять обратно. Я потратил почти 2 дня на googeling и чтение сообщений, но я не могу найти правильный способ реализовать это.

У меня есть тестовый пример, где зашифрованная строка "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY =", которая расшифровывается с паролем "GAT", должна возвращать "101714994".

В документации, в которой я указываю это: строка авторизации была зашифрована со следующими настройками:

  • Заполнение входных данных: PKCS * 7
  • Массив байтов в байтах длиной 32 байта. Строка пароля преобразуется в кодированный байтовый массив UTF-16, а массив байтов заполняется нулями до 32 байт. Более длинные пароли усекаются.

Это пример С#, как расшифровать строку авторизации:

/// <summary> 
/// Decrypts a string. 
/// </summary> 
/// <param name="content">The string to decrypt.</param> 
/// <param name="password">The password to use.</param> 
/// <returns>The decrypted string.</returns> 
private static string DecryptString(string content, string password) { 
    Rijndael aes; 
    byte[] retVal = null; 
    byte[] contentBytes; 
    byte[] passwordBytes; 
    byte[] ivBytes;
    try { 
        //Get the content as byte[] 
        contentBytes = Convert.FromBase64String(content);

        //Create the password and initial vector bytes 
        passwordBytes = new byte[32]; 
        ivBytes = new byte[16]; 
        Array.Copy(Encoding.Unicode.GetBytes(password), passwordBytes, Encoding.Unicode.GetBytes(password).Length); 
        Array.Copy(passwordBytes, ivBytes, 16);

        //Create the cryptograpy 
        object aes = Rijndael.Create(); 
        aes.Key = passwordBytes; 
        aes.IV = ivBytes; 
        aes.Padding = PaddingMode.PKCS7;

        //Decrypt 
        retVal = aes.CreateDecryptor().TransformFinalBlock(contentBytes, 0, contentBytes.Length); 
    } 
    catch { 
    }
    aes = null; 
    contentBytes = null; 
    passwordBytes = null; 
    ivBytes = null;
    return Encoding.Unicode.GetString(retVal) 
}

Это моя Java-процедура для расшифровки строки:

private String decryptAuthorizationString(String authString, String password) {
  try {
    //Force the test string
    authString = "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY=";
    //Force the test password
    password = "GAT";

    //Create the password and initial vector bytes
    byte[] passwordBytes= new byte[32];      
    byte[] b= password.getBytes("UTF-8");      
    int len= b.length;
    if (len > passwordBytes.length) len = passwordBytes.length;
      System.arraycopy(b, 0, passwordBytes, 0, len);

    byte[] ivBytes= new byte[16];
    System.arraycopy(passwordBytes, 0, ivBytes, 0, 16);

    //Get the authString as byte[]
    byte[] authBytes = new BASE64Decoder().decodeBuffer(authString);

    InputStream inputStream = new ByteArrayInputStream(authBytes);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    // If you have Bouncycastle library installed, you can use
    // Rijndael/CBC/PKCS7PADDING directly.
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    Cipher cipher = Cipher.getInstance("Rijndael/CBC/PKCS7PADDING", "BC");

    // convertedSecureString and initVector must be byte[] with correct length
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(passwordBytes, "AES"), new IvParameterSpec(ivBytes));

    CipherInputStream cryptoStream = new CipherInputStream(inputStream, cipher);
    byte[] buffer = new byte[1024];
    len = cryptoStream.read(buffer, 0, buffer.length);
    while (len > 0) {
      outputStream.write(buffer, 0, len);
      len = cryptoStream.read(buffer, 0, buffer.length);
    }

    outputStream.flush();
    cryptoStream.close();
    String resStr = outputStream.toString("UTF-8");       
    return resStr; //<<--- resStr must be "101714994"
  } catch (Throwable t) {

  }
  return null;
}

Процедура выполняется без ошибок, но результат не тот, в чем я нуждаюсь. Любая помощь будет очень оценена.

  • 0
    Используете ли вы режим ECB в C # и режим CBC в Java?
  • 2
    Не используйте sun.misc.BASE64Decoder . Это внутреннее и не поддерживается .
Показать ещё 2 комментария
Теги:
encryption
cryptography
rijndael

1 ответ

4
Лучший ответ

Для этого вам не нужен BouncyCastleProvider, поскольку AES уже включен в Java. Однако заполнение PKCS # 7 неправильно указано "PKCS5Padding", поэтому "AES/CBC/PKCS7Padding" нельзя указать без Bouncy Castle.

Unicode умолчанию для.NET на самом деле более совместима с UTF-16LE. Оставьте это Microsoft, чтобы не придерживаться стандартных имен (хотя они, возможно, предшествовали ей).

Java JCE на самом деле не строит потоки, как классы С#, поэтому лучше избегать потоковой передачи.

Я переписал ваш пример кода, чтобы показать, как правильно его кодировать на Java (вам нужно быть совместимым с Java 7, хотя). Не перетаскивайте исключения из таблицы, превращайте их в AssertError или RuntimeException s.

Я использую декодер Bouncy Castle Base 64, так как он был доступен как для вас, так и для меня (но в остальном это не зависит от Bouncy). Java 8 имеет базовый класс 64.


Так что без дальнейших церемоний:

import static java.nio.charset.StandardCharsets.UTF_16LE;

import java.security.GeneralSecurityException;
import java.util.Arrays;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.util.encoders.Base64;

public class AuthenticationStringDecrypter {

    private static final String AES_CBC_PKCS5PADDING = "AES/CBC/PKCS5PADDING";
    private static final int KEY_SIZE = 256;

    public static void main(final String[] args) throws Exception {
        System.out.println(decryptAuthorizationString(
                "c1W2YO1vYQzu6czteEidrG0U4g5gT4h57vAlP7tdjcY=", "GAT"));
    }

    private static String decryptAuthorizationString(final String authString,
            final String password) {
        try {
            // --- check if AES-256 is available
            if (Cipher.getMaxAllowedKeyLength(AES_CBC_PKCS5PADDING) < KEY_SIZE) {
                throw new IllegalStateException("Unlimited crypto files not present in this JRE");
            }

            // --- create cipher
            final Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5PADDING);

            // --- create the key and initial vector bytes
            final byte[] passwordEncoded = password.getBytes(UTF_16LE);
            final byte[] keyData = Arrays.copyOf(passwordEncoded, KEY_SIZE
                    / Byte.SIZE);
            final byte[] ivBytes = Arrays.copyOf(keyData, cipher.getBlockSize());

            // --- init cipher
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keyData, "AES"),
                    new IvParameterSpec(ivBytes));

            // --- decode & decrypt authentication string
            final byte[] authBytes = Base64.decode(authString);
            final byte[] decryptedData = cipher.doFinal(authBytes);

            // WARNING: may still decrypt to wrong string if
            // authString or password are incorrect - 
            // BadPaddingException may *not* be thrown
            return new String(decryptedData, UTF_16LE);
        } catch (BadPaddingException | IllegalBlockSizeException e) {
            // failure to authenticate
            return null;
        } catch (final GeneralSecurityException e) {
            throw new IllegalStateException(
                    "Algorithms or unlimited crypto files not available", e);
        }
    }
}
  • 0
    Большое спасибо за ваш четкий комментарий и ваше решение, которое работает как шарм. Вы спасли меня огромную головную боль. Спасибо!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню