2015-09-15 2 views
2

Я зашифровать сообщение в .NET с RSACryptoServiceProvider с закрытым ключом. (PKCS # 1 v1.5).NET шифровать RSACryptoServiceProvider с 4096 закрытым ключом, как расшифровать его на Android

Когда я пытаюсь расшифровать в .NET с помощью следующего кода, который использует открытый ключ все работает отлично:

private static string Decrypt(string key, string content) 
{ 
    byte[] rgb = Convert.FromBase64String(content); 
    var cryptoServiceProvider = new RSACryptoServiceProvider(new CspParameters() 
    { 
      ProviderType = 1 
    }); 
    cryptoServiceProvider.ImportCspBlob(Convert.FromBase64String(key)); 
    return Convert.ToBase64String(cryptoServiceProvider.Decrypt(rgb, false)); 
} 

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

Метод в Android здесь:

public String Decrypt(String input) { 
    try { 
     KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 

     String modulusString = "mmGn1IXB+/NEm1ecLiUzgz7g2L6L5EE5DUcptppTNwZSqxeYKn0AuAccupL0iyX3LMPw6Dl9pjPXDjk93TQwYwyGgZaXOSRDQd/W2Y93g8erpGBRm/Olt7QN2GYhxP8Vn+cWUbNuikdD4yMfYX9NeD9UNt5WJGFf+jRkLk0zRK0A7ZIS+q0NvGJ/CgaRuoe3x4Mh1qYP9ZWNRw8rsDbZ6N2zyUa3Hk/WJkptRa6jrzc937r3QYF3eDTurVJZHwC7c3TJ474/8up3YNREnpK1p7hqwQ78fn35Tw4ZyTNxCevVJfYtc7pKHHiwfk36OxtOIesfKlMnHMs4vMWJm79ctixqAe3i9aFbbRj710dKAfZZ0FnwSnTpsoKO5g7N8mKY8nVpZej7tcLdTL44JqWEqnQkocRqgO/p3R8V/6To/OjQGf0r6ut9y/LnlM5qalnKJ1gFg1D7gCzZJ150TX4AO5kGSAFRyjkwGxnR0WLKf+BDZ8T/syOrFOrzg6b05OxiECwCvLWk0AaQiJkdu2uHbsFUj3J2BcwDYm/kZiD0Ri886xHqZMNExZshlIqiecqCskQhaMVC1+aCm+IFf16Qg/+eMYCd+3jm/deezT4rcMBOV/M+muownGYQ9WOdjEK53h9oVheahD3LqCW8MizABFimvXR3wAgkIUvhocVhSN0="; 
     String exponentString = "AQAB"; 

     byte[] modulusBytes = Base64.decode(modulusString.getBytes("UTF-8"), Base64.DEFAULT); 
     byte[] dBytes = Base64.decode(exponentString.getBytes("UTF-8"), Base64.DEFAULT); 

     BigInteger modulus = new BigInteger(1, modulusBytes); 
     BigInteger d = new BigInteger(1, dBytes); 

     RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, d); 
     PublicKey key = keyFactory.generatePublic(keySpec); 

     //at one point I read somewhere that .net reverses the byte array so that it needs to be reversed for java, but who knows any more 
     /*byte[] inputArrayReversed = Base64.decode(input.getBytes("UTF-8"), Base64.DEFAULT); 
     for (int i = 0; i < inputArrayReversed.length/2; i++) { 
      byte temp = inputArrayReversed[i]; 
      inputArrayReversed[i] = inputArrayReversed[inputArrayReversed.length - 1]; 
      inputArrayReversed[inputArrayReversed.length - 1] = temp; 
     }*/ 

     byte[] decryptedText = null; 
     Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, key); 
     decryptedText = cipher.doFinal(Base64.decode(input.getBytes("UTF-8"), Base64.DEFAULT)); 
     return Base64.encodeToString(decryptedText, Base64.NO_WRAP); 
     //return new String(decryptedText, "UTF-8"); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return ""; 
} 

