2015-02-07 2 views
0

Я создаю генератор ключей лицензий, который RSA подписывает данные лицензионного ключа с кодировкой base64 и добавляет подпись к данным лицензии. В конце подписи пользовательской стороны проверяется с использованием открытого ключа, который связан с приложением. Мы приложения, запущенные на .net, php, java и других платформах. Модель License должна быть одинаковой для всех. Теперь к этой проблеме: я создал 2048bit общедоступный/закрытый ключ в C#, используя встроенный поставщик криптографии, и экспортировал ключи в формате XML, чтобы получить компоненты BigInteger прозрачными в кодировке b64. Генератор ключей License должен работать в php и I ' м с помощью phpseclib импортировать ключи XML и подписать код data.Php:Несогласованная подпись RSA на разных платформах

function sign($data) 
{ 
    $RSA_PRIVATE_KEY = "<RSAKeyValue> 
<Modulus>oPmCaLewqKTkRPpKLHz+9hEbrUbVdKNkIxzm4h5/wmuId6PSx6ntV2T44/NSzfx56LffyXvzx27GYEKk4zffpPYKdRIGReZVqfl2U5K3jcuv0h4657ge0nZ5W9vrYMDSFjgtZOSw8t2opWC8IZVUI3X9W8mp1Z3xqtwREpLC/kV0YE1Kmem8k8B4Tv1+qj5C6oPJjhy+T3JipYOQIyVRUoCdo3EHpDQ6K7twKD+Si/rASHyvoCJZTPvWI1w9y9FD8CxstkY7+ap8J9DMczyiFsE4kJtziBKZFph4ZRqz4kSA0TXj0lXhGEAcn1IDjhV5W37PdsWOkUTxBH7Bm7mpuw==</Modulus> 
<Exponent>AQAB</Exponent> 
<P>xU8K0Z1Mn0cLOHrwMIujXvtJbXpmjFfyOkm+WkeNV2hjDmdgMibJJvhqPEYPSabEHF4us8H4IeEqeInGK04LrYmbe3IVgmuGdpacR01WISbqXiarsKbcHkR65mfotQmxay/n3Swi8hN9yTanSJyhoHFBiJ0qYM0VNdamu7LDeGk=</P> 
<Q>0Nukxy2o/o7z6NeR9g+dUcOQzXfWS2Bu4vAXyHGpJArGcIzl4Z2jNuUztGj5sO5b+9SQs4tgijsjbsEQUNN7R1+PB7Zwc50PvGgEvGaIJkPphGoJMNCw4Q2I8tNPb86cEd4WLJ8E6ad93vX0tyZAnn3LrjPg3Bvvxtq157jZLIM=</Q> 
<DP>tGRV0dtsyFrdyV+s5dVlIlvAgFVeGIX3so7leAjfEsEff3XIH1ISqoyIJF8xbvcHaaA6NqLqx57jg50DD2iliJ29B5oATGMeZqHAc/gi/OBlensEkdecfBfD/Y+W1J3uFb+Qz0ehE436fNJ5EwwRQW0Kq2p16lbWQ4jim80OpbE=</DP> 
<DQ>vML2bumuhbrvaK6EBa5hEce9dGXtcJyMO2ChLhDDvIZciNZe4YUWQQPvsgr6OFWFHtojmZHLQ8NlJ7EnrNUl4wDThTX29haqZS5hsWC9hk/0mi83dT33zr7r2gLvFW7XETL2OYfS6dXt5ffHH0xcNKIe1qeef3BkSgXbR72B0j8=</DQ> 
<InverseQ>TffUf2HXQOmVEUo6rinASIOJOfwfucd+MjwDaK61bISFY5J2n3MYRX4wEiFxXlcYgXnR9CsbwRVhUDIDzLuB+ykAG62LkbqCKUYjzyN3RDbc/MSj/sXSw60pTc4DesVhQlumd0zGUOxsFkjOlUowxwIDENiSVFew1IfAOZiakpk=</InverseQ> 
<D>AKHPgpxrXn4nQfi+9HsZKoYurE4sOw+ug6TIE03jWolpjm606SvK+XOKtqUXR3pyUBjzZqsh7eqKxN3+H8DxvogTdRo5BBU/c4dokN4b8maWWNDdkliwEPYoy9Tf5mVbbdLn+rlwfcOjtzfbWpZnhNbLGTfVfuKRNwaI2qB7kNwGB1fd1t4xMLWNozgoxFuiiKFbJKmLEj9zHP/KqjEnOH/zEuUBnXiqGMnCMmD7KnD5WqcqSDOahn0TBFlMxV+9Ul2447bu5LTGWhm3RBPEGgtjMboKC3PlgqwYDpC2gbzX5ZsBNCiuGgumxBHgOfAIOVzI01MZTFEpTcTt3BUrwQ==</D> 
</RSAKeyValue>"; 

    $RSA = new Crypt_RSA(); 
    $RSA->loadKey($RSA_PRIVATE_KEY, CRYPT_RSA_PRIVATE_FORMAT_XML); 
    $RSA->setHash(CRYPT_SHA512); 
    $RSA->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); 
    return $RSA->sign($data); 
} 

