powered by simpleCommunicator - 2.0.54     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
20 сообщений из 20, страница 1 из 1
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38968126
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Предполагается что валидный непросроченный сертификат, соответствующий адресу отправителя в системе имеется.
(если не имеется, значит не подписываем)

Для CDO я нашел готовый пример (на VB6, но какая разница):
How to send digitally signed messages by using CDOSYS/CDOEX

Потенциальный минус кода, бросающийся в глаза: используется CAPICOM которая в отличии от SDOSYS штатным компонентом системы не является (надо доустанавливать с MS).
На рабочем компе CAPICOM у меня установлен (его требует signtool.exe которым я подписываю файлы проги) , но код пока не проверял.

Наверняка можно через API.
И подозреваю что саму подпись можно добавить и .Net средствами, я cмотрю в .Net много всего на эту тему, чуть ли не больше чем в WIN API, хотя странно это.

С System.Net.Mail так понимаю сложнее, т.е. как-то надо выдернуть контекст подготовленного Net.Mail.MailMessage, подписать его и сделать новое Net.Mail.MailMessage.
Ну, я не нашел нормальных примеров, и есть сомнения что это в общем случае возможно (с вложениями и т.п.).

Сторонние либы - ну их нафиг, не рассматриваем, уже с IMAP пытался "освоить", время зря потратил.

Есть соображения?

P.S. На всяк. случай уточню: речь идет о подписи сообщения, а не о его шифровании - это разные вещи.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38968217
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А как мне из хранилища выбрать валидный для подписи e-mail сертификат?
Да хоть с CAPICOM, хоть с .Net
Я пока VB6 пример решил разобрать, но там ф-ции не хватает, типа пишите сами:
Код: vbnet
1.
Public Function GetCertForSignature(ByVal email_addr_from As String) As CAPICOM.Certificate


17687782
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38968231
Фотография VSVLAD
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77,

Методом тыка и не только, открываем хранилище My и выбираем сертификаты все, и выводим их назначения. Я так думаю, тебе нужно будет выбрать с тем OID, который соответствует подписи Email. Проверять можно методом Verify(), вроде работает. На MSDN примерчики есть, если искать по классу X509***

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
Dim xStore As New Security.Cryptography.X509Certificates.X509Store(StoreName.My, StoreLocation.CurrentUser)
xStore.Open(OpenFlags.ReadOnly)

For Each xCert In xStore.Certificates()
	Console.WriteLine("------ " & xCert.Issuer)

	For Each xExt In xCert.Extensions
		Console.WriteLine(xExt.Oid.Value & " / " & xExt.Oid.FriendlyName)
	Next
Next

xStore.Close()
Console.ReadLine()
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38968236
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
VSVLAD,

Рекомендуешь сразу через .Net пробовать, если разбирать приведенный мной пример с CDO? Та часть что относится к CAPICOM, она мне как раз не нравится, почему написал. Полагаю ее можно заменить на .Net -классы (твой код).
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38968290
Фотография VSVLAD
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77,

Где-то читал, что начиная с Висты CAPICOM официально больше не поддерживается, и рекомендуют заменять .NET классами X509Certificates
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38968303
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
VSVLAD,

Найти сертификат, по большому счету фигня.

Ну у меня пока и с CAPICOM ни с VB6, ни с .Net нифига с MS-примером не получилось.

Оригинальный код из статьи не работает, возможно из-за этой строчки:
Код: vbnet
1.
2.
 ' Copy input into output message
    oSecMsg.DataSource.OpenObject oMsg, cdoIMessage


(oSecMsg походу битый получается, вообще не отправляет)

Если передавать оригинальный byRef oMsg на подпись, то в зависимости от варианта:

Код: vbnet
1.
    oSignedData.Content = strContent ' StrConv(strContent, vbFromUnicode)


приходит либо битая фигня (если с StrConv),
либо подписанная но опять же фигня (если без StrConv),

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
Public Function SignMessage(ByRef oMsg As CDO.Message) As Boolean
'    Dim oSecMsg As New CDO.Message
    'Dim oBodyPart As CDO.IBodyPart
    Dim oSignedData As New CAPICOM.SignedData
    Dim strData As String
    Dim strContent As String
    Dim oStream As ADODB.Stream
    Dim oSigner As New CAPICOM.Signer
    Dim oCert As CAPICOM.Certificate
    Dim oAttr As New CAPICOM.Attribute
    Dim byteData() As Byte
    
    'On Error GoTo handle_error
    
'    ' Copy input into output message
'    oSecMsg.DataSource.OpenObject oMsg, cdoIMessage
    
    ' Set up main bodypart
    'Set oBodyPart = oMsg.BodyPart
    oMsg.BodyPart.ContentMediaType = "application/pkcs7-mime;smime-type=signed-data;name=""smime.p7m"""
    oMsg.BodyPart.ContentTransferEncoding = "base64"
    oMsg.BodyPart.Fields("urn:schemas:mailheader:content-disposition") = "attachment;FileName=""smime.p7m"""
    oMsg.BodyPart.Fields.Update
            
    ' Get certificate
    Set oCert = GetCertForSignature("user@gmail.com") '(oSecMsg.From)

    ' If no cert, throw an error and exit
    If oCert Is Nothing Then
        MsgBox "No valid certificate found for sender.", , "Error"
        SignMessage = False
    End If

    ' Add cert to signer object
    oSigner.Certificate = oCert

    ' Add signing time attribute to signer object
    oAttr.Name = CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME
    oAttr.Value = Now
    oSigner.AuthenticatedAttributes.Add oAttr

    ' Sign the content (root bodypart)
    strContent = oMsg.BodyPart.GetStream.ReadText
    oSignedData.Content = strContent ' StrConv(strContent, vbFromUnicode)

    strData = oSignedData.Sign(oSigner, False, CAPICOM_ENCODE_BINARY)

    ' Write the cms blob into the main bodypart
    ' let CDO do the base64 encoding
    Set oStream = oMsg.BodyPart.GetDecodedContentStream

    oStream.Type = adTypeBinary

    ' Get the string data as a byte array
    byteData = strData

    ' Write the data to the stream and flush it
    oStream.Write byteData
    oStream.Flush
    
    GoTo cleanup
    
    ' Report error
handle_error:
    MsgBox Err.Number & ": " & Err.Description, , "Error:"
    'Set oSecMsg = Nothing
    
    ' Clean up memory