На самом деле я пытался также с различными алгоритмами, указанных в Cypher классе, также пробовал много других комбинаций, пытались использовать SpongyCastle вместо встроенного в поставщиков Android RSA, но ничего не получалось , Если кто-нибудь подскажет мне в правильном направлении, я буду абсолютно благодарен.

Первая подсказка - это то, что расшифрованная строка из .NET насчитывает около 25 символов, и когда я получаю Android для возврата расшифрованной строки без исключений, она обычно намного длиннее, примерно 500 байт.

Второй намек удален

Третий намек Я также попытался spongycastle, но это не помогло, что многое

В любом случае, спасибо заранее за любую помощь !!!

UPDATE 1

Второй намек удаляется, потому что был не прав, игнорировать его. Теперь у меня есть один вопрос, если следующее может доказать, что открытый ключ загружен правильно, просто чтобы исключить эту проблему.

Модуль BigInteger и показатель в верхнем коде Android и следующие BigIntegers в .NET показывают равные целочисленные значения.

var parameters = csp.ExportParameters(false); 
    var modulusInteger = new BigInteger(parameters.Modulus.Reverse().Concat(new byte[] { 0 }).ToArray()); 
    var exponentInteger = new BigInteger(parameters.Exponent.Reverse().Concat(new byte[] { 0 }).ToArray()); 

UPDATE 2

This и This SO ответы предоставить некоторые интересные подсказки

+0

Не могли бы вы попробовать и обратный модуль вместо шифротекста ? Выход RSA определяется как строка октета, а модуль - число. Строки октетов не имеют маленьких/больших проблем, но номера действительно. То же самое относится и к публичному экспоненту, но поскольку это значение '010001', это двоичный палиндром. Тем не менее, если это * не * '010001', а небольшое случайное число, ваш алгоритм все равно может потерпеть неудачу. Если это решает проблему, было бы неплохо прокомментировать, поэтому я могу преобразовать этот комментарий в ответ. –

+0

не будет ли это предположение противоречить моему второму намеку, но, конечно, к концу дня я попробую это и дам вам знать. – Brcinho

+0

Это отладка с расстояния. В моем - теперь значительном опыте - он всегда будет доходить до кодера, ударяя головой ладонью, а мы просто обеспечим достаточную энтропию для того, чтобы кодер продолжал отлаживать :) –

ответ

0

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

Моя ошибка заключалась в том, что я предположил, что на клиенте у нас есть открытый ключ и фактически из личного ключа все время я пытался загрузить открытый ключ, а затем расшифровать.

Если бы я знал ИПК в глубину и общались немного лучше с моим коллегой, я мог бы заметил несколько вещей:

  • Расшифровать может быть сделано только с закрытым ключом, в то время как один другой стороны проверить, может быть сделанный с открытым ключом, поэтому, когда я увидел, что Decrypt используется на клиенте в .NET, я должен был предположить, что на клиенте у нас есть закрытый ключ (который является недостатком безопасности в конце так, как мы хотим использовать PKI)

Немного вещей, которые я уже знал или узнал и хочу поделиться с другими:

  1. Закрытый ключ следует хранить в тайне, хотите ли вы иметь его на сервере или предпочтительно только на одном клиенте, поскольку открытый ключ может быть легко догадаться из закрытого ключа, а затем кто-то может легко повторить весь свой процесс шифрования легко и прорвать вашу безопасность
  2. PKI работает для двух сценариев: Первого сценария, когда вы хотите Encrypt что-то, и что только конкретный человек/компьютер может Расшифровать это. В первом сценарии, как вы видите, многие заинтересованные стороны могут иметь чей-то открытый ключ и отправлять ему сообщения, и только он может прочитать их с его приватным ключом. Второй сценарий - это когда вы хотите быть уверенным, что сообщение, которое пришло к вам, не было изменено и отправлено конкретным человеком/компьютером. В этом случае вы Вход данные сличного ключа и Проверьте его на другом конце собщественного ключа. Единственный процесс, который подходит для нас, - это знак < -> Проверить, потому что мы отправляем в него обычную текстовую лицензию с подписью, и, таким образом, на клиенте мы хотим быть уверены, что никто не подделывает лицензию на простой текст и что он пришел от нас.
  3. В вашем коде, если Функция дешифрования или проверки исключает исключения из-за загрузки неправильного ключа или неправильной загрузки правильного ключа, а в других 50% это потому, что вы используете неправильные алгоритм или потому, что параметры алгоритма неправильно установлены или потому, что реализация алгоритма между платформами несовместимы (последний очень редко)

