Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
 Подписать e-mail сертификатом, но надо это сделать под NetworkService Account  [new]
Дмитрий77
Member

Откуда:
Сообщений: 4467
Коды отрабатывал года 2 назад, под System они работают
Есть корректный сертификат для заданного e-mail,
он установлен в системе в хранилище LocalMashine

Код такой:
    Dim strContent As String = oMsg.BodyPart.GetStream.ReadText
    Dim str_err As String = ""
    Dim signedbytes As Byte()

    ' first look in LocalMachine store
    AddLog(Now.ToString & Chr(9) & strL_SendMail_Sign_Cert_LocalMashine_Looking) ' "Looking for certificate in LocalMashine store."
    Dim certificate As X509Certificate2 = _
     GetCertForSignature(New MailAddress(oMsg.From).Address, StoreLocation.LocalMachine)
    If certificate IsNot Nothing Then
      AddLog(Now.ToString & Chr(9) & strL_SendMail_Sign_Cert_LocalMashine_Found) ' "Valid certificate found in LocalMashine store."
      AddLog(Now.ToString & Chr(9) & strL_SendMail_Sign_SigningMailContent) ' "Signing mail content."
      signedbytes = SignContent(strContent, certificate, str_err)
      If signedbytes Is Nothing Then
        AddLog(Now.ToString & Chr(9) & str_err)

Подписываем так:

  Public Function SignContent(ByVal str_content As String, ByVal certificate As X509Certificate2, _
   Optional ByRef str_error As String = vbNullString) As Byte()
    Try
      Dim data As Byte() = Encoding.GetEncoding(1252).GetBytes(str_content)
      Dim content As New ContentInfo(data)
      Dim signedCms As New SignedCms(content, False)
      Dim signer As New CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate)
      signedCms.ComputeSignature(signer, True) 'attached
      Return signedCms.Encode()
    Catch ex As Exception
      str_error = strL_SendMail_Sign_ErrorCode & ": " & Err.Number & " (" & Err.Description & ")" ' "Error code: "
      Return Nothing
    End Try
  End Function


Сертификат находит, но подписать не может, выдает в логе следующее:
20.07.2017 4:25:04	Signing mail with digital signature (opaque signing)
20.07.2017 4:25:04	Looking for certificate in LocalMashine store.
20.07.2017 4:25:04	Valid certificate found in LocalMashine store.
20.07.2017 4:25:04	Signing mail content.
20.07.2017 4:25:04	Error code: 5 (Набор ключей не существует)


Запускается это дело под NT AUTHORITY\NetworkService

NetworkService Account

(не моя прихоть, виндовый сервис работает под NetworkService, моя dll-extension соответственно тоже, dll запускает exe как CreateProcess, exe должен отправить мыло, в ф-ции отправки мыла есть опция прозрачной и непрозрачной подписи )

Кто нибудь знает как завести? Мыло (CDO.Message) соответственно уходит, но без подписи.
20 июл 17, 05:15    [20659492]     Ответить | Цитировать Сообщить модератору
 Re: Подписать e-mail сертификатом, но надо это сделать под NetworkService Account  [new]
Дмитрий77
Member

Откуда:
Сообщений: 4467
Вроде разобрался

How to assign a certificate to a service?

Заработало.

mmc-> Добавить или удалить оснастку ->
сертификаты -> добавить
учетной записи компьютера -> управляет локальным компьютером

выбираем из меню сертификата
все задачи -> управление закрытыми ключами

для NETWORK SERVICE даем доступ на чтение
20 июл 17, 06:17    [20659508]     Ответить | Цитировать Сообщить модератору
 Re: Подписать e-mail сертификатом, но надо это сделать под NetworkService Account  [new]
Дмитрий77
Member

Откуда:
Сообщений: 4467
А программно как то можно READ-access для закрытых ключей для NETWORK SERVICE, для конкретного сертификата проверить/установить?

Я с сертификатами работал только с .Net -кодами типа приведенных в первом посте.
Опыт работы с "безопасность" DACL и т.п. (а-ля cacl/icacl) как бы есть немалый, но чисто через API c объектами типа File, Folder, реестр, NT Service но никак не с сертификатами.

До кучи еще неплохо бы научиться устанавливать сертификат кодом.
Хотя бы переустанавливать.
Т.е. например серт. установлен в .CurrentUser, хочется его переустановить в .LocalSystem (мастер экспорта-импорта).
(про то что это делать с правами администратора понятно)

Т.е. типичная ситуация. Юзер поимел email-сертификат откуда-нибудь с комодо.
Браузер его бухнул в CurrentUser.
Надо:
1) скопировать его в .LocalSystem (ручками мастер экспорта-импорта)
2) дать Read доступ для закрытых ключей для NETWORK SERVICE (ручками как описал в предыдущем посте)

При этих 2-х условиях будет работать под NETWORK SERVICE код авто-подписывания из моего первого поста.
Ну, есть подозрение, что программно это как раз сделать можно.
Инструкции "сделать ручками", особенно с учетом второго пункта писать замудохаешься.
И главное юзер замудохается это делать.

Не так чтоб это первостепенная задача, но почему б не решить, если оно возможно.
20 июл 17, 14:52    [20661730]     Ответить | Цитировать Сообщить модератору
 Re: Подписать e-mail сертификатом, но надо это сделать под NetworkService Account  [new]
Дмитрий77
Member

Откуда:
Сообщений: 4467
А программно как то можно READ-access для закрытых ключей для NETWORK SERVICE, для конкретного сертификата проверить/установить?


Нарыл вот это:
How to set read permission on the private key file of X.509 certificate from .NET