cleanup:
    'Set oBodyPart = Nothing
    Set oSignedData = Nothing
    Set oStream = Nothing
    Set oSigner = Nothing
    Set oCert = Nothing
    Set oAttr = Nothing
    
    ' Return new message
    'Set SignMessage = oSecMsg
End Function

Public Function GetCertForSignature(ByVal signature As String) As CAPICOM.Certificate
  Dim MyStore As New CAPICOM.Store
  
  ' Open the MY store and retrieve the first compartible certificate from the
  ' Store. The signing operation will only work if this
  ' certificate is valid and has access to the signer's private key.
  MyStore.Open CAPICOM_CURRENT_USER_STORE, "MY", CAPICOM_STORE_OPEN_READ_ONLY
  Dim cert As CAPICOM.Certificate
  For Each cert In MyStore.Certificates
    If cert.IsValid Then
      'Debug.Print cert.SubjectName
      If InStr(cert.SubjectName, signature) > 0 Then
        Debug.Print cert.SubjectName
        Set GetCertForSignature = cert
        Exit Function
      End If
    End If
  Next



С .Net вот нарыл "аналог", подпись вроде ставит (в варианте с false ) , и она есть в пришедшем письме, но только в заголовках,
а не в виде значочка, то бишь не то подписываю что отправляю, какие-то байты напутаны.

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
 Public Function GetCertForSignature(ByVal signature As String) As CAPICOM.Certificate
    Dim MyStore As New CAPICOM.Store

    ' Open the MY store and retrieve the first compartible certificate from the
    ' Store. The signing operation will only work if this
    ' certificate is valid and has access to the signer's private key.
    MyStore.Open(CAPICOM.CAPICOM_STORE_LOCATION.CAPICOM_CURRENT_USER_STORE, "MY", _
     CAPICOM.CAPICOM_STORE_OPEN_MODE.CAPICOM_STORE_OPEN_READ_ONLY)
    For Each cert As CAPICOM.Certificate In MyStore.Certificates
      If cert.IsValid.Result Then
        'Debug.Print cert.SubjectName
        If InStr(cert.SubjectName, signature) > 0 Then
          Debug.Print(cert.SubjectName)
          Return cert
          Exit Function
        End If
      End If
    Next
    Return Nothing
    'Signer.Certificate = MyStore.Certificates.Item(1)

  End Function

  Private Function SignMessage(ByRef Message As Object, ByVal ClearFlag As Boolean) As Boolean

    'http://www.pcreview.co.uk/threads/re-capicom-use-in-c-problem-with-verifying-email-signature.1233519/

    Dim SignedMessage As Object ' = CreateObject("CDO.Message")
    Dim BodyPart As Object 'CDO.IBodyPart
    Dim Fields As Object
    Dim Stream As ADODB.Stream
    Dim Signer As New CAPICOM.Signer
    Dim SignedData As New CAPICOM.SignedData
    Dim Utilities As New CAPICOM.Utilities
    Dim Attribute As New CAPICOM.Attribute
    Dim SignerCertificate As CAPICOM.Certificate
    Dim SignerCertificates As CAPICOM.Certificates
    Dim Store As New CAPICOM.Store
    Dim Signature As String
    Dim byteSignature() As Byte

    ' Used for Global Memory Allocation to send ANSI Text to Signer
    Dim MyGC As GCHandle

    ' Used to look up Signature Capable Certificates
    'Const CERT_KEY_SPEC_PROP_ID = 6


    'Try
    ' create the SignedData object we will use to create the PKCS7()
    SignedData = New CAPICOM.SignedData

    ' create the new message
    SignedMessage = CreateObject("CDO.Message") ' New CDO.Message

    ' select the signer certificate
    Signer.Certificate = GetCertForSignature("user@gmail.com")


    ' set the from field based off of the selected certificate
    SignedMessage.From = _
      Signer.Certificate.GetInfo(CAPICOM.CAPICOM_CERT_INFO_TYPE.CAPICOM_CERT_INFO_SUBJECT_EMAIL_NAME)

    ' Add signing time attribute to signer object
    Attribute = New CAPICOM.Attribute
    Attribute.Name = CAPICOM.CAPICOM_ATTRIBUTE.CAPICOM_AUTHENTICATED_ATTRIBUTE_SIGNING_TIME
    Attribute.Value = Now
    Signer.AuthenticatedAttributes.Add(Attribute)

    Select Case ClearFlag
      Case True

        ' this is to be a clear text signed message so we need to copy the interesting
        ' parts (sender, recipient, and subject) into the new header
        SignedMessage.To = Message.To
        SignedMessage.CC = Message.CC
        SignedMessage.Subject = Message.Subject

        BodyPart = SignedMessage.BodyPart.AddBodyPart
        Fields = BodyPart.Fields
        Fields.Item("urn:schemas:mailheader:content-type") =
         Message.BodyPart.BodyParts(1).Fields("urn:schemas:mailheader:content-type").Value
        Fields.Update()

        Stream = BodyPart.GetDecodedContentStream

        Stream.WriteText(Message.BodyPart.BodyParts(1).GetDecodedContentStream.ReadText)
        Stream.Flush()

        ' set the content to be signed
        ' This only works for words that are less than 6 bytes long - it's wierd
        'Dim bytes As Byte()
        'bytes = System.Text.Encoding.GetEncoding(1252).GetBytes(SignedMessage.BodyPart.BodyParts(1).GetStream.ReadText)
        'SignedData.Content =  System.Text.Encoding.Unicode.GetString(Bytes)
        ' -- Original VB v6 -- SignedData.Content = StrConv(SignedMessage.BodyPart.BodyParts(1).GetStream.ReadText, vbFromUnicode)
        ' See  http://support.microsoft.com/default.aspx?scid=kb;en-us;Q311338

        ' Therefore it was necessary to utilize a Custom  Designed Interop
        ' Per the instructions of:http://groups.google.com/groups?hl=...oups?hl=en&lr=&q=System.Text.Encoding+capicom

        'A byte array
        Dim Bytes() As Byte

        'Convert string to byte and copy to byte array
        Bytes = Encoding.GetEncoding(1251).GetBytes(SignedMessage.BodyPart.BodyParts(1).GetStream.ReadText)
        'Bytes = Encoding.UTF8.GetBytes(SignedMessage.BodyPart.BodyParts(1).GetStream.ReadText)

        ' Now in order to use the new Interop - it is expecting a Pointer (native int)
        ' The Runtime Interop Classes provide the ability to do this.
        ' But make sure to free memory

        'Create Gchandle instance and pin variable required
        MyGC = GCHandle.Alloc(Bytes, GCHandleType.Pinned)

        'get address of variable in pointer variable
        Dim ByteAddress As IntPtr =
        MyGC.AddrOfPinnedObject()

        ' Set the Content to be signed
        SignedData.Content = ByteAddress

        ' sign the content
        Signature = SignedData.Sign(Signer, True, CAPICOM.CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BINARY)

        ' Get the string data as a byte array
        byteSignature = System.Text.Encoding.Unicode.GetBytes(Signature)

        ' Attach the signature and let CDO base64 encode it
        BodyPart = SignedMessage.BodyPart.AddBodyPart
        Fields = BodyPart.Fields

        'BodyPart.Fields.Item("urn:schemas:mailheader:content-type").Value = _
        ' "application/x-pkcs7-signature" & vbCrLf & "Name = ""smime.p7s"""
        BodyPart.Fields.Item("urn:schemas:mailheader:content-type").Value = _
         "application/pkcs7-mime;smime-type=signed-data;name=""smime.p7m"""

        BodyPart.Fields.Item("urn:schemas:mailheader:content-transfer-encoding").Value = "base64"

        'BodyPart.Fields.Item("urn:schemas:mailheader:content-disposition").Value _
        ' = "attachment;" & vbCrLf & "FileName=""smime.p7s"""
        BodyPart.Fields.Item("urn:schemas:mailheader:content-disposition").Value _
         = "attachment;FileName=""smime.p7m"""
        Fields.Update()

        Stream = BodyPart.GetDecodedContentStream
        Stream.Type = ADODB.StreamTypeEnum.adTypeBinary
        Stream.Write(byteSignature)
        Stream.Flush()

        ' Set the messages content type, this needs to be done last to ensure it is not changed when we add the BodyParts

        SignedMessage.Fields.Item("urn:schemas:mailheader:content-type").Value _
         = "multipart/signed;" & vbCrLf & "protocol=""application/x-pkcs7-signature"";" & _
         vbCrLf & "micalg=SHA1;"
        SignedMessage.Fields.Update()

        'Free GChandle to avoid memory leaks
        MyGC.Free()


      Case False
        ' this is to be a opaquely signed message so we need to copy the entire message into our
        ' new encrypted message
        SignedMessage.DataSource.OpenObject(Message, "IMessage")

        ' Set up main bodypart
        BodyPart = SignedMessage.BodyPart
        'BodyPart.ContentMediaType =
        '"application/pkcs7-mime;" & vbCrLf & "smime-type=signed-data;" & vbCrLf _
        ' & "name=""smime.p7m"""
        BodyPart.Fields.Item("urn:schemas:mailheader:content-type").Value = _
         "application/pkcs7-mime;smime-type=signed-data;name=""smime.p7m"""
        BodyPart.ContentTransferEncoding = "base64"
        Fields = BodyPart.Fields

        'Fields("urn:schemas:mailheader:content-disposition") = "attachment;" & _
        ' vbCrLf & "FileName=""smime.p7m"""
        Fields.Item("urn:schemas:mailheader:content-disposition").Value _
         = "attachment;FileName=""smime.p7m"""
        BodyPart.Fields.Update()

        ' set the from field based off of the selected certificate()
        Message.From = Signer.Certificate.GetInfo(CAPICOM.CAPICOM_CERT_INFO_TYPE.CAPICOM_CERT_INFO_SUBJECT_EMAIL_NAME)

        ' set the content to be signed
        ' This only works for words that are less than 6 bytes long - it's wierd
        'Dim bytes As Byte()
        'bytes = System.Text.Encoding.GetEncoding(1252).GetBytes(SignedMessage.BodyPart.BodyParts(1).GetStream.ReadText)
        'SignedData.Content =          System.Text.Encoding.Unicode.GetString(Bytes)
        ' -- Original VB v6 DIFFERENT THAN ABOVE IT USES          Message --SignedData.Content =          StrConv(SignedMessage.BodyPart.BodyParts(1).GetStream.ReadText,          vbFromUnicode)
        ' See http://support.microsoft.com/default.aspx?scid=kb;en-us;Q311338

        ' Therefore it was necessary to utilize a Custom          Designed Interop
        ' Per the instructions of:http://groups.google.com/groups?hl=...oups?hl=en&lr=&q=System.Text.Encoding+capicom

        'A byte array
        Dim Bytes() As Byte

        'Convert string to byte and copy to byte array
        Bytes = Encoding.GetEncoding(1251).GetBytes(Message.BodyPart.GetStream.ReadText)
        'Bytes = Encoding.UTF8.GetBytes(Message.BodyPart.GetStream.ReadText)

        ' Now in order to use the new Interop - it is expecting a Pointer (native int)
        ' The Runtime Interop Classes provide the ability to do this.
        ' But make sure to free memory

        'Create Gchandle instance and pin variable required
        MyGC = GCHandle.Alloc(Bytes, GCHandleType.Pinned)

        'get address of variable in pointer variable
        Dim ByteAddress As IntPtr =
        MyGC.AddrOfPinnedObject()

        ' Set the Content to be signed
        SignedData.Content = ByteAddress

        ' Sign the content
        Signature = SignedData.Sign(Signer, False, CAPICOM.CAPICOM_ENCODING_TYPE.CAPICOM_ENCODE_BINARY)

        ' Get the string data as a byte array
        byteSignature = System.Text.Encoding.Unicode.GetBytes(Signature)

        ' Attach the signature and let CDO base64 encode it
        Stream = BodyPart.GetDecodedContentStream
        Stream.Type = ADODB.StreamTypeEnum.adTypeBinary
        Stream.Write(byteSignature)
        Stream.Flush()

        'Free GChandle to avoid memory leaks
        MyGC.Free()

    End Select

    ' Signing Was sucessfull
    SignMessage = True
    Message = SignedMessage

    ' Catch ex As Exception
    'MsgBox(ex.Message, MsgBoxStyle.Critical, "Error:")

    'If Err.Number <> CAPICOM_E_CANCELLED Then
    ' MsgBox("Error: " & Hex(Err.Number) & ": " & Err.Description)
    'End If
    'Err.Clear()

    ' An error occurred
    'SignMessage = False
    'Message = Nothing


    '  Finally

    If MyGC.IsAllocated Then
      MyGC.Free()
      MyGC = Nothing
    End If

    SignedMessage = Nothing
    BodyPart = Nothing
    Fields = Nothing
    Stream = Nothing
    Signer = Nothing
    SignedData = Nothing
    Utilities = Nothing
    Attribute = Nothing
    SignerCertificate = Nothing
    SignerCertificates = Nothing
    Store = Nothing
    '   End Try

  End Function



