2014-10-22 2 views
1

Мне нужно зашифровать пароль на веб-сайте с помощью открытого ключа. Открытый ключ доставляется через веб-службу Java с информацией: Ключ - это ключ RSA для использования с алгоритмом RSA/ECB/PKCS1Padding. Открытый ключ поставляется в виде JSON в виде:Java RSA/ECB/PKCS1Padding encryption with .NET

{ 
    "kid":"PWD", 
    "kty":"RSA", 
    "use":"enc", 
    "n":"MTA0OTgzNjg0OTMxMzE2NjkwNTU4Mjg3NDIwMDg1NTY0ODEyMjg1MDk2NTcwNzU5NDIzNzM0O 
    DA3OTA2MzA0MDczNTU0NDQ2Njg3ODY2ODk2NTk0NjYzNTAxMzg0NzE1OTExMjA0MjU1MzMzOTIzMjA 
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
    zcwMjA3MzQxOTcwNzc4NDAwNzM3MTY2NDMyNzIwNjMwMDQwOTMwOTQ0MTA2OTE1NDEzMDAwNTMyMTE 
    5ODM0MTA2MjAzMDIyODEwMjYyMDM3MDQ0NzkxNzIzNTU1MjQyNjYxMzE2ODc4OTc5NzY1OTgzMjg4M 
    zQ0NDc3OTYwNTg3MzE2NTUwMDgx", 
    "e":"NjU1Mzc" 
} 

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

