public byte[] Sign(ICryptographyPluginDataProvider dataProvider, X509Certificate2 certificate) { if (this.cryptoProviderAddress == null) { throw new CloudPluginException("Settings is wrong or corrupted"); } // В примере используется аутентификация в криптопровайдере по JWT-токенам. var accessToken = this.GetAccessTokenAsync().Result; if (!TryGetPin(certificate.Thumbprint, out var pin)) { // Пользователь отказался от ввода ПИН-кода. // Генерировать и показывать исключения в этом случае не нужно. Нужно просто выйти из подписания. return(null); } // Платформа sungero может дать как хеш подписываемых данных, так и поток с ними. if (certificate.SubjectName.Name == "Иванов Иван") { return(this.SignByStreamAsync(dataProvider, accessToken, pin).Result); } return(this.SignByHashAsync(dataProvider, accessToken, pin).Result); }
/// <summary> /// Подписать по хешу. /// </summary> /// <param name="dataProvider">Поставщик данных.</param> /// <param name="accessToken">Токен доступа.</param> /// <param name="pin">ПИН-код.</param> /// <returns>Подпись.</returns> private async Task <byte[]> SignByHashAsync(ICryptographyPluginDataProvider dataProvider, string accessToken, string pin) { // Поставщик данных может рассчитать хеш от данных по нужному алгоритму, если он реализован в этом или другом криптографическом плагине. var hash = dataProvider.ComputeHash("1.1.1.1.123.456"); using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri(this.cryptoProviderAddress, "/signByHash"))) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); request.Headers.Add("PIN", pin); request.Content = new StringContent(Convert.ToBase64String(hash)); using (var response = await client.SendAsync(request)) { response.EnsureSuccessStatusCode(); var signatureBase64 = await response.Content.ReadAsStringAsync(); return(Convert.FromBase64String(signatureBase64)); } } }
/// <summary> /// Подписать по потоку данных. /// </summary> /// <param name="dataProvider">Поставщик данных.</param> /// <param name="accessToken">Токен доступа.</param> /// <param name="pin">ПИН-код.</param> /// <returns>Подпись.</returns> private async Task <byte[]> SignByStreamAsync(ICryptographyPluginDataProvider dataProvider, string accessToken, string pin) { var stream = dataProvider.GetData(out var needDispose); try { using (var memoryStream = new MemoryStream()) { await stream.CopyToAsync(memoryStream); using (var request = new HttpRequestMessage(HttpMethod.Post, new Uri(this.cryptoProviderAddress, "/signByBody"))) { request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); request.Headers.Add("PIN", pin); request.Content = new StringContent(Convert.ToBase64String(memoryStream.ToArray())); using (var response = await client.SendAsync(request)) { response.EnsureSuccessStatusCode(); var signatureBase64 = await response.Content.ReadAsStringAsync(); return(Convert.FromBase64String(signatureBase64)); } } } } finally { // Потоки и их жизненный цикл могут быть разными, в зависимости от того какие данные подписываются. // Поэтому платформа Sungero передает специальный флаг needDispose, определяющий, // нужно ли в плагине после использования потока вызвать для него Dispose() или не нужно. // Также нужно учитывать, что StreamContent для потоковой передачи данных всегда вызывает Dispose() у своего внутреннего потока. // Поэтому при использовании StreamContent полученный у поставщика данных поток необходимо скопировать в другой или защитить от освобождения как-либо иначе. if (needDispose) { stream.Dispose(); } } }