Причем проблема так понимаю исходит из того же преобразования, которая в VB6 сидит в строчке
StrConv(strContent, vbFromUnicode).

Да забью наверно.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38970275
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Повозился, вроде как интересно стало.
Вот максимум чего удалось добиться (CAPICOM в .Net не нужна) -это полный аналог VB6 кода от MS что я привел в первом посте:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
  Private Sub ButtonGmail_CDO_sign_Click(sender As Object, e As EventArgs) Handles ButtonGmail_CDO_sign.Click
    Try
      Dim o_Mess = CreateObject("CDO.Message")
      Dim v_Conf As String = "http://schemas.microsoft.com/cdo/configuration/"
      With o_Mess
        '===(В-1)код необходим для добавления utf-8 в Subject(если .AddAttachment стоит до .TextBodyPart.Charset)===
        .BodyPart.Charset = "utf-8"
        '===(В-1)код необходим для добавления utf-8 в Subject(если .AddAttachment стоит до .TextBodyPart.Charset)===
        .To = "Получатель<user@bk.ru>;"
        .From = "Отправитель<user@gmail.com>;"
        .Subject = "Тема сообщения"
        .TextBody = "Текст сообщения"
        .TextBodyPart.Charset = "utf-8"
        .AddAttachment(IO.Path.Combine(Application.StartupPath, "readme.txt")) '.Charset = "utf-8"
        With .Configuration.Fields
          .Item(v_Conf & "sendusing") = 2
          .Item(v_Conf & "smtpserver") = "smtp.gmail.com"
          .Item(v_Conf & "smtpserverport") = 465
          .Item(v_Conf & "smtpauthenticate") = 1 '1-использовать
          .Item(v_Conf & "sendusername") = "user@gmail.com"
          .Item(v_Conf & "sendpassword") = "password"
          .Item(v_Conf & "smtpusessl") = True 'использовать SSL
          .Item(v_Conf & "smtpconnectiontimeout") = 60 'пока считаем что 60, потом м.б. добавим параметр
          .Update()
        End With

      End With

      Dim o_signMess As Object 'CDO.Message
      o_signMess = SignMessage(o_Mess)

      If o_signMess IsNot Nothing Then
        o_signMess.Send()
        o_signMess.Attachments.DeleteAll()
        o_signMess = Nothing
      Else
        o_Mess.Send()
      End If
      o_Mess.Attachments.DeleteAll()
      o_Mess = Nothing

      MsgBox("Test mail was sent successfully!", vbInformation + vbOKOnly, "Email was sent")
    Catch
      MsgBox("Sending mail...Failed." & vbCrLf & "Error code: " & Err.Number & vbCrLf & Err.Description, _
       vbCritical + vbOKOnly, "Sending mail failed.")
    End Try
  End Sub

  Public Function SignMessage(oMsg As Object) As Object 'CDO.Message
    Dim oSecMsg As Object 'New CDO.Message
    oSecMsg = CreateObject("CDO.Message")
    Dim oBodyPart As Object ' CDO.IBodyPart
    Dim oStream As ADODB.Stream

    On Error GoTo handle_error

    ' Copy input into output message
    oSecMsg.DataSource.OpenObject(oMsg, "IMessage") ' cdoIMessage

    ' Set up main bodypart
    oBodyPart = oSecMsg.BodyPart
    oBodyPart.ContentMediaType = "application/pkcs7-mime;smime-type=signed-data;name=""smime.p7m"""
    oBodyPart.ContentTransferEncoding = "base64"
    oBodyPart.Fields("urn:schemas:mailheader:content-disposition") = "attachment;FileName=""smime.p7m"""
    oBodyPart.Fields.Update()

    ' Get certificate '1.3.6.1.5.5.7.3.4 – защита электронной почты (Email Protection);  
    Dim store As New X509Store(StoreLocation.CurrentUser)
    store.Open(OpenFlags.OpenExistingOnly Or OpenFlags.[ReadOnly])
    Dim certs As X509Certificate2Collection = _
     store.Certificates.Find(X509FindType.FindByApplicationPolicy, "1.3.6.1.5.5.7.3.4", True).Find( _
    X509FindType.FindByKeyUsage, "KeyEncipherment", True)
    Dim certificate As X509Certificate2 = Nothing
    For Each cert As X509Certificate2 In certs
      If cert.Subject.IndexOf(New MailAddress(oMsg.From).Address) >= 0 Then '"user@gmail.com"
        certificate = cert
        Exit For
      End If
    Next

    ' If no cert, throw an error and exit
    If certificate Is Nothing Then
      MsgBox("No valid certificate found for sender.", , "Error")
      SignMessage = Nothing
    End If

    Dim strContent As String = oMsg.BodyPart.GetStream.ReadText
    Dim data As Byte() = Encoding.GetEncoding(1252).GetBytes(strContent) 'с 1252 русский utf8 текст не коверкается
    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) 'означает что не вывожу системный диалог подписи, но это может вызвать ошибку
    Dim signedbytes As Byte() = signedCms.Encode()

    ' Write the signature into the main bodypart
    ' let CDO do the base64 encoding
    oStream = oSecMsg.BodyPart.GetDecodedContentStream
    oStream.Type = ADODB.StreamTypeEnum.adTypeBinary

    ' Write the data to the stream and flush it
    oStream.Write(signedbytes)
    oStream.Flush()

    oSecMsg.Configuration = oMsg.Configuration

    GoTo cleanup

    ' Report error