encryptedPassword = EncrypIt("Password01", "MTA1MzQxNzIwODA3NjUwNzg5ND 
Y4ODU2Mjc0NDA3ODIwMjQ1ODQ5NDE1MDk1MDIzMTM3MjM0NzAwNzYzNjc2MTgwNjg3ODMxMjA3 
NTY0NTcxMjg2MzM4NjQ1NzEwMDcyMjY2MTQyNDIzMTczMjkwMDk0MTc0MTA5MTc5MzI0NjYwMjQ4NzI3NzM0MTQ5NDY0MjUwODkwO 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
c2OTAzNjc3NzQzODM3NzM0MjE2ODM0NjY4MjM4MTQ0OTA3MDQ3MTk1Njc1NzU3OTE2NjEyNzkzMzM2MzI3MDUyNjg0NDI5NDExNjI 
2MzA0MzM5", "NjU1Mzc"); 

    public static string EncrypIt(string password, string publicKey, string exponent) 
    { 
     UnicodeEncoding ByteConverter = new UnicodeEncoding(); 
     byte[] publicKeyByte = ByteConverter.GetBytes(publicKey); 
     byte[] passwordByte = ByteConverter.GetBytes(password); 

     RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 
     RSAParameters RSAKeyInfo = new RSAParameters(); 
     RSAKeyInfo = RSA.ExportParameters(false); //Export only public key 
     //Set RSAKeyInfo to the public key values. 
     RSAKeyInfo.Modulus = publicKeyByte; 
     //RSAKeyInfo.Exponent = exponentBytes; //I tried to set exponent but I have an error 
     RSA.ImportParameters(RSAKeyInfo); 
     byte[] encryptedPassword = RSA.Encrypt(passwordByte, false); 
     return Convert.ToBase64String(encryptedPassword); 
    } 

(открытый ключ отличается между JSON и мой код, но не обращайте на это внимания, я просто скопированную информацию из различных источников)

  • зашифрованный пароль Я получил слишком много: зашифрованный пароль должен быть 172 символа (я знаю, потому что у меня есть небольшая программа Java, которая позволяет мне правильно шифровать пароли), но я получаю 1100 символов.
  • Я не использую экспонента: Должен ли я?
  • Могу ли я использовать JSON для непосредственной настройки RSACryptoServiceProvider правильно?

Ответ от owlstead помог мне получить зашифрованный пароль с правильным размером строки, но сервис с помощью зашифрованной строки отклонить его с сообщением: javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes

Я otained код программы Java который делает правильное шифрование (см. ниже). Мне нужно добиться того же шифрования с помощью .NET.

public class EncryptionServiceImpl 
{ 

    private static final Charset UTF8 = Charset.forName("UTF-8"); 

    @Resource(name = "briqueAuthentificationClient") 
    private BriqueAuthentificationClientImpl briqueAuthentificationClient; 

    protected static final String ALGORITHM_RSA = "RSA"; 

    protected static final String TRANSFORMATION_RSA_ECB_PKCS1PADDING = "RSA/ECB/PKCS1Padding"; 

    private static final Logger LOG = LoggerFactory.getLogger(EncryptionServiceImpl.class); 

    public EncryptionServiceImpl() { 
     LOG.info("constructeur EncryptionServiceImpl"); 
    } 

    /** 
    * @param briqueAuthentificationClient the briqueAuthentificationClient to set 
    */ 
    public void setBriqueAuthentificationClient(final BriqueAuthentificationClientImpl briqueAuthentificationClient) { 
     this.briqueAuthentificationClient = briqueAuthentificationClient; 
    } 

    public String encrypt(final String input) throws GeneralSecurityException { 

     if (StringUtils.isNotBlank(input)) { 
      final CertificateDto certificate = this.briqueAuthentificationClient.getCurrentCertificate(); 

      if (certificate != null) { 
       return new String(this.encryptAndEncode(input.getBytes(), certificate), EncryptionServiceImpl.UTF8); 
      } else { 
       throw new RuntimeException("Certificate is null"); 
      } 
     } 
     return null; 
    } 

    protected byte[] encryptAndEncode(final byte[] input, final CertificateDto currentCertificate) 
      throws GeneralSecurityException { 

     // Création de la clé publique 
     final PublicKey publicKey = this.buildPublicKey(currentCertificate); 

     // Chiffre 
     final byte[] inputEncrypted = this.encrypte(input, publicKey); 

     // Encode 
     return this.encodeBase64Url(inputEncrypted); 
    } 

    protected PublicKey buildPublicKey(final CertificateDto currentCertificate) throws GeneralSecurityException { 

     if ("RSA".equals(currentCertificate.getKeyType())) { 
      return this.buildRSAPublicKey(currentCertificate); 
     } 
     LOG.error(String.format("Tentative de création d'une clé publique avec un algorithme non connu [%s]", 
       currentCertificate.getKeyType())); 
     return null; 
    } 

    protected PublicKey buildRSAPublicKey(final CertificateDto currentCertificate) throws GeneralSecurityException { 

     final BigInteger modulus = new BigInteger(new String(Base64.decodeBase64(currentCertificate.getModulus()), 
       EncryptionServiceImpl.UTF8)); 
     final BigInteger publicExponent = new BigInteger(new String(Base64.decodeBase64(currentCertificate 
       .getPublicExponent()), EncryptionServiceImpl.UTF8)); 

     try { 
      return KeyFactory.getInstance(ALGORITHM_RSA).generatePublic(new RSAPublicKeySpec(modulus, publicExponent)); 
     } catch (InvalidKeySpecException e) { 
      throw e; 
     } catch (NoSuchAlgorithmException e) { 
      throw e; 
     } 
    } 

    protected byte[] encrypte(final byte[] input, final RSAPublicKeySpec rsaPublicKeySpec) 
      throws GeneralSecurityException { 

     PublicKey publicKey; 
     try { 
      publicKey = KeyFactory.getInstance(ALGORITHM_RSA).generatePublic(
        new RSAPublicKeySpec(rsaPublicKeySpec.getModulus(), rsaPublicKeySpec.getPublicExponent())); 
     } catch (InvalidKeySpecException e) { 
      throw e; 
     } catch (NoSuchAlgorithmException e) { 
      throw e; 
     } 
     return this.encrypte(input, publicKey); 
    } 

    protected byte[] encrypte(final byte[] input, final PublicKey publicKey) throws GeneralSecurityException { 

     try { 
      final Cipher cipher = Cipher.getInstance(TRANSFORMATION_RSA_ECB_PKCS1PADDING); 
      cipher.init(Cipher.ENCRYPT_MODE, publicKey); 
      return cipher.doFinal(input); 
     } catch (NoSuchAlgorithmException e) { 
      throw e; 
     } catch (NoSuchPaddingException e) { 
      throw e; 
     } catch (IllegalBlockSizeException e) { 
      throw e; 
     } catch (BadPaddingException e) { 
      throw e; 
     } 

    } 

    protected byte[] decrypte(final byte[] input, final RSAPrivateKeySpec rsaPrivateKeySpec) 
      throws GeneralSecurityException { 

     final BigInteger modulus = rsaPrivateKeySpec.getModulus(); 
     final BigInteger privateExponent = rsaPrivateKeySpec.getPrivateExponent(); 

     try { 
      final PrivateKey privateKey = KeyFactory.getInstance(ALGORITHM_RSA).generatePrivate(
        new RSAPrivateKeySpec(modulus, privateExponent)); 

      final Cipher cipher = Cipher.getInstance(TRANSFORMATION_RSA_ECB_PKCS1PADDING); 
      cipher.init(Cipher.DECRYPT_MODE, privateKey); 
      return cipher.doFinal(input); 

     } catch (NoSuchAlgorithmException e) { 
      throw e; 
     } catch (NoSuchPaddingException e) { 
      throw e; 
     } catch (IllegalBlockSizeException e) { 
      throw e; 
     } catch (BadPaddingException e) { 
      throw e; 
     } catch (InvalidKeySpecException e) { 
      throw e; 
     } catch (InvalidKeyException e) { 
      throw e; 
     } 

    } 

    protected byte[] encodeBase64Url(final byte[] input) { 
     return Base64.encodeBase64(input, false); 
    } 

    protected byte[] decodeBase64Url(final byte[] input) { 
     return Base64.decodeBase64(input); 
    } 

    /** 
    * Method to connect to an url 
    * 
    * @param httpclient the http connection 
    * @return the response GetMethod 
    * @throws OAuthException in cas of connection error 
    */ 
    private GetMethod connect(final HttpClient httpclient, final String url) { 

     final GetMethod httpget = new GetMethod(url); 
     try { 

      httpclient.executeMethod(httpget); 

     } catch (final UnknownHostException e) { 
      throw new RuntimeException("Connection ERROR - Host could not be determined.", e); 
     } catch (final IOException e) { 
      throw new RuntimeException("Connection ERROR - Input/Output error.", e); 
     } 
     return httpget; 
    } 

} 

шаги я достиг с помощью owlstead в ответ ниже. Когда я использую эту программу Java для кодирования Строки Password01 я получаю строку, как:

sy5/XElHvuYA4Rbq8OBydLymT82R+z77jy6MU2sNal7VenzPEbARgy7p3zWgYgG13Cypk+zbnnB5L37fVUhgOgCqCyLtikzxJBNmPyzUK9+beiHJKyONZwifDzQ44hXTeXcZ0bmF9b5dLFy9QS/N5m28vIyBSGY8K2B7EB2FF38= 

Этот зашифрованный пароль может быть расшифрован на Java стороне

Когда я использую код .NET строка является например:

ACZXYj/KudyxKBB510SxSouKaVwssmEUM6Jpreh8jTtrIH9eGb18GIdkBU7rXzMuLYbAhyREbFLbR87zW/2DNa4tN97FOlqr6k1JppJ/SSS/9fGdMvSOAQbWjsxksDH7fRu9dCvK0m0exFtGfXG7Yua9bB1m0dTNiwCZUZM0LnEM 

Этот зашифрованный пароль не был расшифрован на стороне Java. Ошибка с сообщением об ошибке:

javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes 
+0

Вместо того, чтобы * кодировать * эти строки, используя UTF-16, вы почти наверняка должны быть * расшифровать * его с base64. И да, вам нужно правильно установить экспонента. –

+0

@GregS Нужен сон, это база 64, но без заполнения символов. –

+0

@owlstead: Я считал, что в кодировках всего 25 различных символов. Это f__ked. –

ответ

3

Сначала необходимо выполнить Base 64 декодирования с использованием Convert.FromBase64String из n и e, преобразовать результат из кодировки ASCII в строку, а затем разобрать результат, используя BigInteger.parse. Затем вы можете преобразовать с помощью toByteArray в байты, но будьте осторожны, что BigInteger мало endian, and RSAParameters expects big endian, поэтому вам нужно отменить байты.

Зашифрованный текст в формате .NET RSA (который, вероятно, не следует отменять), предшествует байту с байтом 00h, что делает его недопустимым зашифрованным текстом. RSA ciphertext должен быть иметь точную длину в байтах ключа.

+0

Он близок к формату [JOSE] (https://tools.ietf.org/html/draft-ietf-jose-json-web-key-35), но это просто прямой base64, как и следовало ожидать. –

+0

@GregS Да, похоже, что кто-то безуспешно пытался это скопировать. Даже конечные символы '=' отсутствуют, как в спецификации JOSE (все в порядке, никогда не понимал, почему в этом случае требуются дополнительные символы). –

+0

Благодарим вас за ответ. Я попытался применить ваши советы, но я все еще застрял. Подробности приведены в ответе ниже. – GuyOlivier

1

Спасибо сэров за ответы и комментарии, это поможет мне окончательно решить мою проблему:

Для показателя, я получил сообщение об ошибке: Invalid длину базовых 64-массива символов или строк Это происходит потому, что значение базы 64 должно быть кратно 4. Если это не так, мы должны добавить знак равенства (=), чтобы достигнуть кратного 4. Таким образом, после изменения строки экспоненты от "NjU1Mzc" до "NjU1Mzc=" это значение может быть расшифровано.

Затем я применил решение, предоставленное owlstead. Вот окончательный код, который работает отлично:

//Decode from base 64 
byte[] publicKeyByte = Convert.FromBase64String(rsaPublicKey.modulo); 
byte[] exponentByte = Convert.FromBase64String(rsaPublicKey.exponent); 

//Convert to ASCII string 
UTF8Encoding ByteConverter = new UTF8Encoding(); 
string publicKeyString = System.Text.Encoding.Default.GetString(publicKeyByte); 
string exponentString = System.Text.Encoding.Default.GetString(exponentByte); 

//Convert to BigInteger 
BigInteger publicKeyBI = BigInteger.Parse(publicKeyString); 
BigInteger exponentBI = BigInteger.Parse(exponentString); 

//Convert back to byte array 
byte[] publicKeyByte2 = publicKeyBI.ToByteArray(); 
byte[] exponentByte2 = exponentBI.ToByteArray(); 

//We must remove the 129th sign byte which is added when converting to BigInteger 
if (publicKeyByte2.Length == 129) Array.Resize(ref publicKeyByte2, 128); 

//Create crypto service 
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 
RSAParameters RSAKeyInfo = new RSAParameters(); 

//Assign RSA key modulus/exponent reversing from little endian to big endian 
RSAKeyInfo.Modulus = publicKeyByte2.Reverse().ToArray(); 
RSAKeyInfo.Exponent = exponentByte2.Reverse().ToArray(); 
RSA.ImportParameters(RSAKeyInfo); 

//Convert password string to byte array 
byte[] passwordByte = ByteConverter.GetBytes(clearPassword); 

//Encrypt the password and encode 64 
encryptedPassword = Convert.ToBase64String(RSA.Encrypt(passwordByte, false)); 

недостающее точка из owlstead является то, что: метод возвращает массив байт с дополнительным элементом, значение которого равно нуль на неоднозначность положительных значений смотрите в документацию Microsoft для получения более подробной информации о этот момент: BigInteger.ToByteArray Method

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

+1

Пожалуйста, отредактируйте свой вопрос, чтобы включить обновление, вместо того, чтобы отправлять его в качестве ответа. – Rotem

+0

Хорошо, я забыл сделать один шаг. Вам нужно decpde от ascii получить результат из base64 ... и затем разобрать на biginteger, снова вернуться к байтам, а затем отменить. –

0

Я попытался это и шифрует правильно

var input = "String to Encode."; 
    var mod = "MTA1MzQxNzIwODA3NjUwNzg5NDY4ODU2Mjc0NDA3ODIwMjQ1ODQ5NDE1MDk1MDIzMTM3MjM0NzAwNzYzNjc2MTgwNjg3ODMxMjA3NTY0NTcxMjg2MzM4NjQ1NzEwMDcyMjY2MTQyNDIzMTczMjkwMDk0MTc0MTA5MTc5MzI0NjYwMjQ4NzI3NzM0MTQ5NDY0MjUwODkwOXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXc2OTAzNjc3NzQzODM3NzM0MjE2ODM0NjY4MjM4MTQ0OTA3MDQ3MTk1Njc1NzU3OTE2NjEyNzkzMzM2MzI3MDUyNjg0NDI5NDExNjI2MzA0MzM5=="; 
    var exp = "NjU1Mzc="; 
    var intValue = int.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(exp))); 

    var rsaParam = new RSAParameters(); 
    rsaParam.Modulus = Convert.FromBase64String(mod); 
    rsaParam.Exponent = BitConverter.GetBytes(intValue); 
    using (var rsa = new RSACryptoServiceProvider()) 
    { 
     rsa.ImportParameters(rsaParam); 
     Console.WriteLine(Convert.ToBase64String(rsa.Encrypt(Encoding.UTF8.GetBytes(input), false))); 
    } 
    Console.ReadLine(); 

Я думаю, что вопрос является показателем странно. Exp = 65537;

+0

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