Я хотел бы добиться чего-то, что, на мой взгляд, было довольно простым:
A) Мой клиент VB.NET (в идеале предназначенный для Framework 4.0) отправляет текстовую строку на мой сервер Apache / PHP через запрос HTTPS POST.
B) Мой сервер отвечает подписью этой текстовой строки.Закрытый ключ, используемый Сервером, всегда один и тот же, а открытый ключ, используемый Клиентом, уже встроен в исходный код.
После изучения и прочтения большого количества документации я придумал следующую стратегию и получил:два вопроса:
- Эффективна ли моя стратегия?
- Приведенный ниже код не работает (.VerifyData возвращает FALSE).Чего мне не хватает?
Стратегия
На стороне сервера
- Apache / PHP: потому что это единственный серверный язык, с которым я знаком, но яможет переключаться, если рекомендуется.
- OpenSSL: потому что я использую файлы PHP
- PEM: потому что я использую OpenSSL
- Размер ключа RSA составляет 2048 бит: рекомендуемый минимум в 2019
- Алгоритм - SHA256: потому что все, кажется, используют этот один
- Заголовок текста / обычный, UTF8: Почему нет?
Клиентская сторона
- Профиль клиента VB.Net Framework 4.0: потому что я хочу максимально использовать устаревшее (VSTO 2013)
- System.Security.Cryptography.RSACryptoServiceProvider
- Информация об открытом ключе PEM, загружаемая через строку XML
- HTTPS должен быть TLS1.0 или выше: потому что я нацеливаюсь на .Net Framework 4.0 (рекомендуется TLS1.1, если это возможно)
Исходный код
Сторона сервера (генерируйте ключ .pemфайлы, только один раз)
<?
// Create new Keys Pair
$new_key_pair = openssl_pkey_new(array(
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
));
//Save Private Key
openssl_pkey_export($new_key_pair, $private_key_pem, "my passphrase to protect my private key; add random characters like $, ?, #, & or ! for improved security");
file_put_contents('private_key.pem', $private_key_pem);
//Save Public Key
$details = openssl_pkey_get_details($new_key_pair);
$public_key_pem = $details['key'];
file_put_contents('public_key.pem', $public_key_pem);
?>
Сторона сервера (цель запросов POST)
<?
header('Content-Type: text/plain; charset=utf-8');
// Verify connection is secure
if(empty($_SERVER['HTTPS']) || $_SERVER['HTTPS']=="off")
{
echo "Unauthorized Access";
exit;
}
// Data to Sign
$data = base64_decode(file_get_contents('php://input'));
//Load Private Key
$private_key_pem = openssl_pkey_get_private('file:///path/protected/by/dotHtaccess/private_key.pem', "my passphrase to protect my private key; add random characters like $, ?, #, & or ! for improved security");
//Create Signature
openssl_sign($data, $signature, $private_key_pem, OPENSSL_ALGO_SHA256);
echo base64_encode($signature);
?>
Клиентская сторона
imports System.Diagnostics
Sub mySignatureTest()
Dim oURI As Uri = New Uri("https://www.example.com/targetpage.php")
Dim sData As String = "myStringToSign"
Dim sResponse As String
'# Get POST request Response
Using oWeb As New System.Net.WebClient()
Try
System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol And Not System.Net.ServicePointManager.SecurityProtocol.Ssl3 'Override defautl Security Protocols: Prohibit SSL3
oWeb.Encoding = Encoding.UTF8
Debug.Print("SSL version is " & System.Net.ServicePointManager.SecurityProtocol.ToString)
Debug.Print("Sending " & sData)
Debug.Print("To " & oURI.ToString)
Debug.Print("Encoding is " & oWeb.Encoding.ToString)
sResponse = oWeb.UploadString(oURI, "POST", Convert.ToBase64String(Encoding.UTF8.GetBytes(sData)))
Debug.Print("Server reponse = " & sResponse)
Catch ex As Exception
MsgBox("Connection with server failed: " & ex.ToString, vbCritical + vbOKOnly, "Add-In")
End Try
End Using
'#Verify RSA SHA256 Signature
Dim sDataToSign As String = sData
Dim sSignatureToVerify As String = sResponse
Using myRSA As New System.Security.Cryptography.RSACryptoServiceProvider
'XML format obtain from PEM file by hand copy/paste here: https://superdry.apphb.com/tools/online-rsa-key-converter
myRSA.FromXmlString("<RSAKeyValue><Modulus>SomeLongBase64StringHere</Modulus><Exponent>SomeShortBase64StringHere</Exponent></RSAKeyValue>")
Debug.Print("Signature verification = " & myRSA.VerifyData(Encoding.UTF8.GetBytes(sDataToSign), _
System.Security.Cryptography.CryptoConfig.MapNameToOID("SHA256"), _
Convert.FromBase64String(sSignatureToVerify)).ToString)
End Using
End Sub
После того, как вопрос 2) будет решен, я надеюсь, что мы сможем развить этот код, чтобы он стал хорошим, но в то же время простым примером того, как реализовать .NET Client проверку OpenSSLПодпись сервера.Мне не удалось найти простой и понятный пример в Интернете.
Как правило, его можно использовать для целей лицензирования, когда сервер предоставляет файл, содержащий дату истечения срока действия, а также подпись этой даты в порядкечтобы клиентское приложение подтвердило, что срок действия не был изменен, например, владельцем клиентского компьютера.