handle_error:
    MsgBox(Err.Number & ": " & Err.Description, , "Error:")
    oSecMsg.Attachments.DeleteAll()
    oSecMsg = Nothing

    ' Clean up memory
cleanup:
    oBodyPart = Nothing
    oStream = Nothing

    ' Return new message
    SignMessage = oSecMsg
  End Function



Проблемы такие:

1) Данный код реализует "Кодировать сообщения перед подписыванием (непрозрачная подпись)",
то бишь без OE-подобного в каком-нибудь Web-клиенте который сертификатов в лоб не видит фиг прочтешь.

Т.е. код прошивает main bodypart подписью насквозь (не шифрование, но на дурака уже не прочтешь)
Правильно конечно подписать main bodypart вот так:
Код: vbnet
1.
2.
  Dim signedCms As New SignedCms(content, True) 
'только подпись подтверждающая валидность content без него самого


и получившееся добавить в новую BodyPart oSecMsg.BodyPart.AddBodyPart
Но сколько я не мудохался, ничего не выходит.
Подпись в конец "прозрачно добавляется" но она ошибочная(не соответствует content).
Вот код (аналогичен первому, но есть отличия в алгоритме), м.б. кто сообразит в чем засада:
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
 Public Function SignMessageClear(oMsg As Object) As Object 'CDO.Message
    Dim oSecMsg As Object 'New CDO.Message
    oSecMsg = CreateObject("CDO.Message")
    Dim oBodyPart As Object ' CDO.IBodyPart
    Dim oStream As ADODB.Stream

    On Error GoTo handle_error

    ' Copy input into output message
    oSecMsg.DataSource.OpenObject(oMsg, "IMessage") ' cdoIMessage

    ' Get certificate
    Dim store As New X509Store(StoreLocation.CurrentUser)
    store.Open(OpenFlags.OpenExistingOnly Or OpenFlags.[ReadOnly])
    Dim certs As X509Certificate2Collection = store.Certificates
    Dim certificate As X509Certificate2 = Nothing
    For Each cert As X509Certificate2 In certs
      If cert.Subject.IndexOf(New MailAddress(oMsg.From).Address) >= 0 Then '"user@gmail.com"
        certificate = cert
        Exit For
      End If
    Next

    ' If no cert, throw an error and exit
    If certificate Is Nothing Then
      MsgBox("No valid certificate found for sender.", , "Error")
      SignMessageClear = Nothing
    End If

    Dim strContent As String = oMsg.GetStream.ReadText() '= oMsg.BodyPart.GetStream.ReadText()
    Dim data As Byte() = Encoding.GetEncoding(1252).GetBytes(strContent)
    Dim content As New ContentInfo(data)
    Dim signedCms As New SignedCms(content, True) 'подпись отдельно от тела письма
    Dim signer As New CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate)
    signedCms.ComputeSignature(signer)
    Dim signedbytes As Byte() = signedCms.Encode()

    ' Attach the signature and let CDO base64 encode it
    oBodyPart = oSecMsg.BodyPart.AddBodyPart
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-type") = _
     "application/x-pkcs7-signature" & vbCrLf & "Name = ""smime.p7s"""
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-transfer-encoding") = "base64"
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-disposition") _
     = "attachment;" & vbCrLf & "FileName=""smime.p7s"""
    oBodyPart.Fields.Update()

    oStream = oBodyPart.GetDecodedContentStream
    oStream.Type = ADODB.StreamTypeEnum.adTypeBinary

    ' Write the data to the stream and flush it
    oStream.Write(signedbytes)
    oStream.Flush()

    oSecMsg.Fields.Item("urn:schemas:mailheader:content-type") _
     = "multipart/signed;" & vbCrLf & "protocol=""application/x-pkcs7-signature"";" & _
     vbCrLf & "micalg=SHA1;"
    oSecMsg.Fields.Update()

    oSecMsg.Configuration = oMsg.Configuration

    GoTo cleanup

    ' Report error