C# код:

private String signData(String data) { 
      byte[] private_key_modulus_array = Convert.FromBase64String(B64_MODULUS); 
      byte[] private_key_exponent_array = Convert.FromBase64String(B64_EXPONENT); 
      byte[] private_key_p_array = Convert.FromBase64String(B64_P); 
      byte[] private_key_q_array = Convert.FromBase64String(B64_Q); 
      byte[] private_key_dp_array = Convert.FromBase64String(B64_DP); 
      byte[] private_key_dq_array = Convert.FromBase64String(B64_DQ); 
      byte[] private_key_inverseQ_array = Convert.FromBase64String(B64_INVERSE_Q); 
      byte[] private_key_d_array = Convert.FromBase64String(B64_D); 

      //Create a new instance of RSACryptoServiceProvider. 
      RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(); 
      RSAParameters RSAKeyInfo = new RSAParameters(); 

      //Set RSAKeyInfo to the private key values. 
      RSAKeyInfo.Modulus = private_key_modulus_array; 
      RSAKeyInfo.P = private_key_p_array; 
      RSAKeyInfo.Q = private_key_q_array; 
      RSAKeyInfo.DP = private_key_dp_array; 
      RSAKeyInfo.DQ = private_key_dq_array; 
      RSAKeyInfo.D = private_key_d_array; 
      RSAKeyInfo.InverseQ = private_key_inverseQ_array; 
      RSAKeyInfo.Exponent = private_key_exponent_array; 
      RSA.ImportParameters(RSAKeyInfo); 

      return Convert.ToBase64String(RSA.SignData(Encoding.ASCII.GetBytes(data), new SHA512CryptoServiceProvider())); 
     } 

генерируется подпись не совпадает, когда генерируется с C# и PHP. Я думаю, что, возможно, phpseclib не разобрал секретный ключ правильно?

На стороне C# подпись такая же, когда она создана со встроенной криптографией и bouncycastle lib.

Что мне делать? Нужен ли мне какой-либо другой ключевой формат для обеспечения согласованности?

Thanx для справки.

+0

Crypt_RSA как в http://pear.php.net/package/Crypt_RSA? И «то же самое» означает «может быть проверено с помощью соответствующих методов подписи»? – VolkerK

+0

Его чистая php, криптографическая библиотека: http://phpseclib.sourceforge.net yes C# подпись не проверяет в php, пробовала это – Brlja

+1

+1, похоже, что вы сделали все правильно в этом фрагменте PHP, насколько я могу сказать. Я предполагаю, что код C# форматирует данные, которые подписываются по-разному. Не могли бы вы разместить соответствующий код C#, который вы хотите согласовать? –

ответ

-2

Я зафиксировал проблему с помощью OpenSSL на стороне PHP и BouncyCastle на C# стороне.
Ключ - формат пем.

Вот как тест 512bit ключ выглядит:

-----BEGIN RSA PRIVATE KEY----- 
MIIBOgIBAAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6zxqlVzz0wy2j4kQVUC4Z 
RZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQJAL151ZeMKHEU2c1qdRKS9 
sTxCcc2pVwoAGVzRccNX16tfmCf8FjxuM3WmLdsPxYoHrwb1LFNxiNk1MXrxjH3R 
6QIhAPB7edmcjH4bhMaJBztcbNE1VRCEi/bisAwiPPMq9/2nAiEA3lyc5+f6DEIJ 
h1y6BWkdVULDSM+jpi1XiV/DevxuijMCIQCAEPGqHsF+4v7Jj+3HAgh9PU6otj2n 
Y79nJtCYmvhoHwIgNDePaS4inApN7omp7WdXyhPZhBmulnGDYvEoGJN66d0CIHra 
I2SvDkQ5CmrzkW5qPaE2oO7BSqAhRZxiYpZFb5CI 
-----END RSA PRIVATE KEY----- 
-----BEGIN PUBLIC KEY----- 
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6 
zxqlVzz0wy2j4kQVUC4ZRZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQ== 
-----END PUBLIC KEY----- 