С некоторой поправкой на честность кода (вариант что установлено больше одного правила, одно из которых напр. Allow All, а другое Deny Read теоретически возможен, но практически маловероятен)...

Проверка:
  Public Function IsNetworkServiceHasReadAccessToCert(ByRef cert As X509Certificate2) As Boolean
    If cert Is Nothing Then Return False
    Try
      Dim rsa As RSACryptoServiceProvider = cert.PrivateKey
      If rsa Is Nothing Then Return False
      Dim cspParams As CspParameters = New CspParameters(rsa.CspKeyContainerInfo.ProviderType, rsa.CspKeyContainerInfo.ProviderName, rsa.CspKeyContainerInfo.KeyContainerName)
      With cspParams
        .Flags = CspProviderFlags.UseExistingKey Or CspProviderFlags.UseMachineKeyStore
        .CryptoKeySecurity = rsa.CspKeyContainerInfo.CryptoKeySecurity
      End With
      For Each Rule As AuthorizationRule In _
       cspParams.CryptoKeySecurity.GetAccessRules(True, True, GetType(System.Security.Principal.NTAccount))
        Try
          If Rule.IdentityReference.Translate(GetType(SecurityIdentifier)) = _
           New SecurityIdentifier(WellKnownSidType.NetworkServiceSid, Nothing) Then
            Dim accessRule As CryptoKeyAccessRule = DirectCast(Rule, CryptoKeyAccessRule)
            If ((accessRule.CryptoKeyRights And CryptoKeyRights.GenericRead) = CryptoKeyRights.GenericRead) And _
             (accessRule.AccessControlType = AccessControlType.Allow) Then
              Return True
            End If
          End If
        Catch
        End Try
      Next
    Catch
    End Try
    Return False
  End Function


Установка Read-доступа:
  Public Function SetNetworkServicesReadAccessToCert(ByRef cert As X509Certificate2) As Boolean
    If cert Is Nothing Then Return False
    Try
      Dim rsa As RSACryptoServiceProvider = cert.PrivateKey
      If rsa Is Nothing Then Return False
      Dim cspParams As CspParameters = New CspParameters(rsa.CspKeyContainerInfo.ProviderType, rsa.CspKeyContainerInfo.ProviderName, rsa.CspKeyContainerInfo.KeyContainerName)
      With cspParams
        .Flags = CspProviderFlags.UseExistingKey Or CspProviderFlags.UseMachineKeyStore
        .CryptoKeySecurity = rsa.CspKeyContainerInfo.CryptoKeySecurity
      End With

      'set rule
      cspParams.CryptoKeySecurity.AddAccessRule _
       (New CryptoKeyAccessRule(New SecurityIdentifier(WellKnownSidType.NetworkServiceSid, Nothing), _
       CryptoKeyRights.GenericRead, AccessControlType.Allow))
      Using rsa2 As New RSACryptoServiceProvider(cspParams)
        'Only created to persist the rule change in the CryptoKeySecurity
      End Using
      'на всяк. случай проверим
      If IsNetworkServiceHasReadAccessToCert(cert) Then Return True
    Catch
    End Try
    Return False
  End Function


Ну, единственное неудобство, чтоб хотя бы просто узнать есть ли Read доступ,
надо запускать код под администратором,
иначе
Dim rsa As RSACryptoServiceProvider = cert.PrivateKey

тупо вылетит с Exeption, нет похоже прав у простого смертного юзера даже знать, кто может пользоваться private keys, а кто нет.
Или я чего-то недопонял.
А обидно однако, обычно проверку делаем под юзером,
а уж настройки менять - "вызываем админа".
Причем эту логику и в инсталлятор не запихнешь, потому как завязана на конкретный e-mail адрес и сертификат к нему.
22 июл 17, 07:23    [20666326]     Ответить | Цитировать Сообщить модератору
 Re: Подписать e-mail сертификатом, но надо это сделать под NetworkService Account  [new]
Дмитрий77
Member

Откуда:
Сообщений: 4467
Дмитрий77
Т.е. типичная ситуация. Юзер поимел email-сертификат откуда-нибудь с комодо.
Браузер его бухнул в CurrentUser.
Надо:
1) скопировать его в .LocalSystem (ручками мастер экспорта-импорта)

Это вроде просто (asAdmin):

  Private Sub ButtonReinstallToLocal_Click(sender As Object, e As EventArgs) Handles ButtonReinstallToLocal.Click
    Dim certificate As X509Certificate2 = _
    GetCertForSignature("blabla@gmail.com", StoreLocation.CurrentUser)
    If certificate IsNot Nothing Then
      MsgBox("cert. found in current user")
      MsgBox(ReInstallCertToLocalMachine(certificate))
    Else
      MsgBox("cert. not found in current user")
    End If
  End Sub


  Public Function ReInstallCertToLocalMachine(ByRef cert As X509Certificate2) As Boolean
    Try
      Dim sb As Byte()
      sb = cert.Export(X509ContentType.Pkcs12, "12345")
      Dim newcert As X509Certificate2 = _
       New X509Certificate2(sb, "12345", X509KeyStorageFlags.Exportable Or X509KeyStorageFlags.MachineKeySet Or X509KeyStorageFlags.PersistKeySet)
      Dim store As X509Store = New X509Store(StoreLocation.LocalMachine)
      store.Open(OpenFlags.ReadWrite)
      store.Add(newcert)
      store.Close()
      Return True
    Catch
      Return False
    End Try
  End Function
22 июл 17, 09:15    [20666360]     Ответить | Цитировать Сообщить модератору
Все форумы / WinForms, .Net Framework Ответить