handle_error:
    MsgBox(Err.Number & ": " & Err.Description, , "Error:")
    oSecMsg.Attachments.DeleteAll()
    oSecMsg = Nothing

    ' Clean up memory
cleanup:
    oBodyPart = Nothing
    oStream = Nothing

    ' Return new message
    SignMessageClear = oSecMsg
  End Function



Проблема 2) С сертификатом для user@gmail у меня вот эта строчка
Код: vbnet
1.
    signedCms.ComputeSignature(signer, True)


съедает сертификат автоматически
А с другим мылом выдает ошибку: типа автоматически невозможно.
В обоих случаях сертификат однозначным образом определяется.
Помогает конечно вывод системного диалога подтверждения подписи:
Код: vbnet
1.
    signedCms.ComputeSignature(signer, False)


Но меня так не устраивает.
Как прога автоматически будет отправлять подписанные письма?
Нет какого коллбэка для автоподтверждения?
Хотя подозреваю что это может быть связано с настройкой самого сертификата и тогда ничего сделать нельзя.

Вопрос 3. Ну а для System.Net.Mail подписать никак?
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38971201
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77 Проблема 2) С сертификатом для user@gmail у меня вот эта строчка
Код: vbnet
1.
    signedCms.ComputeSignature(signer, True)


съедает сертификат автоматически
А с другим мылом выдает ошибку: типа автоматически невозможно.
В обоих случаях сертификат однозначным образом определяется.
Помогает конечно вывод системного диалога подтверждения подписи:
Код: vbnet
1.
    signedCms.ComputeSignature(signer, False)


Но меня так не устраивает.
Как прога автоматически будет отправлять подписанные письма?
Нет какого коллбэка для автоподтверждения?
Хотя подозреваю что это может быть связано с настройкой самого сертификата и тогда ничего сделать нельзя.
Это можно и нужно решать двумя способами.
1) При импорте сертификата в хранилище CurrentUser(Текущий пользователь) не ставить галку напротив "включать усиленную защиту закрытого ключа". Если поставить (как я обычно делаю), то будет выдавать предупреждение при каждом подписывании, что несовместимо с "автоматикой" и функцией signedCms.ComputeSignature(signer, True ). Но иногда однако прокатывает и с "усиленной защитой" (с сертификатом от Comodo на Win8.1 в моем случае, причем на XP с тем же сертификатом при "усиленной защите" уже не работает).
2) (наиболее правильный). Импортировать сертификат в хранилище LocalSystem (Локальный компьютер).
При этом никаких "усиленных защит" при импорте сертификата вообще не предлагается и все всегда работает (для любого пользователя).
И, это единственный правильный путь если прога имеет место быть работает сервисом NT.
Дмитрий77 Вопрос 3. Ну а для System.Net.Mail подписать никак?
Пожалуй лучшая статья что я нашел на эту тему.
Sending S/MIME encrypted email using C#
(там приведен конкретный алгоритм)
Но в целом понятно что на "Net.-халяву" для общего случая (с вложениями и т.п.) сделать не получится,
т.к. .Net-овский конструктор MailMessage крайне скуден даже если сравнивать с CDO.Message.
Так понимаю если только весь MIME для body ручками писать, при доступности CDO оно того не стоит.
Дмитрий77 1) Данный код реализует "Кодировать сообщения перед подписыванием (непрозрачная подпись)",
то бишь без OE-подобного в каком-нибудь Web-клиенте который сертификатов в лоб не видит фиг прочтешь.