код .NET сервер

public string Sign(string privateKey, string data) 
    { 
     _rsaProvider.ImportCspBlob(Convert.FromBase64String(privateKey)); 

     //// Write the message to a byte array using UTF8 as the encoding. 
     var encoder = new UTF8Encoding(); 
     byte[] byteData = encoder.GetBytes(data); 

     //// Sign the data, using SHA512 as the hashing algorithm 
     byte[] encryptedBytes = _rsaProvider.SignData(byteData, new SHA1CryptoServiceProvider()); 

     return Convert.ToBase64String(encryptedBytes); 
    } 

код .NET клиент (Win Mobile)

private bool Verify(string key, string signature, string data) 
    { 
     CspParameters cspParams = new CspParameters { ProviderType = 1 }; 
     RSACryptoServiceProvider rsaProvider = new RSACryptoServiceProvider(cspParams); 
     rsaProvider.ImportCspBlob(Convert.FromBase64String(key)); 

     byte[] signatureBytes = Convert.FromBase64String(signature); 
     var encoder = new UTF8Encoding(); 
     byte[] dataBytes = encoder.GetBytes(data); 

     return rsaProvider.VerifyData(dataBytes, new SHA1CryptoServiceProvider(), signatureBytes); 
    } 

код Android-клиент:

public boolean Verify(RSAPublicKey key, String signature, String data) 
{ 
    try 
    { 
     Signature sign = Signature.getInstance("SHA1withRSA"); 
     sign.initVerify(key); 
     sign.update(data.getBytes("UTF-8")); 
     return sign.verify(Base64.decode(signature.getBytes("UTF-8"), Base64.NO_WRAP)); 
    } 
    catch (Exception e) 
    { 
     e.printStackTrace(); 
    } 
    return false; 
} 

в.NET открытый ключ экспортируется в формате XML с помощью следующего кода:

public string ExportPublicToXML(string publicKey) 
{ 
    RSACryptoServiceProvider csp = new RSACryptoServiceProvider(new CspParameters() 
    { 
     ProviderType = 1 
    }); 
    csp.ImportCspBlob(Convert.FromBase64String(publicKey)); 

    return csp.ToXmlString(false); 
} 

, а затем модуль и показатель используется в Android, чтобы загрузить открытый ключ:

private RSAPublicKey GetPublicKey(String keyXmlString) throws InvalidKeySpecException, UnsupportedEncodingException, NoSuchAlgorithmException 
{ 
    KeyFactory keyFactory = KeyFactory.getInstance("RSA"); 

    String modulusString = keyXmlString.substring(keyXmlString.indexOf("<Modulus>"), keyXmlString.indexOf("</Modulus>")).replace("<Modulus>", ""); 
    String exponentString = keyXmlString.substring(keyXmlString.indexOf("<Exponent>"), keyXmlString.indexOf("</Exponent>")).replace("<Exponent>", ""); 

    byte[] modulusBytes = Base64.decode(modulusString.getBytes("UTF-8"), Base64.DEFAULT); 
    byte[] dBytes = Base64.decode(exponentString.getBytes("UTF-8"), Base64.DEFAULT); 

    BigInteger modulus = new BigInteger(1, modulusBytes); 
    BigInteger d = new BigInteger(1, dBytes); 

    RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, d); 

    return (RSAPublicKey) keyFactory.generatePublic(keySpec); 
}