Ключи генерируются с помощью утилиты OpenSSL: www.openssl.org
Вы можете получить частную пару/открытый ключ с помощью:

openssl genrsa 512 >private_key.txt 
openssl rsa -pubout <private_key.txt >public_key.txt 

512 является ключевым bitsize.

подпись PHP Data:

function signData($message) 
    { 
     $private_key = <<<EOD 
-----BEGIN RSA PRIVATE KEY----- 
MIIBOgIBAAJBANDiE2+Xi/WnO+s120NiiJhNyIButVu6zxqlVzz0wy2j4kQVUC4Z 
RZD80IY+4wIiX2YxKBZKGnd2TtPkcJ/ljkUCAwEAAQJAL151ZeMKHEU2c1qdRKS9 
sTxCcc2pVwoAGVzRccNX16tfmCf8FjxuM3WmLdsPxYoHrwb1LFNxiNk1MXrxjH3R 
6QIhAPB7edmcjH4bhMaJBztcbNE1VRCEi/bisAwiPPMq9/2nAiEA3lyc5+f6DEIJ 
h1y6BWkdVULDSM+jpi1XiV/DevxuijMCIQCAEPGqHsF+4v7Jj+3HAgh9PU6otj2n 
Y79nJtCYmvhoHwIgNDePaS4inApN7omp7WdXyhPZhBmulnGDYvEoGJN66d0CIHra 
I2SvDkQ5CmrzkW5qPaE2oO7BSqAhRZxiYpZFb5CI 
-----END RSA PRIVATE KEY----- 
EOD; 

     $binary_signature = ""; 
     openssl_sign($message, $binary_signature, $private_key, OPENSSL_ALGO_SHA1); 
     return base64_encode($binary_signature); 
    } 

C# подпись проверки достоверности, используя только открытый ключ и BouncyCastle:

public bool verifySignature(byte[] signatureBytes, String message) 
{ 
    AsymmetricKeyParameter publicKey = (AsymmetricKeyParameter)new PemReader(newStringReader(PUBLIC_KEY)).ReadObject(); 
    ISigner sig = SignerUtilities.GetSigner("SHA1withRSA"); 
    sig.Init(false, publicKey); 

    byte[] messageBytes = Encoding.ASCII.GetBytes(message); 
    sig.BlockUpdate(messageBytes, 0, messageBytes.Length); 
    return sig.VerifySignature(signatureBytes); 
} 

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

-1

Разница вызвана различным форматированием данных до применения примитива экспоненциального RSA. Метод RSAryptoServiceProvider SignData использует форматирование, иногда называемое «сырой» сигнатурой. Вместо этого модуль phpseclib rsa использует форматирование, как указано в PKCS#1 version 1.5.

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

static byte[] PKCS1Signature(string content, RSACryptoServiceProvider rsaKey) { 
     byte[] contentBytes = Encoding.UTF8.GetBytes (content); 
     RSAPKCS1SignatureFormatter signer = new RSAPKCS1SignatureFormatter (rsaKey); 
     signer.SetHashAlgorithm ("SHA512"); 
     SHA512 hasher = SHA512.Create(); 
     byte[] hash = hasher.ComputeHash (contentBytes); 
     byte[] SignedHash = signer.CreateSignature (hash); 
     return SignedHash; 
    } 
+2

[Не использовать PKCS # 1 версию 1.5] (http://stackoverflow.com/a/34088308/2224584). Это серьезно небезопасно. –

+0

@ScottArciszewski: Я не использую его. –

+2

Ваш ответ использует его. –

4

Убедитесь, что вы используете тот же режим заполнения при подписании, как и при проверке подписи, как James K Polk demonstrates.

Однако, если вы хотите быть в безопасности, use RSASSA-PSS with e=65537 and MFG1+SHA256 instead of PKCS1v1.5 padding (phpseclib поддерживает оба).

Также рассмотрите возможность отмены RSA в целом в пользу Ed25519, который доступен в PHP 7.2 или PECL (PHP). С другой стороны, получите libsodium.net (C#).

На PHP конце:

$message = 'Whatever you want'; 
$signature = sodium_crypto_sign_detached($message, $secretKey); 
// ... 
if (sodium_crypto_sign_verify_detached($signature, $message, $publicKey)) { 
    // Valid signature for message and public key 
} 

В конце C#:

var message = "whatever you want"; 
// privateKey is a byte[] of length 64 
// publicKey is a byte[] of length 32 

// get a signature for the message 
var signature = PublicKeyAuth.SignDetached(message, privateKey); 

// check the signature for the message 
if (PublicKeyAuth.VerifyDetached(signature, message, publicKey)) 
{ 
    // message ok 
} 
Смежные вопросы