Т.е. код прошивает main bodypart подписью насквозь (не шифрование, но на дурака уже не прочтешь)
Правильно конечно подписать main bodypart вот так:
Код: vbnet
1.
2.
  Dim signedCms As New SignedCms(content, True) 
'только подпись подтверждающая валидность content без него самого


и получившееся добавить в новую BodyPart oSecMsg.BodyPart.AddBodyPart
Но сколько я не мудохался, ничего не выходит.
Подпись в конец "прозрачно добавляется" но она ошибочная(не соответствует content).
А вот это самое обидное -что не удалось с прозрачной подписью (с CDO, второй длинный код что я привел целиком выше, подпись битая получается).
В лучшем случае где-то напутал с какими-то байтами.
Но есть еще подозрение что в принципе не заведется.
В том случае если при добавлении к Body новой BodyPart (подпись Body), CDO автоматом меняет какие-то байты и/ли заголовки в исходных BodyParts (которые подписываем). Если это так, то задача в CDO-модели в принципе не решаема, но не уверен что так.
Обидно-с.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38971222
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
VSVLADДмитрий77,

Где-то читал, что начиная с Висты CAPICOM официально больше не поддерживается, и рекомендуют заменять .NET классами X509CertificatesА чего бы ему не поддерживаться? Это обычная COM-dll. Зарегистрировал и вперед.

Другое дело, она может юзать апи, которых больше нет. Но тогда резко становится несовместимым весь софт, которых их юзает напрямую.

Да не, скорее всего смысл в том, что capicom по каким-то причинам больше не входит в дистрибутив.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38971237
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Antonariy,

CAPICOM в .Net полностью заменяется на вот это, нарочно вынес в 2 отдельные ф-ции, это финальный вариант (уже вставил первый код что выше в проект):
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
 '"C:\WINDOWS\SYSTEM32\MMC.EXE" "C:\WINDOWS\SYSTEM32\CERTLM.MSC" -системное хранилище
  Public Function GetCertForSignature(ByVal str_email As String, ByVal location As StoreLocation, _
   Optional ByRef str_error As String = vbNullString) As X509Certificate2
    Try
      ' Get certificate '1.3.6.1.5.5.7.3.4 – защита электронной почты (Email Protection);  
      Dim store As New X509Store(location)
      store.Open(OpenFlags.OpenExistingOnly Or OpenFlags.[ReadOnly])
      Dim certs As X509Certificate2Collection = _
       store.Certificates.Find(X509FindType.FindByApplicationPolicy, "1.3.6.1.5.5.7.3.4", True).Find( _
      X509FindType.FindByKeyUsage, "KeyEncipherment", True)
      Dim certificate As X509Certificate2 = Nothing
      For Each cert As X509Certificate2 In certs
        If cert.Subject.IndexOf(str_email) >= 0 Then '"user@gmail.com"
          Return cert
          Exit For
        End If
      Next
    Catch ex As Exception
      str_error = "Error code: " & Err.Number & " (" & Err.Description & ")"
    End Try
    Return Nothing
  End Function

  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)
      Return signedCms.Encode()
    Catch ex As Exception
      str_error = "Error code: " & Err.Number & " (" & Err.Description & ")"
      Return Nothing
    End Try
  End Function



Скажи лучше, м.б. ты знаешь почему у меня нижний код не делает желаемого эффекта и в чем грабли (с пристыковкой прозрачной подписи).
Почему работает верхний - понятно, потому что я целиком заменяю Body на "подписанный кусок" (непрозрачная подпись).
Т.е. если я чуть (на пару байтов) искажаю оригинал, то подписываю я уже "искаженный" и это не страшно.
А во втором случае один байт уже критичен.

Коды все рабочие и я ничего не выкидывал.
Можешь вставить проект и попробовать.
Бесплатный сертификат для почты можно взять на Comodo.

P.S. Мне просто обидно что не завелось.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38971242
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77Скажи лучше, м.б. ты знаешь почему у меня нижний код не делает желаемого эффекта и в чем грабли (с пристыковкой прозрачной подписи).Вообще без понятия, я capicom мимоходом смотрел и с сертификатами не работал.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38971338
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Antonariy,

пофиг, с CDO то наверно работал.
надо понять правильно ли я читаю Content мыла в байт массив data через GetStream (adodb.stream)
Код: vbnet
1.
2.
   Dim strContent As String = oMsg.GetStream.ReadText() '= oMsg.BodyPart.GetStream.ReadText()
    Dim data As Byte() = Encoding.GetEncoding(1252).GetBytes(strContent)


Честно говоря меня 1252 несколько смущает, но с 1251, ASSI, Unicode или Default русский текст закодированный в MIME с флагом utf-8 корячится. Утверждение бардачное, но тем не менее. Объяснение про 1252 вычитал на каком то форуме (.UK), вообще хз, тамошний ТС кроме English возможно других языков не знает, и если его утверждение не обобщенное, то возможно в этом 1252 собака и зарыта.

Я подписываю именно байт-массив data As Byte() а результат (подпись) добавляю как новый раздел oBodyPart = oSecMsg.BodyPart.AddBodyPart в мой CDO.Message.
Если я в data As Byte() получил "чуть не то", то у меня подпись уже не верна, что и имеет место быть.

А как я из data() получаю саму подпись signedbytes As Byte() (через CAPICOM или через .Net -хрени) -это уже пофиг - они абсолютно одно и то же одинаково хорошо делают.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38971368
Фотография Antonariy
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77Antonariy,

пофиг, с CDO то наверно работал.Работал, но сильно не вникал. Сделал - заработало - забыл. Мне от него были нужны лишь самые базовые функции.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38971722
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Кажется понял ошибку:

У меня структура неподписанного письма грубо такая
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
Content-Type: multipart/mixed;
	boundary="----=_NextPart_1"
------=_NextPart_1
Content-Type: text/plain;
    здесь например текст письма
------=_NextPart_1
Content-Type: image/tiff;
    здесь например вложение
------=_NextPart_1 'здесь конец последней части multipart/mixed



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

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
Content-Type: multipart/signed;
	boundary="----=_NextPart_2"
------=_NextPart_2 'здесь начинается первая часть(то что подписываем, исходное письмо)
    Content-Type: multipart/mixed; 
    	boundary="----=_NextPart_1"
    ------=_NextPart_1
    Content-Type: text/plain;
        здесь например текст письма
    ------=_NextPart_1
    Content-Type: image/tiff;
        здесь например вложение
    ------=_NextPart_1 'здесь конец последней части multipart/mixed
------=_NextPart_2 
   здесь должна быть подпись 1-й части
------=_NextPart_2 



А то что я делаю своим кодом (где пытаюсь поставить прозрачную подпись) это бред
и понятно что письмо битое, почтовик ругается и если что-то и отображает то в искаженном виде,
например запихивает текст письма во вложение, отображает подпись как вложение,
а если и показывает подпись, то пишет что подпись недействительна
(как контекст ляжет)
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
Content-Type: multipart/signed;
	boundary="----=_NextPart_1"
------=_NextPart_1
Content-Type: text/plain;
    здесь например текст письма
------=_NextPart_1
Content-Type: image/tiff;
    здесь например вложение
------=_NextPart_1 'здесь конец последней части того что было multipart/mixed
   здесь ставлю подпись непонятно чего, в лучшем случае двух предыдущих кусков
------=_NextPart_1



Т.е. правильно думаю создать новое письмо.
Объявить
Content-Type: multipart/signed
и добавить 2 части
1) то что является первым письмом
2) то что является подписью первой части

С пониманием этого надо попробовать еще раз, думаю CDO справится.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38972243
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну сделал логически правильный код для прозрачной подписи, но с multipart/mixed (вариант 1) подпись битая.
Если из исходного письма выдрать только одну часть (любую) (вариант 2 раскомментировать, вариант 1 закомментировать),
то делает прозрачную подпись корректно.
На входе предполагается письмо с multipart/mixed (например text/plain + вложение).

Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
  Public Function SignMessageClear(oMsg As CDO.Message) As CDO.Message 'CDO.Message
    Dim oSecMsg As New CDO.Message
    Dim oBodyPart As CDO.IBodyPart 'Object ' 
    Dim oBodyPart2 As CDO.IBodyPart 'Object ' 
    Dim oStream As ADODB.Stream

    On Error GoTo handle_error
    oSecMsg.To = oMsg.To
    oSecMsg.From = oMsg.From
    oSecMsg.Subject = oMsg.Subject

    oBodyPart = oSecMsg.BodyPart.AddBodyPart
    Dim data As Byte()
    '=====вариант(1), когда подписываемое сообщение multipart/mixed (и текст либо только вложение)
    '=====структуру рисует правильно, а подпись в итоге битая
    For i As Integer = 1 To oMsg.BodyPart.BodyParts.Count
      oBodyPart2 = oBodyPart.AddBodyPart
      oStream = oMsg.BodyPart.BodyParts(i).GetStream
      oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
      data = oStream.Read
      oStream.Close()
      ' Write the data to the stream and flush it
      oStream = oBodyPart2.GetStream
      oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
      oStream.Write(data)
      oStream.Flush()
      oStream.Close()
    Next
    '=====вариант(1), когда подписываемое сообщение multipart/mixed (и текст либо только вложение)

    '=====вариант(2), когда подписываемое сообщение состоит из одной части (только текст либо только вложение)
    '=====здесь подписывает нормально
    'oStream = oMsg.BodyPart.BodyParts(1).GetStream '
    'oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
    'data = oStream.Read()
    'oStream = oBodyPart.GetStream
    'oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
    'oStream.Write(data)
    'oStream.Flush()
    '=====вариант(2), когда подписываемое сообщение состоит из одной части (только текст либо только вложение)


    ' Attach the signature and let CDO base64 encode it
    oBodyPart = oSecMsg.BodyPart.AddBodyPart
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-type").Value = _
       "application/x-pkcs7-signature" & vbCrLf & "Name = ""smime.p7s"""
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-transfer-encoding").Value = "base64"
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-disposition").Value _
     = "attachment;" & vbCrLf & "FileName=""smime.p7s"""
    oBodyPart.Fields.Update()

    ' Get certificate
    Dim certificate As X509Certificate2 = _
     GetCertForSignature(New MailAddress(oMsg.From).Address, StoreLocation.LocalMachine)

    oStream = oSecMsg.BodyPart.BodyParts(1).GetStream
    oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
    data = oStream.Read
    oStream.Close()
    Dim content As New ContentInfo(data)
    Dim signedCms As New SignedCms(content, True)
    Dim signer As New CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate)
    signedCms.ComputeSignature(signer, True)
    Dim signedbytes As Byte() = signedCms.Encode()

    oStream = oBodyPart.GetDecodedContentStream
    oStream.Type = ADODB.StreamTypeEnum.adTypeBinary

    ' Write the data to the stream and flush it
    oStream.Write(signedbytes)
    oStream.Flush()
    oStream.Close()

    oSecMsg.Fields.Item("urn:schemas:mailheader:content-type").Value _
     = "multipart/signed;" & vbCrLf & "protocol=""application/x-pkcs7-signature"";" & _
     vbCrLf & "micalg=SHA1;"
    oSecMsg.Fields.Update()

    oSecMsg.Configuration = oMsg.Configuration

    GoTo cleanup

    ' Report error
handle_error:
    MsgBox(Err.Number & ": " & Err.Description, , "Error:")
    oSecMsg.Attachments.DeleteAll()
    oSecMsg = Nothing

    ' Clean up memory
cleanup:
    oBodyPart = Nothing
    oStream = Nothing

    ' Return new message
    SignMessageClear = oSecMsg
  End Function



Ощущение такое, что как только добавить вторую часть с подписью первой, то CDO автоматом переписывает и первую (подписанную часть, одного байта достаточно) если она сама содержит вложенные части (сама является multipart), причем побороть похоже никак. Если первая (подписываемая) часть не имеет вложенности, то тогда трюк проходит.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38972260
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Идиотизм конечно,
CDO в последний момент выкидывает из уже подписанной multipart/mixed части строчку:
Код: vbnet
1.
This is a multi-part message in MIME format. + CRLF



Если сделать вот так:
Код: vbnet
1.
2.
3.
4.
5.
6.
    Dim strContent As String = oSecMsg.BodyPart.BodyParts(1).GetStream.ReadText
    strContent = Replace(strContent, "This is a multi-part message in MIME format." & vbCrLf, "", , 1)
    data = Encoding.GetEncoding(1252).GetBytes(strContent)
    'oStream = oSecMsg.BodyPart.BodyParts(1).GetStream
    'oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
    'data = oStream.Read


то код становится рабочим и подпись верна.
Код: vbnet
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
  Public Function SignMessageClear(oMsg As CDO.Message) As CDO.Message 'CDO.Message
    Dim oSecMsg As New CDO.Message
    Dim oBodyPart As CDO.IBodyPart 'Object ' 
    Dim oBodyPart2 As CDO.IBodyPart 'Object ' 
    Dim oStream As ADODB.Stream

    On Error GoTo handle_error
    oSecMsg.To = oMsg.To
    oSecMsg.From = oMsg.From
    oSecMsg.Subject = oMsg.Subject

    oBodyPart = oSecMsg.BodyPart.AddBodyPart
    Dim data As Byte()
    '=====вариант(1), когда подписываемое сообщение multipart/mixed (и текст и вложение)
     For i As Integer = 1 To oMsg.BodyPart.BodyParts.Count
      oBodyPart2 = oBodyPart.AddBodyPart
      oStream = oMsg.BodyPart.BodyParts(i).GetStream
      oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
      data = oStream.Read
      oStream.Close()
      ' Write the data to the stream and flush it
      oStream = oBodyPart2.GetStream
      oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
      oStream.Write(data)
      oStream.Flush()
      oStream.Close()
    Next
    '=====вариант(1), когда подписываемое сообщение multipart/mixed (и текст и вложение)

    '=====вариант(2), когда подписываемое сообщение состоит из одной части (только текст либо только вложение)
    '=====здесь подписывает нормально
    'oStream = oMsg.BodyPart.BodyParts(1).GetStream '
    'oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
    'data = oStream.Read()
    'oStream = oBodyPart.GetStream
    'oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
    'oStream.Write(data)
    'oStream.Flush()
    '=====вариант(2), когда подписываемое сообщение состоит из одной части (только текст либо только вложение)


    ' Attach the signature and let CDO base64 encode it
    oBodyPart = oSecMsg.BodyPart.AddBodyPart
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-type").Value = _
       "application/x-pkcs7-signature" & vbCrLf & "Name = ""smime.p7s"""
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-transfer-encoding").Value = "base64"
    oBodyPart.Fields.Item("urn:schemas:mailheader:content-disposition").Value _
     = "attachment;" & vbCrLf & "FileName=""smime.p7s"""
    oBodyPart.Fields.Update()

    ' Get certificate
    Dim certificate As X509Certificate2 = _
     GetCertForSignature(New MailAddress(oMsg.From).Address, StoreLocation.LocalMachine)

    Dim strContent As String = oSecMsg.BodyPart.BodyParts(1).GetStream.ReadText
    strContent = Replace(strContent, "This is a multi-part message in MIME format." & vbCrLf, "", , 1)
    data = Encoding.GetEncoding(1252).GetBytes(strContent)
    'oStream = oSecMsg.BodyPart.BodyParts(1).GetStream
    'oStream.Type = ADODB.StreamTypeEnum.adTypeBinary
    'data = oStream.Read
    'oStream.Close()
    IO.File.WriteAllBytes("original.txt", data)
    Dim content As New ContentInfo(data)
    Dim signedCms As New SignedCms(content, True)
    Dim signer As New CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate)
    signedCms.ComputeSignature(signer, True)
    Dim signedbytes As Byte() = signedCms.Encode()

    oStream = oBodyPart.GetDecodedContentStream
    oStream.Type = ADODB.StreamTypeEnum.adTypeBinary

    ' Write the data to the stream and flush it
    oStream.Write(signedbytes)
    oStream.Flush()
    oStream.Close()

    oSecMsg.Fields.Item("urn:schemas:mailheader:content-type").Value _
     = "multipart/signed;" & vbCrLf & "protocol=""application/x-pkcs7-signature"";" & _
     vbCrLf & "micalg=SHA1;"
    oSecMsg.Fields.Update()

    oSecMsg.Configuration = oMsg.Configuration

    GoTo cleanup

    ' Report error
handle_error:
    MsgBox(Err.Number & ": " & Err.Description, , "Error:")
    oSecMsg.Attachments.DeleteAll()
    oSecMsg = Nothing

    ' Clean up memory
cleanup:
    oBodyPart = Nothing
    oStream = Nothing

    ' Return new message
    SignMessageClear = oSecMsg
  End Function


Но м.б. есть способ выкинуть эту фразу еще до подписывания.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38972263
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77Но м.б. есть способ выкинуть эту фразу еще до подписывания.
Не никак. Она даже после .Send там остается (если выковыривать через oSecMsg.BodyPart.BodyParts(1).GetStream.ReadText) , а приходит без нее.
и если сделать oSecMsg.GetStream.SaveToFile("full.eml"), то там ее тоже нет.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38972265
Фотография Axeleron
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Мляха, когда уже VB.NET уберут из Студии
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38972266
Дмитрий77
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
AxeleronМляха, когда уже VB.NET уберут из Студии
Если б ты по делу мыслишек подкинул с приложением кода из своего шарпа, это было бы интереснее.
...
Рейтинг: 0 / 0
А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
    #38972267
Фотография Axeleron
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Дмитрий77Если б ты по делу мыслишек подкинул с приложением кода из своего шарпа, это было бы интереснее.
Пятница, вечер (поздний), все мыслишки остались размазаны по рабочему столу... Прости
...
Рейтинг: 0 / 0
20 сообщений из 20, страница 1 из 1
Форумы / WinForms, .Net Framework [игнор отключен] [закрыт для гостей] / А как используя Net.Mail и CDO.Message подписать отправляемое мыло цифровой подписью?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]