├── NOTICE.txt
├── doc
├── icons
│ ├── TimeStampClient.ico
│ ├── TimeStampClient.png
│ └── TimeStampClient.svg
└── images
│ └── screenshot-windows.png
├── src
├── TimeStampClient
│ ├── TimeStampClient.snk
│ ├── PkiStatus.cs
│ ├── Utils.cs
│ ├── Oid.cs
│ ├── TsaId.cs
│ ├── PkiFailureInfo.cs
│ ├── TimeStampClient.csproj
│ ├── UserCredentials.cs
│ ├── MsgImprint.cs
│ ├── DigestUtils.cs
│ ├── TimeStampToken.cs
│ ├── TimeStampException.cs
│ ├── Request.cs
│ ├── Response.cs
│ └── Client.cs
├── TimeStampClientCmd
│ ├── TimeStampClient.ico
│ ├── TimeStampClientCmd.csproj
│ ├── Program.cs
│ └── SharedUtils.cs
├── TimeStampClientGui.Windows
│ ├── TimeStampClient.ico
│ ├── app.config
│ ├── Program.cs
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── ComboItemHash.cs
│ ├── ComboItemFormat.cs
│ ├── ComboItemCertReq.cs
│ ├── XmlSerializer.cs
│ ├── ClientSettings.cs
│ ├── TimeStampClientGui.Windows.csproj
│ ├── AboutDialog.cs
│ └── MainForm.cs
├── TimeStampClientGui.MacOs
│ ├── TimeStampClientGui.icns
│ ├── Info.plist
│ ├── Program.cs
│ └── TimeStampClientGui.MacOs.csproj
├── TimeStampClientGui.Linux
│ ├── Program.cs
│ └── TimeStampClientGui.Linux.csproj
└── DisigTimeStamp.sln
├── .gitignore
├── .gitattributes
├── README.md
└── LICENSE.txt
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | This product includes software developed by Disig a.s. (http://www.disig.sk).
--------------------------------------------------------------------------------
/doc/icons/TimeStampClient.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/disig/TimeStampClient/HEAD/doc/icons/TimeStampClient.ico
--------------------------------------------------------------------------------
/doc/icons/TimeStampClient.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/disig/TimeStampClient/HEAD/doc/icons/TimeStampClient.png
--------------------------------------------------------------------------------
/doc/images/screenshot-windows.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/disig/TimeStampClient/HEAD/doc/images/screenshot-windows.png
--------------------------------------------------------------------------------
/src/TimeStampClient/TimeStampClient.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/disig/TimeStampClient/HEAD/src/TimeStampClient/TimeStampClient.snk
--------------------------------------------------------------------------------
/src/TimeStampClientCmd/TimeStampClient.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/disig/TimeStampClient/HEAD/src/TimeStampClientCmd/TimeStampClient.ico
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/TimeStampClient.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/disig/TimeStampClient/HEAD/src/TimeStampClientGui.Windows/TimeStampClient.ico
--------------------------------------------------------------------------------
/src/TimeStampClientGui.MacOs/TimeStampClientGui.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/disig/TimeStampClient/HEAD/src/TimeStampClientGui.MacOs/TimeStampClientGui.icns
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Bb]in/
2 | [Oo]bj/
3 | *.userprefs
4 | test-results/
5 | TestResults/
6 | src/.vs/*
7 | *.suo
8 | *.user
9 | **/packages/*
10 | /build/nuget-unsigned/
11 | /build/nuget-signed/
12 | /build/TimeStampClientCmd/
13 | /build/TimeStampClientGui.Linux/
14 | /build/TimeStampClientGui.MacOs.app/
15 | /build/TimeStampClientGui.Windows/
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set default behaviour, in case users don't have core.autocrlf set.
2 | * text=auto
3 |
4 | # Explicitly declare text files we want to always be normalized and converted
5 | # to native line endings on checkout.
6 | *.cs text
7 | *.txt text
8 | *.config text
9 |
10 | # Declare files that will always have CRLF line endings on checkout.
11 | *.sln text eol=crlf
12 | *.csproj text eol=crlf
13 |
14 | # Denote all files that are truly binary and should not be modified.
15 | *.dll binary
16 |
--------------------------------------------------------------------------------
/src/TimeStampClientCmd/TimeStampClientCmd.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 | Disig.TimeStampClient.Cmd
7 | TimeStampClient.ico
8 | TimeStampClient
9 | 1.2.0
10 | Disig a.s.
11 | Copyright 2016-2021 Disig a.s.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.MacOs/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleName
6 | TimeStampClientGui
7 | CFBundleIdentifier
8 | sk.disig.opensource.TimeStampClientGui
9 | CFBundleShortVersionString
10 | 1.2.0
11 | LSMinimumSystemVersion
12 | 10.12
13 | CFBundleDevelopmentRegion
14 | en
15 | NSHumanReadableCopyright
16 | Copyright (c) 2016-2021 Disig a.s.
17 | CFBundleIconFile
18 | TimeStampClientGui.icns
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Linux/Program.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Jaroslav IMRICH
20 | */
21 |
22 | using System;
23 | using Eto.Forms;
24 |
25 | namespace Disig.TimeStampClient.Gui
26 | {
27 | class MainClass
28 | {
29 | [STAThread]
30 | public static void Main(string[] args)
31 | {
32 | new Application(Eto.Platforms.Gtk).Run(new MainForm());
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.MacOs/Program.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Jaroslav IMRICH
20 | */
21 |
22 | using System;
23 | using Eto.Forms;
24 |
25 | namespace Disig.TimeStampClient.Gui
26 | {
27 | class MainClass
28 | {
29 | [STAThread]
30 | public static void Main(string[] args)
31 | {
32 | new Application(Eto.Platforms.Mac64).Run(new MainForm());
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/Program.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 | using Eto.Forms;
24 |
25 | namespace Disig.TimeStampClient.Gui
26 | {
27 | public sealed class Program
28 | {
29 | [STAThread]
30 | public static void Main()
31 | {
32 | // TODO: unhandled exceptions: Eto.Forms.Application.UnhandledException
33 | new Application(Eto.Platforms.Wpf).Run(new MainForm());
34 | }
35 |
36 | private Program()
37 | {
38 |
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle("TimeStampClientGui")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("Disig a.s.")]
11 | [assembly: AssemblyProduct("TimeStampClient")]
12 | [assembly: AssemblyCopyright("Copyright 2016-2021 Disig a.s.")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | [assembly: AssemblyVersion("1.2.0")]
21 | [assembly: AssemblyFileVersion("1.2.0")]
22 | [assembly: ComVisible(false)]
23 |
24 | // The following attributes are used to specify the signing key for the assembly,
25 | // if desired. See the Mono documentation for more information about signing.
26 |
27 | //[assembly: AssemblyDelaySign(false)]
28 | //[assembly: AssemblyKeyFile("")]
29 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/ComboItemHash.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using Eto.Forms;
23 |
24 | namespace Disig.TimeStampClient.Gui
25 | {
26 | internal class ComboItemHash : IListItem
27 | {
28 | public ComboItemHash(string text, Oid value)
29 | {
30 | this.Text = text;
31 | this.Value = value;
32 | this.Key = this.Text;
33 | }
34 |
35 | public string Key
36 | {
37 | get;
38 | private set;
39 | }
40 |
41 | public string Text
42 | {
43 | get;
44 | set;
45 | }
46 |
47 | public Oid Value
48 | {
49 | get;
50 | set;
51 | }
52 |
53 | public override string ToString()
54 | {
55 | return this.Text;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/ComboItemFormat.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using Eto.Forms;
23 |
24 | namespace Disig.TimeStampClient.Gui
25 | {
26 | internal class ComboItemFormat : IListItem
27 | {
28 | public ComboItemFormat(string text, SharedUtils.ResultFormat format)
29 | {
30 | this.Text = text;
31 | this.Key = format.ToString();
32 | this.Format = format;
33 | }
34 |
35 | public SharedUtils.ResultFormat Format
36 | {
37 | get;
38 | set;
39 | }
40 |
41 | public string Key
42 | {
43 | get;
44 | private set;
45 | }
46 |
47 | public string Text
48 | {
49 | get;
50 | set;
51 | }
52 |
53 | public override string ToString()
54 | {
55 | return this.Text;
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/ComboItemCertReq.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using Eto.Forms;
23 |
24 | namespace Disig.TimeStampClient.Gui
25 | {
26 | internal class ComboItemCertReq : IListItem
27 | {
28 | public ComboItemCertReq(bool value)
29 | {
30 | this.Value = value;
31 |
32 | if (value)
33 | {
34 | this.Key = true.ToString();
35 | this.Text = "Yes";
36 | }
37 | else
38 | {
39 | this.Key = false.ToString();
40 | this.Text = "No";
41 | }
42 | }
43 |
44 | public bool Value
45 | {
46 | get;
47 | set;
48 | }
49 |
50 | public string Key
51 | {
52 | get;
53 | private set;
54 | }
55 |
56 | public string Text
57 | {
58 | get;
59 | set;
60 | }
61 |
62 | public override string ToString()
63 | {
64 | return this.Text;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Linux/TimeStampClientGui.Linux.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net5.0
6 | Disig.TimeStampClient.Gui
7 | TimeStampClient
8 | 1.2.0
9 | Disig a.s.
10 | Copyright 2016-2021 Disig a.s.
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 |
--------------------------------------------------------------------------------
/src/TimeStampClient/PkiStatus.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | namespace Disig.TimeStampClient
23 | {
24 | ///
25 | /// PKI statuses according to RFC 3161
26 | ///
27 | public enum PkiStatus
28 | {
29 | ///
30 | /// When the PKIStatus contains the value zero a TimeStampToken, as requested, is present.
31 | ///
32 | Granted = 0,
33 |
34 | ///
35 | /// When the PKIStatus contains the value one a TimeStampToken, with modifications, is present.
36 | ///
37 | GrantedWithMods = 1,
38 |
39 | ///
40 | /// When the PKIStatus contains the value two a TimeStamp request was rejected.
41 | ///
42 | Rejection = 2,
43 |
44 | ///
45 | /// The request body part has not yet been processed, expect to hear more later.
46 | ///
47 | Waiting = 3,
48 |
49 | ///
50 | /// A warning that a revocation is imminent.
51 | ///
52 | RevocationWarning = 4,
53 |
54 | ///
55 | /// Revocation has occurred.
56 | ///
57 | RevocationNotification = 5
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/TimeStampClient/Utils.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | namespace Disig.TimeStampClient
23 | {
24 | ///
25 | /// Utils functions.
26 | ///
27 | internal static class Utils
28 | {
29 | ///
30 | /// Compares byte arrays.
31 | ///
32 | /// first byte array
33 | /// second byte array
34 | /// true if arrays are identical. Otherwise returns false.
35 | internal static bool CompareByteArray(byte[] a, byte[] b)
36 | {
37 | if (null == a && null == b)
38 | {
39 | return true;
40 | }
41 |
42 | if ((null == a && null != b) || (null != a && null == b))
43 | {
44 | return false;
45 | }
46 |
47 | if (a.Length != b.Length)
48 | {
49 | return false;
50 | }
51 |
52 | int i = 0;
53 | while (i < a.Length && a[i] == b[i])
54 | {
55 | i++;
56 | }
57 |
58 | if (i != a.Length)
59 | {
60 | return false;
61 | }
62 |
63 | return true;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.MacOs/TimeStampClientGui.MacOs.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 | Disig.TimeStampClient.Gui
7 | osx-x64
8 | TimeStampClient
9 | 1.2.0
10 | Disig a.s.
11 | Copyright 2016-2021 Disig a.s.
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 |
--------------------------------------------------------------------------------
/src/TimeStampClient/Oid.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System.Collections.Generic;
23 |
24 | namespace Disig.TimeStampClient
25 | {
26 | ///
27 | /// Object identifiers.
28 | ///
29 | public sealed class Oid
30 | {
31 | ///
32 | /// Object identifier for the SHA1 algorithm.
33 | ///
34 | public static readonly Oid SHA1 = new Oid("1.3.14.3.2.26");
35 |
36 | ///
37 | /// Object identifier for the SHA512 algorithm.
38 | ///
39 | public static readonly Oid SHA512 = new Oid("2.16.840.1.101.3.4.2.3");
40 |
41 | ///
42 | /// Object identifier for the MD5 algorithm.
43 | ///
44 | public static readonly Oid MD5 = new Oid("1.2.840.113549.2.5");
45 |
46 | ///
47 | /// Object identifier for the SHA256 algorithm.
48 | ///
49 | public static readonly Oid SHA256 = new Oid("2.16.840.1.101.3.4.2.1");
50 |
51 | ///
52 | /// Initializes a new instance of the class.
53 | ///
54 | /// Object identifier.
55 | private Oid(string oid)
56 | {
57 | this.OID = oid;
58 | }
59 |
60 | ///
61 | /// Gets object identifier.
62 | ///
63 | public string OID
64 | {
65 | get;
66 | private set;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/TimeStampClient/TsaId.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System.Security.Cryptography.X509Certificates;
23 | using Org.BouncyCastle.Cms;
24 |
25 | namespace Disig.TimeStampClient
26 | {
27 | ///
28 | /// Stores available information about TSA.
29 | ///
30 | public class TsaId
31 | {
32 | internal TsaId(SignerID signerID, X509Certificate2 cert)
33 | {
34 | this.TsaCertSerialNumber = signerID.SerialNumber.ToByteArray();
35 | this.TsaCertSubjectKeyIdentifier = signerID.SubjectKeyIdentifier;
36 | this.TsaCertIssuerName = new X500DistinguishedName(signerID.Issuer.GetEncoded());
37 |
38 | if (null != cert)
39 | TsaCert = cert;
40 | }
41 |
42 | ///
43 | ///
44 | ///
45 | public X500DistinguishedName TsaCertIssuerName
46 | {
47 | get;
48 | private set;
49 | }
50 |
51 | ///
52 | ///
53 | ///
54 | public byte[] TsaCertSerialNumber
55 | {
56 | get;
57 | private set;
58 | }
59 |
60 | ///
61 | ///
62 | ///
63 | public byte[] TsaCertSubjectKeyIdentifier
64 | {
65 | get;
66 | private set;
67 | }
68 |
69 | ///
70 | /// Signing certificate of a TSA.
71 | ///
72 | public X509Certificate2 TsaCert
73 | {
74 | get;
75 | private set;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/TimeStampClient/PkiFailureInfo.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | namespace Disig.TimeStampClient
23 | {
24 | ///
25 | /// Reason why the time-stamp request was rejected.
26 | ///
27 | public enum PkiFailureInfo
28 | {
29 | ///
30 | /// Unrecognized or unsupported Algorithm Identifier.
31 | ///
32 | BadAlg = 0,
33 |
34 | ///
35 | /// Transaction not permitted or supported.
36 | ///
37 | BadRequest = 2,
38 |
39 | ///
40 | /// The data submitted has the wrong format.
41 | ///
42 | BadDataFormat = 5,
43 |
44 | ///
45 | /// The TSA's time source is not available.
46 | ///
47 | TimeNotAvailable = 14,
48 |
49 | ///
50 | /// The requested TSA policy is not supported by the TSA.
51 | ///
52 | UnacceptedPolicy = 15,
53 |
54 | ///
55 | /// The requested extension is not supported by the TSA.
56 | ///
57 | UnacceptedExtension = 16,
58 |
59 | ///
60 | /// The additional information requested could not be understood or is not available.
61 | ///
62 | AddInfoNotAvailable = 17,
63 |
64 | ///
65 | /// The request cannot be handled due to system failure.
66 | ///
67 | SystemFailure = 25,
68 |
69 | ///
70 | /// Unknown status.
71 | ///
72 | Unknown = -1
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/TimeStampClient/TimeStampClient.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net45;netstandard2.0
5 | true
6 | true
7 | true
8 | true
9 | snupkg
10 | true
11 | true
12 | true
13 |
14 |
15 |
16 | Disig a.s.
17 | TimeStampClient
18 | 1.2.0
19 | TimeStampClient
20 | Copyright 2016-2021 Disig a.s.
21 | TimeStampClient.LICENSE.txt
22 | https://github.com/disig/TimeStampClient
23 | TimeStampClient.png
24 | https://github.com/disig/TimeStampClient.git
25 | git
26 | trusted timestamp time-stamp trust time stamp security crypto cryptography
27 | RFC 3161 time-stamping client library
28 | Disig a.s.
29 |
30 |
31 |
32 | true
33 | TimeStampClient.snk
34 | false
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/XmlSerializer.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System.IO;
23 | using System.Runtime.Serialization;
24 | using System.Text;
25 | using System.Xml;
26 |
27 | namespace Disig.TimeStampClient.Gui
28 | {
29 | public static class XmlSerializer
30 | {
31 | public static T Deserialize(Stream inputStream)
32 | {
33 | XmlReaderSettings xmlReaderSettings = new XmlReaderSettings()
34 | {
35 | CloseInput = false,
36 | ConformanceLevel = ConformanceLevel.Document,
37 | DtdProcessing = DtdProcessing.Prohibit,
38 | ValidationType = ValidationType.None
39 | };
40 |
41 | using (XmlReader xmlReader = XmlReader.Create(inputStream, xmlReaderSettings))
42 | {
43 | DataContractSerializer dataContractSerializer = new DataContractSerializer(typeof(T));
44 | return (T)dataContractSerializer.ReadObject(xmlReader);
45 | }
46 | }
47 |
48 | public static void Serialize(object o, Stream outputStream)
49 | {
50 | if (o == null)
51 | {
52 | return;
53 | }
54 | XmlWriterSettings xmlWriterSettings = new System.Xml.XmlWriterSettings()
55 | {
56 | CloseOutput = false,
57 | Encoding = new UTF8Encoding(false, true),
58 | OmitXmlDeclaration = false,
59 | Indent = true
60 | };
61 |
62 | using (XmlWriter xmlWriter = XmlWriter.Create(outputStream, xmlWriterSettings))
63 | {
64 | DataContractSerializer dataContractSerializer = new DataContractSerializer(o.GetType());
65 | dataContractSerializer.WriteObject(xmlWriter, o);
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/ClientSettings.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System.Runtime.Serialization;
23 |
24 | namespace Disig.TimeStampClient.Gui
25 | {
26 | [DataContract(Namespace = "http://disig.sk/TimeStampClient/1.0.0")]
27 | public class ClientSettings
28 | {
29 | [DataMember]
30 | public string Nonce
31 | {
32 | get;
33 | set;
34 | }
35 |
36 | [DataMember]
37 | public string HashAlgorithm
38 | {
39 | get;
40 | set;
41 | }
42 |
43 | [DataMember]
44 | public string Policy
45 | {
46 | get;
47 | set;
48 | }
49 |
50 | [DataMember]
51 | public bool CertReq
52 | {
53 | get;
54 | set;
55 | }
56 |
57 | [DataMember]
58 | public string SourceFile
59 | {
60 | get;
61 | set;
62 | }
63 |
64 | [DataMember]
65 | public string ServerAddress
66 | {
67 | get;
68 | set;
69 | }
70 |
71 | [DataMember]
72 | public string UserCert
73 | {
74 | get;
75 | set;
76 | }
77 |
78 | [DataMember]
79 | public string UserCertPassword
80 | {
81 | get;
82 | set;
83 | }
84 |
85 | [DataMember]
86 | public string UserName
87 | {
88 | get;
89 | set;
90 | }
91 |
92 | [DataMember]
93 | public string UserPassword
94 | {
95 | get;
96 | set;
97 | }
98 |
99 | [DataMember]
100 | public SharedUtils.ResultFormat Format
101 | {
102 | get;
103 | set;
104 | }
105 |
106 | [DataMember]
107 | public string OutFile
108 | {
109 | get;
110 | set;
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/TimeStampClient/UserCredentials.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System.Net;
23 | using System.Security.Cryptography.X509Certificates;
24 |
25 | namespace Disig.TimeStampClient
26 | {
27 | ///
28 | /// User credentials class.
29 | ///
30 | public class UserCredentials
31 | {
32 | ///
33 | /// Initializes a new instance of the class.
34 | ///
35 | /// User's certificate to access a TSA service.
36 | public UserCredentials(X509Certificate2 httpCredentials)
37 | {
38 | this.UserSslCert = httpCredentials;
39 | }
40 |
41 | ///
42 | /// Initializes a new instance of the class.
43 | ///
44 | /// User's network credential to access a TSA service.
45 | public UserCredentials(NetworkCredential httpCredentials)
46 | {
47 | this.HttpCredentials = httpCredentials;
48 | }
49 |
50 | ///
51 | /// Initializes a new instance of the class.
52 | ///
53 | /// User's certificate to access a TSA service.
54 | /// User's network credential to access a TSA service.
55 | public UserCredentials(X509Certificate2 userSslCert, NetworkCredential httpCredentials)
56 | : this(httpCredentials)
57 | {
58 | this.UserSslCert = userSslCert;
59 | }
60 |
61 | ///
62 | /// Gets user's certificate.
63 | ///
64 | public X509Certificate2 UserSslCert
65 | {
66 | get;
67 | private set;
68 | }
69 |
70 | ///
71 | /// Gets user's network credential.
72 | ///
73 | public NetworkCredential HttpCredentials
74 | {
75 | get;
76 | private set;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/DisigTimeStamp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30907.101
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimeStampClient", "TimeStampClient\TimeStampClient.csproj", "{41A8CFEB-6DE0-49D3-84B6-4ACA04459A92}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeStampClientCmd", "TimeStampClientCmd\TimeStampClientCmd.csproj", "{48621175-3DB7-49C2-8857-99379B0C5B27}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TimeStampClientGui.Windows", "TimeStampClientGui.Windows\TimeStampClientGui.Windows.csproj", "{9C02275A-4665-428B-9BFA-DF1D70C320F3}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimeStampClientGui.Linux", "TimeStampClientGui.Linux\TimeStampClientGui.Linux.csproj", "{7326BCB6-263E-430F-B0D5-71B442E87A54}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TimeStampClientGui.MacOs", "TimeStampClientGui.MacOs\TimeStampClientGui.MacOs.csproj", "{EE454584-D145-4390-8B16-1D079AA70695}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Release|Any CPU = Release|Any CPU
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {41A8CFEB-6DE0-49D3-84B6-4ACA04459A92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {41A8CFEB-6DE0-49D3-84B6-4ACA04459A92}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {41A8CFEB-6DE0-49D3-84B6-4ACA04459A92}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {41A8CFEB-6DE0-49D3-84B6-4ACA04459A92}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {48621175-3DB7-49C2-8857-99379B0C5B27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {48621175-3DB7-49C2-8857-99379B0C5B27}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {48621175-3DB7-49C2-8857-99379B0C5B27}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {48621175-3DB7-49C2-8857-99379B0C5B27}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {9C02275A-4665-428B-9BFA-DF1D70C320F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {9C02275A-4665-428B-9BFA-DF1D70C320F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {9C02275A-4665-428B-9BFA-DF1D70C320F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {9C02275A-4665-428B-9BFA-DF1D70C320F3}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {7326BCB6-263E-430F-B0D5-71B442E87A54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {7326BCB6-263E-430F-B0D5-71B442E87A54}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {7326BCB6-263E-430F-B0D5-71B442E87A54}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {7326BCB6-263E-430F-B0D5-71B442E87A54}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {EE454584-D145-4390-8B16-1D079AA70695}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {EE454584-D145-4390-8B16-1D079AA70695}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {EE454584-D145-4390-8B16-1D079AA70695}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {EE454584-D145-4390-8B16-1D079AA70695}.Release|Any CPU.Build.0 = Release|Any CPU
42 | EndGlobalSection
43 | GlobalSection(SolutionProperties) = preSolution
44 | HideSolutionNode = FALSE
45 | EndGlobalSection
46 | GlobalSection(ExtensibilityGlobals) = postSolution
47 | SolutionGuid = {727035AF-C1C2-4F8E-B590-D83D1E7666A3}
48 | EndGlobalSection
49 | EndGlobal
50 |
--------------------------------------------------------------------------------
/src/TimeStampClient/MsgImprint.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 |
24 | namespace Disig.TimeStampClient
25 | {
26 | ///
27 | /// Encapsulates the hashed message and the hash algorithm identifier.
28 | ///
29 | internal class MsgImprint
30 | {
31 | ///
32 | /// Initializes a new instance of the class.
33 | ///
34 | /// The hashed message.
35 | /// The hash algorithm identifier.
36 | internal MsgImprint(byte[] hashedMessage, string hashAlgOid)
37 | {
38 | this.HashedMessage = hashedMessage;
39 | this.HashAlgorithm = hashAlgOid;
40 | }
41 |
42 | ///
43 | /// Initializes a new instance of the class.
44 | ///
45 | /// The hashed message.
46 | /// The hash algorithm identifier.
47 | internal MsgImprint(byte[] hashedMessage, Oid oid)
48 | {
49 | this.HashAlgorithm = oid.OID;
50 | this.HashedMessage = hashedMessage;
51 | }
52 |
53 | ///
54 | /// Gets the hash identifier.
55 | ///
56 | internal string HashAlgorithm
57 | {
58 | get;
59 | private set;
60 | }
61 |
62 | ///
63 | /// Gets the The hashed message.
64 | ///
65 | internal byte[] HashedMessage
66 | {
67 | get;
68 | private set;
69 | }
70 |
71 | ///
72 | /// Compares two message imprints.
73 | ///
74 | /// The first message imprint.
75 | /// The second message imprint.
76 | /// True if message imprints are identical, otherwise false.
77 | internal static bool CompareImprints(MsgImprint a, MsgImprint b)
78 | {
79 | if ((null == a) && (null == b))
80 | {
81 | return true;
82 | }
83 |
84 | if ((null == a && null != b) || (null != a && null == b))
85 | {
86 | return false;
87 | }
88 |
89 | if (0 != string.CompareOrdinal(a.HashAlgorithm, b.HashAlgorithm))
90 | {
91 | return false;
92 | }
93 |
94 | if (false == Utils.CompareByteArray(a.HashedMessage, b.HashedMessage))
95 | {
96 | return false;
97 | }
98 |
99 | return true;
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/TimeStampClientGui.Windows.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | {9C02275A-4665-428B-9BFA-DF1D70C320F3}
7 | WinExe
8 | Disig.TimeStampClient.Gui
9 | TimeStampClientGui
10 | v4.5.2
11 | true
12 |
13 |
14 |
15 |
16 |
17 | true
18 | bin\Debug\
19 | DEBUG;
20 | full
21 | AnyCPU
22 | prompt
23 | true
24 | AllRules.ruleset
25 |
26 |
27 | bin\Release\
28 | true
29 | full
30 | AnyCPU
31 | prompt
32 | true
33 |
34 |
35 |
36 |
37 |
38 | TimeStampClient.ico
39 |
40 |
41 | true
42 |
43 |
44 | true
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 | SharedUtils.cs
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | {41a8cfeb-6de0-49d3-84b6-4aca04459a92}
75 | TimeStampClient
76 |
77 |
78 |
79 |
80 | 1.15.0
81 |
82 |
83 | 2.4.1
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TimeStampClient
2 | Easy to use .NET RFC 3161 time-stamp client library and applications based on [Bouncy Castle library](https://www.bouncycastle.org/).
3 |
4 | TimeStampClient:
5 | * runs on Windows, Linux and macOS
6 | * supports Time-Stamp Protocol over HTTP/HTTPS and TCP
7 | * supports SSL authentication (client certificate) and HTTP basic authentication (login and password)
8 | * uses system HTTP proxy settings
9 | * contains GUI application
10 | * contains command line application
11 | * contains reusable .NET library
12 |
13 | ## Download
14 |
15 | There are multiple [release artifacts](https://github.com/disig/TimeStampClient/releases/latest) available:
16 |
17 | * `TimeStampClient.nupkg` - NuGet package with reusable .NET library
18 | * `TimeStampClient.snupkg` - NuGet package with debug symbols for reusable .NET library
19 | * `TimeStampClientCmd.zip` - Command line tool usable on Windows, Linux and macOS
20 | * `TimeStampClientGui.Linux.zip` - GUI application usable on Linux
21 | * `TimeStampClientGui.MacOs.zip` - GUI application usable on macOS
22 | * `TimeStampClientGui.Windows.zip` - GUI application usable on Windows
23 |
24 | Official [NuGet package](https://www.nuget.org/packages/TimeStampClient/) is published in nuget.org repository.
25 |
26 | ## Usage
27 | ### TimeStampClient Library
28 |
29 | In most cases you just need the following single line of code to get a time-stamp.
30 | ```csharp
31 | var timeStampToken = Disig.TimeStampClient.TimeStampClient.RequestTimeStampToken("http://localhost/tsa", "document.docx");
32 | ```
33 |
34 | ### TimeStampClient command line application
35 | [.NET 5.0 runtime](https://dotnet.microsoft.com/download/dotnet/5.0) is required on all platforms in order to use `TimeStampClientCmd` application.
36 |
37 | Getting time-stamp using the command line application on Windows:
38 | ```
39 | > TimeStampClientCmd.exe --tsa http://localhost/tsa --file document.docx --out token.tst
40 | ```
41 |
42 | Getting time-stamp using the command line application on Linux and macOS:
43 | ```
44 | $ dotnet ./TimeStampClientCmd.dll --tsa http://localhost/tsa --file document.docx --out token.tst
45 | ```
46 |
47 | ### TimeStampClient GUI application
48 |
49 | On Windows:
50 | - Extract `TimeStampClientGui.Windows.zip` archive
51 | - Start the application by double-clicking on `TimeStampClientGui.exe` file
52 |
53 | On Linux:
54 | - Install [.NET 5.0 runtime](https://dotnet.microsoft.com/download/dotnet/5.0)
55 | - Extract `TimeStampClientGui.Linux.zip` archive
56 | - Start the application with the following command:
57 | ```
58 | $ dotnet ./TimeStampClientGui.Linux.dll
59 | ```
60 |
61 | On macOS:
62 | - Install [.NET 5.0 runtime](https://dotnet.microsoft.com/download/dotnet/5.0)
63 | - Extract `TimeStampClientGui.MacOs.zip` archive
64 | - Execute following commands from command line:
65 | ```sh
66 | $ chmod +x TimeStampClientGui.MacOs.app/Contents/MacOS/TimeStampClientGui.MacOs
67 | $ xattr -c TimeStampClientGui.MacOs.app
68 | ```
69 | - Start the application by double-clicking on its icon
70 |
71 | User needs to specify the URL of a time-stamping authority in the "TSA service URL" field and the path to a file to be time-stamped in the "File to time-stamp" field. After clicking on the "Request time-stamp" button the time-stamp token is saved to the file specified in the "Output file" field.
72 |
73 | 
74 |
75 | ## License
76 | TimeStampClient library and applications are available under the terms of the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).
77 | [Human friendly license summary](https://tldrlegal.com/l/apache2) is available at tldrlegal.com but the [full license text](LICENSE.txt) always prevails.
78 |
79 | ## About
80 | TimeStampClient library and applications are provided by [Disig a.s.](https://www.disig.sk/)
81 |
--------------------------------------------------------------------------------
/src/TimeStampClient/DigestUtils.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System.IO;
23 | using System.Security.Cryptography;
24 |
25 | namespace Disig.TimeStampClient
26 | {
27 | ///
28 | /// Digest helper methods.
29 | ///
30 | public static class DigestUtils
31 | {
32 | ///
33 | /// Computes digest of the data.
34 | ///
35 | /// Data stream to compute digest for.
36 | /// Defines hash algorithm to be used to compute digest.
37 | /// digest stored in a byte array
38 | public static byte[] ComputeDigest(Stream dataStream, Oid digestType)
39 | {
40 | HashAlgorithm digestAlg = null;
41 | try
42 | {
43 | if (Oid.MD5 == digestType)
44 | {
45 | digestAlg = new MD5CryptoServiceProvider();
46 | }
47 | else if (Oid.SHA1 == digestType)
48 | {
49 | digestAlg = new SHA1CryptoServiceProvider();
50 | }
51 | else if (Oid.SHA256 == digestType)
52 | {
53 | digestAlg = new SHA256Managed();
54 | }
55 | else if (Oid.SHA512 == digestType)
56 | {
57 | digestAlg = new SHA512Managed();
58 | }
59 | else
60 | {
61 | throw new CryptographicException("Unsupported hash algorithm.");
62 | }
63 |
64 | return digestAlg.ComputeHash(dataStream);
65 | }
66 | finally
67 | {
68 | digestAlg.Dispose();
69 | }
70 | }
71 |
72 | ///
73 | /// Computes digest of the file.
74 | ///
75 | /// Path to file to compute digest for.
76 | /// Defines hash algorithm to be used to compute digest.
77 | /// digest stored in a byte array
78 | public static byte[] ComputeDigest(string pathToFile, Oid digestType)
79 | {
80 | using (FileStream fs = new FileStream(pathToFile, FileMode.Open, FileAccess.Read))
81 | {
82 | return ComputeDigest(fs, digestType);
83 | }
84 | }
85 |
86 | ///
87 | /// Computes digest of the data.
88 | ///
89 | /// Data to compute digest for.
90 | /// Defines hash algorithm to be used to compute digest.
91 | /// digest stored in a byte array
92 | public static byte[] ComputeDigest(byte[] data, Oid digestType)
93 | {
94 | using (MemoryStream ms = new MemoryStream(data))
95 | {
96 | return ComputeDigest(ms, digestType);
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/doc/icons/TimeStampClient.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
116 |
--------------------------------------------------------------------------------
/src/TimeStampClient/TimeStampToken.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 | using System.Collections;
24 | using System.Collections.Generic;
25 | using System.Linq;
26 | using System.Security.Cryptography.X509Certificates;
27 | using System.Text;
28 |
29 | namespace Disig.TimeStampClient
30 | {
31 | ///
32 | /// Encapsulates information from time stamp token.
33 | ///
34 | public class TimeStampToken
35 | {
36 | ///
37 | /// Bouncy castle representation of time stamp token
38 | ///
39 | internal Org.BouncyCastle.Tsp.TimeStampToken timeStampToken;
40 |
41 | ///
42 | /// DER encoded time stamp token
43 | ///
44 | internal byte[] EncodedToken;
45 |
46 | ///
47 | /// Initializes a new instance of the class.
48 | ///
49 | /// DER encoded time stamp token
50 | public TimeStampToken(byte[] encodedToken)
51 | {
52 | if (null == encodedToken)
53 | {
54 | throw new ArgumentNullException("encodedToken");
55 | }
56 |
57 | EncodedToken = encodedToken;
58 | timeStampToken = new Org.BouncyCastle.Tsp.TimeStampToken(new Org.BouncyCastle.Cms.CmsSignedData(encodedToken));
59 | this.MessageImprint = new MsgImprint(this.timeStampToken.TimeStampInfo.GetMessageImprintDigest(), this.timeStampToken.TimeStampInfo.MessageImprintAlgOid);
60 |
61 | X509Certificate2 tsaCert = null;
62 | Org.BouncyCastle.Cms.SignerID signerId = this.timeStampToken.SignerID;
63 | Org.BouncyCastle.Cms.CmsSignedData cmsSignedData = new Org.BouncyCastle.Cms.CmsSignedData(this.timeStampToken.GetEncoded());
64 | ICollection signerCerts = cmsSignedData.GetCertificates("COLLECTION").GetMatches(this.timeStampToken.SignerID);
65 |
66 | foreach (Org.BouncyCastle.X509.X509Certificate cert in signerCerts)
67 | {
68 | tsaCert = new X509Certificate2(cert.GetEncoded());
69 | }
70 | this.TsaInformation = new TsaId(signerId, tsaCert);
71 | }
72 |
73 | ///
74 | /// Information about time stamp authority.
75 | ///
76 | public TsaId TsaInformation
77 | {
78 | get;
79 | set;
80 | }
81 |
82 | ///
83 | /// Returns DER encoded time-stamp token.
84 | ///
85 | /// Byte array containing DER encoded time-stamp token.
86 | public byte[] ToByteArray()
87 | {
88 | return this.EncodedToken;
89 | }
90 |
91 | ///
92 | /// Gets the hash of the data to be time-stamped.
93 | ///
94 | public byte[] HashedMessage
95 | {
96 | get
97 | {
98 | return this.MessageImprint.HashedMessage;
99 | }
100 | }
101 |
102 | ///
103 | /// Gets the hash algorithm identifier.
104 | /// Must correspond to hash algorithm identifier requested in the request.
105 | ///
106 | public string HashAlgorithm
107 | {
108 | get
109 | {
110 | return this.MessageImprint.HashAlgorithm;
111 | }
112 | }
113 |
114 | ///
115 | /// Gets Nonce, large random number with a high probability that it is generated by the client only once.
116 | /// Corresponds to nonce presented in the request.
117 | /// Prevents replay attack.
118 | ///
119 | public byte[] Nonce
120 | {
121 | get
122 | {
123 | if (null != this.timeStampToken.TimeStampInfo.Nonce)
124 | return this.timeStampToken.TimeStampInfo.Nonce.ToByteArray();
125 | return null;
126 | }
127 | }
128 |
129 | ///
130 | /// Gets security policy under which the token was created.
131 | ///
132 | public string PolicyOid
133 | {
134 | get
135 | {
136 | return this.timeStampToken.TimeStampInfo.Policy;
137 | }
138 | }
139 |
140 | ///
141 | /// Gets serial number of a time stamp.
142 | ///
143 | public byte[] SerialNumber
144 | {
145 | get
146 | {
147 | return this.timeStampToken.TimeStampInfo.SerialNumber.ToByteArray();
148 | }
149 | }
150 |
151 | ///
152 | /// Gets time from time stamp token.
153 | ///
154 | public DateTime Time
155 | {
156 | get
157 | {
158 | return this.timeStampToken.TimeStampInfo.GenTime;
159 | }
160 | }
161 |
162 | ///
163 | /// Gets the hash algorithm identifier and the hash value of the data to be time-stamped.
164 | ///
165 | internal MsgImprint MessageImprint
166 | {
167 | get;
168 | private set;
169 | }
170 |
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/TimeStampClient/TimeStampException.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 | using System.Runtime.Serialization;
24 |
25 | namespace Disig.TimeStampClient
26 | {
27 | ///
28 | /// Time stamp exception class.
29 | ///
30 | [Serializable]
31 | public class TimeStampException : Exception
32 | {
33 | ///
34 | /// Initializes a new instance of the class.
35 | ///
36 | public TimeStampException()
37 | : base()
38 | {
39 | }
40 |
41 | ///
42 | /// Initializes a new instance of the class with the specified error message.
43 | ///
44 | /// The message that describes the error.
45 | public TimeStampException(string message)
46 | : base(message)
47 | {
48 | }
49 |
50 | ///
51 | /// Initializes a new instance of the class with the specified error message and nested exception.
52 | ///
53 | /// The message that describes the error.
54 | /// A nested Exception.
55 | public TimeStampException(string message, Exception e)
56 | : base(message, e)
57 | {
58 | }
59 |
60 | ///
61 | /// Initializes a new instance of the class with the specified PKI status.
62 | ///
63 | /// PKI status.
64 | public TimeStampException(PkiStatus pkiStatus)
65 | : base()
66 | {
67 | this.PKIStatus = pkiStatus;
68 | }
69 |
70 | ///
71 | /// Initializes a new instance of the class with the specified error message, PKI status code, PKI status string and PKI failure info.
72 | ///
73 | /// The message that describes the error.
74 | /// PKI status.
75 | /// PKI status string.
76 | /// PKI failure info.
77 | public TimeStampException(string message, PkiStatus pkiStatus, string pkiStatusString, PkiFailureInfo? pkiFailureInfo)
78 | : base(message)
79 | {
80 | this.PKIStatus = pkiStatus;
81 | this.PKIStatusString = pkiStatusString;
82 | this.PKIFailureInfo = pkiFailureInfo;
83 | }
84 |
85 | ///
86 | /// Initializes a new instance of the class with the specified error message, nested exception and PKI status code.
87 | ///
88 | /// The message that describes the error.
89 | /// PKI status code.
90 | public TimeStampException(string message, PkiStatus statusCode)
91 | : base(message)
92 | {
93 | this.PKIStatus = statusCode;
94 | }
95 |
96 | ///
97 | /// Initializes a new instance of the class with the specified error message, nested exception and PKI status code.
98 | ///
99 | /// The message that describes the error.
100 | /// A nested Exception.
101 | /// PKI status code.
102 | public TimeStampException(string message, Exception e, PkiStatus statusCode)
103 | : base(message, e)
104 | {
105 | this.PKIStatus = statusCode;
106 | }
107 |
108 | ///
109 | /// Initializes a new instance of the class with serialized data.
110 | ///
111 | /// The SerializationInfo that holds the serialized object data about the exception being thrown.
112 | /// The StreamingContext that contains contextual information about the source or destination.
113 | protected TimeStampException(SerializationInfo info, StreamingContext context)
114 | : base(info, context)
115 | {
116 | if (null != info)
117 | {
118 | this.PKIStatus = (PkiStatus)info.GetInt32("PKIStatus");
119 | this.PKIStatusString = info.GetString("PKIStatusString");
120 | this.PKIFailureInfo = (PkiFailureInfo)info.GetInt32("PKIFailureInfo");
121 | }
122 | }
123 |
124 | ///
125 | /// Gets or sets PKIStatus.
126 | ///
127 | public PkiStatus PKIStatus
128 | {
129 | get;
130 | set;
131 | }
132 |
133 | ///
134 | /// Gets or sets PKIStatusString.
135 | ///
136 | public string PKIStatusString
137 | {
138 | get;
139 | set;
140 | }
141 |
142 | ///
143 | /// Gets or sets PKIFailure info. If present, indicates the error that occurred while time-stamping data.
144 | ///
145 | public PkiFailureInfo? PKIFailureInfo
146 | {
147 | get;
148 | set;
149 | }
150 |
151 | ///
152 | /// Populates a SerializationInfo with the data needed to serialize the target object.
153 | ///
154 | /// The SerializationInfo to populate with data.
155 | /// The destination for this serialization.
156 | public override void GetObjectData(SerializationInfo info, StreamingContext context)
157 | {
158 | if (null != info)
159 | {
160 | info.AddValue("PKIStatus", this.PKIStatus);
161 | info.AddValue("PKIStatusString", this.PKIStatusString);
162 | info.AddValue("PKIFailureInfo", this.PKIFailureInfo);
163 | }
164 |
165 | base.GetObjectData(info, context);
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/TimeStampClientCmd/Program.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 | using System.Net;
24 | using System.Reflection;
25 | using System.Security.Cryptography.X509Certificates;
26 |
27 | namespace Disig.TimeStampClient.Cmd
28 | {
29 | public class Program
30 | {
31 | public static void Main(string[] args)
32 | {
33 | try
34 | {
35 | string fileName = null;
36 | string tsa = null;
37 | string hash = null;
38 | string policy = null;
39 | string nonce = null;
40 | bool cert = false;
41 | string outFile = null;
42 | string sslClientCertFile = null;
43 | string sslClientCertPass = null;
44 | string httpAuthLogin = null;
45 | string httpAuthPass = null;
46 | bool isAsics = false;
47 |
48 | if (args == null)
49 | {
50 | return;
51 | }
52 |
53 | int i = 0;
54 | if (0 >= args.Length)
55 | {
56 | ExitWithHelp(string.Empty);
57 | }
58 |
59 | while (i < args.Length)
60 | {
61 | switch (args[i])
62 | {
63 | case "--file":
64 | fileName = args[++i];
65 | break;
66 | case "--tsa":
67 | tsa = args[++i];
68 | break;
69 | case "--out":
70 | outFile = args[++i];
71 | break;
72 | case "--hash":
73 | hash = args[++i];
74 | break;
75 | case "--policy":
76 | policy = args[++i];
77 | break;
78 | case "--nonce":
79 | nonce = args[++i];
80 | break;
81 | case "--cert-req":
82 | cert = true;
83 | break;
84 | case "--asics":
85 | isAsics = true;
86 | break;
87 | case "--ssl-client-cert-file":
88 | sslClientCertFile = args[++i];
89 | break;
90 | case "--ssl-client-cert-pass":
91 | sslClientCertPass = args[++i];
92 | break;
93 | case "--http-auth-login":
94 | httpAuthLogin = args[++i];
95 | break;
96 | case "--http-auth-pass":
97 | httpAuthPass = args[++i];
98 | break;
99 | default:
100 | ExitWithHelp("Invalid argument: " + args[i]);
101 | break;
102 | }
103 |
104 | i++;
105 | }
106 |
107 | X509Certificate2 sslCert = null;
108 | if (!string.IsNullOrEmpty(sslClientCertFile))
109 | {
110 | sslCert = new X509Certificate2(sslClientCertFile, sslClientCertPass);
111 | }
112 |
113 | NetworkCredential networkCredential = null;
114 | if (!string.IsNullOrEmpty(httpAuthLogin) && !string.IsNullOrEmpty(httpAuthPass))
115 | {
116 | networkCredential = new NetworkCredential(httpAuthLogin, httpAuthPass);
117 | }
118 |
119 | UserCredentials credentials = null;
120 | if (networkCredential != null || sslCert != null)
121 | {
122 | credentials = new UserCredentials(sslCert, networkCredential);
123 | }
124 |
125 | TimeStampToken token = SharedUtils.RequestTimeStamp(tsa, fileName, hash, policy, nonce, cert, credentials, new LogDelegate(LogMessage), true);
126 | if (isAsics)
127 | {
128 | SharedUtils.SaveToAsicSimple(fileName, token, outFile);
129 | }
130 | else
131 | {
132 | SharedUtils.SaveResponse(outFile, token);
133 | }
134 | }
135 | catch (Exception e)
136 | {
137 | Console.WriteLine(e.Message);
138 | ExitWithHelp(null);
139 | }
140 |
141 | Console.WriteLine("Success");
142 | }
143 |
144 | public static void LogMessage(string msg)
145 | {
146 | Console.WriteLine(msg);
147 | }
148 |
149 | public static void ExitWithHelp(string error)
150 | {
151 | if (string.IsNullOrEmpty(error))
152 | {
153 | Console.WriteLine(Assembly.GetExecutingAssembly().GetName().Name + " " + Assembly.GetExecutingAssembly().GetName().Version);
154 | Console.WriteLine(@"Copyright (c) 2016-2021 Disig a.s. ");
155 | Console.WriteLine();
156 | }
157 | else
158 | {
159 | Console.WriteLine(@"Argument error: " + error);
160 | Console.WriteLine();
161 | }
162 |
163 | Console.WriteLine(@"Usage:");
164 | Console.WriteLine();
165 | Console.WriteLine(@" TimeStampClientCmd");
166 | Console.WriteLine(@" --file ""file_to_timestamp""");
167 | Console.WriteLine(@" --tsa ""tsa_service_address""");
168 | Console.WriteLine(@" --out ""file_to_save_timestamp""");
169 | Console.WriteLine(@" [--hash ""sha1 | sha256 | sha512 | md5""]");
170 | Console.WriteLine(@" [--policy ""policy_oid""]");
171 | Console.WriteLine(@" [--nonce ""1234567890ABCDEF""]");
172 | Console.WriteLine(@" [--cert-req]");
173 | Console.WriteLine(@" [--asics]");
174 | Console.WriteLine(@" [--ssl-client-cert-file ""path_to_client_pkcs12_certificate""]");
175 | Console.WriteLine(@" [--ssl-client-cert-pass ""certificate_password""]");
176 | Console.WriteLine(@" [--http-auth-login ""http_authentication_login""]");
177 | Console.WriteLine(@" [--http-auth-pass ""http_authentication_password""]");
178 |
179 | Environment.Exit(1);
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/src/TimeStampClient/Request.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using Org.BouncyCastle.Tsp;
23 | using System;
24 |
25 | namespace Disig.TimeStampClient
26 | {
27 | /*
28 | TimeStampReq ::= SEQUENCE {
29 | version INTEGER { v1(1) },
30 | messageImprint MessageImprint,
31 | reqPolicy TSAPolicyId OPTIONAL,
32 | nonce INTEGER OPTIONAL,
33 | certReq BOOLEAN DEFAULT FALSE,
34 | extensions [0] IMPLICIT Extensions OPTIONAL
35 | }
36 |
37 | The version field (currently v1) describes the version of the Time-
38 | Stamp request.
39 | */
40 |
41 | ///
42 | /// Time stamp request
43 | ///
44 | public class Request
45 | {
46 | ///
47 | /// Initializes a new instance of the class.
48 | ///
49 | /// This field contains the hash of the data to be time-stamped.
50 | /// The hash algorithm identifier.
51 | /// Cryptographic nonce preventing replay attack.
52 | /// Requested policy.
53 | /// Flag indicating that a TSA should include its certificate in a response.
54 | /// The version of the time stamp request.
55 | public Request(byte[] hashedMessage, string hashAlgOid, byte[] nonce = null, string reqPolicy = null, bool certReq = false, int version = 1)
56 | {
57 | this.Setup(hashedMessage, hashAlgOid, nonce, reqPolicy, certReq, version);
58 | }
59 |
60 | ///
61 | /// Initializes a new instance of the class.
62 | ///
63 | /// This field contains the hash of the data to be time-stamped.
64 | /// The hash algorithm identifier.
65 | /// Cryptographic nonce preventing replay attack.
66 | /// Requested policy.
67 | /// Flag indicating that TSA should include its certificate in a response.
68 | /// The version of the time stamp request.
69 | public Request(byte[] hashedMessage, Oid hashAlg, byte[] nonce = null, string reqPolicy = null, bool certReq = false, int version = 1)
70 | {
71 | if (null != hashAlg)
72 | {
73 | this.Setup(hashedMessage, hashAlg.OID, nonce, reqPolicy, certReq, version);
74 | }
75 | else
76 | {
77 | throw new ArgumentNullException("hashAlg");
78 | }
79 | }
80 |
81 | ///
82 | /// Initializes a new instance of the class.
83 | ///
84 | /// DER encoded time stamp request
85 | public Request(byte[] tsrBytes)
86 | {
87 | TimeStampRequest req;
88 | req = new TimeStampRequest(tsrBytes);
89 | byte[] nonce = null;
90 | if (null != req.Nonce)
91 | {
92 | nonce = req.Nonce.ToByteArray();
93 | }
94 |
95 | this.Setup(req.GetMessageImprintDigest(), req.MessageImprintAlgOid, nonce, req.ReqPolicy, req.CertReq, req.Version);
96 | }
97 |
98 | ///
99 | /// Gets the version of a time stamp request.
100 | ///
101 | public int Version
102 | {
103 | get;
104 | private set;
105 | }
106 |
107 | ///
108 | /// Gets the requested policy (OID).
109 | ///
110 | public string ReqPolicy
111 | {
112 | get;
113 | private set;
114 | }
115 |
116 | ///
117 | /// Gets Nonce, a large random number with a high probability that it is generated by the client only once.
118 | /// Prevents replay attack.
119 | ///
120 | public byte[] Nonce
121 | {
122 | get;
123 | private set;
124 | }
125 |
126 | ///
127 | /// Gets a value indicating whether a TSA should include its certificate in a response.
128 | ///
129 | public bool CertReq
130 | {
131 | get;
132 | private set;
133 | }
134 |
135 | ///
136 | /// Gets the hash algorithm identifier.
137 | ///
138 | public string HashAlgorithm
139 | {
140 | get
141 | {
142 | return this.MessageImprint.HashAlgorithm;
143 | }
144 | }
145 |
146 | ///
147 | /// Gets the hashed message.
148 | ///
149 | public byte[] HashedMessage
150 | {
151 | get
152 | {
153 | return this.MessageImprint.HashedMessage;
154 | }
155 | }
156 |
157 | ///
158 | /// Gets the structure containing the hash of the data to be time-stamped and the algorithm identifier used to compute the hash.
159 | ///
160 | internal MsgImprint MessageImprint
161 | {
162 | get;
163 | private set;
164 | }
165 |
166 | ///
167 | /// Returns DER encoded time-stamp request.
168 | ///
169 | /// Byte array containing DER encoded request.
170 | public byte[] ToByteArray()
171 | {
172 | TimeStampRequestGenerator tsqGenerator = new TimeStampRequestGenerator();
173 | TimeStampRequest tsreq;
174 |
175 | tsqGenerator.SetCertReq(this.CertReq);
176 | if (!string.IsNullOrEmpty(this.ReqPolicy))
177 | {
178 | tsqGenerator.SetReqPolicy(this.ReqPolicy);
179 | }
180 |
181 | if (null == this.Nonce)
182 | {
183 | tsreq = tsqGenerator.Generate(this.MessageImprint.HashAlgorithm, this.MessageImprint.HashedMessage);
184 | }
185 | else
186 | {
187 | tsreq = tsqGenerator.Generate(this.MessageImprint.HashAlgorithm, this.MessageImprint.HashedMessage, new Org.BouncyCastle.Math.BigInteger(this.Nonce));
188 | }
189 |
190 | return tsreq.GetEncoded();
191 | }
192 |
193 | ///
194 | /// Sets properties of the class.
195 | ///
196 | /// This field contains the hash of the data to be time-stamped.
197 | /// The hash algorithm identifier.
198 | /// Cryptographic nonce preventing replay attack.
199 | /// Requested policy.
200 | /// The flag indicating that TSA should include its certificate in a response.
201 | /// The version of the time stamp request.
202 | private void Setup(byte[] hashedMessage, string hashAlgOid, byte[] nonce = null, string reqPolicy = null, bool certReq = false, int version = 1)
203 | {
204 | this.Version = version;
205 | this.MessageImprint = new MsgImprint(hashedMessage, hashAlgOid);
206 | this.ReqPolicy = reqPolicy;
207 | this.Nonce = nonce;
208 | this.CertReq = certReq;
209 | }
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/AboutDialog.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 | using System.Diagnostics;
24 | using Eto.Drawing;
25 | using Eto.Forms;
26 |
27 | namespace Disig.TimeStampClient.Gui
28 | {
29 | internal class AboutDialog : Dialog
30 | {
31 | internal AboutDialog()
32 | {
33 | this.InitializeComponent();
34 | return;
35 | }
36 |
37 | private Button WebsiteButton
38 | {
39 | get;
40 | set;
41 | }
42 |
43 | private Button CloseButton
44 | {
45 | get;
46 | set;
47 | }
48 |
49 | private TextArea LicenseTextArea
50 | {
51 | get;
52 | set;
53 | }
54 |
55 | private TextArea ComponentsTextArea
56 | {
57 | get;
58 | set;
59 | }
60 |
61 | private void InitializeComponent()
62 | {
63 | this.Title = "About";
64 | this.InitControls();
65 |
66 | this.Content = this.CreateAboutContent();
67 | this.MinimumSize = new Size(400, 300);
68 | this.Size = this.MinimumSize;
69 | }
70 |
71 | private Layout CreateAboutContent()
72 | {
73 | TabControl tabControl = new TabControl();
74 | TabPage licensePage = new TabPage();
75 |
76 | licensePage.Text = "License";
77 | licensePage.Content = new TableLayout
78 | {
79 | Rows =
80 | {
81 | new TableRow
82 | {
83 | ScaleHeight = true,
84 | Cells =
85 | {
86 | new TableCell(
87 | new TableLayout
88 | {
89 | Rows =
90 | {
91 | new TableRow { Cells = { this.LicenseTextArea }, ScaleHeight = true },
92 | },
93 | }, true),
94 | }
95 | },
96 | }
97 | };
98 |
99 | TabPage componentsPage = new TabPage();
100 | componentsPage.Text = "3rd party components";
101 | componentsPage.Content = new TableLayout
102 | {
103 | Rows =
104 | {
105 | new TableRow
106 | {
107 | ScaleHeight = true,
108 | Cells =
109 | {
110 | new TableCell(
111 | new TableLayout
112 | {
113 | Rows =
114 | {
115 | new TableRow { Cells = { this.ComponentsTextArea }, ScaleHeight = true },
116 | },
117 | }, true),
118 | }
119 | },
120 | }
121 | };
122 |
123 | tabControl.Pages.Add(licensePage);
124 | tabControl.Pages.Add(componentsPage);
125 |
126 | return new TableLayout
127 | {
128 | Padding = 10,
129 | Spacing = new Size(5, 5),
130 | Rows =
131 | {
132 | new TableRow { Cells = { tabControl }, ScaleHeight = true },
133 | new TableRow
134 | {
135 | Cells =
136 | {
137 | new TableCell(
138 | new TableLayout
139 | {
140 | Rows =
141 | {
142 | new TableRow(this.WebsiteButton, new TableCell { ScaleWidth = true }, this.CloseButton),
143 | new TableRow { ScaleHeight = true },
144 | },
145 | }, true),
146 | },
147 | },
148 | },
149 | };
150 | }
151 |
152 | private void InitControls()
153 | {
154 | this.WebsiteButton = new Button();
155 | this.WebsiteButton.Text = "Website";
156 | this.WebsiteButton.Click += this.WebsiteButtonOnClick;
157 |
158 | this.CloseButton = new Button();
159 | this.CloseButton.Text = "Close";
160 | this.CloseButton.Click += this.CloseButtonOnClick;
161 |
162 | this.LicenseTextArea = new RichTextArea();
163 | this.FillLicenseTextArea();
164 |
165 | this.ComponentsTextArea = new RichTextArea();
166 | this.ComponentsTextArea.ReadOnly = true;
167 | this.ComponentsTextArea.Text = string.Empty;
168 | this.ComponentsTextArea.Append(@"TimeStampClient uses following 3rd party components (in alphabetical order):" + Environment.NewLine
169 | + Environment.NewLine
170 | + "- DotNetZip" + Environment.NewLine
171 | + "- Eto.Forms" + Environment.NewLine
172 | + "- Eto.Platform.Gtk" + Environment.NewLine
173 | + "- Eto.Platform.Mac64" + Environment.NewLine
174 | + "- Eto.Platform.Wpf" + Environment.NewLine
175 | + "- BouncyCastle.Crypto" + Environment.NewLine
176 | + Environment.NewLine
177 | + "Full license text for each of these components can be found in the installation directory.");
178 |
179 | if (SharedUtils.RunningOnMacOs())
180 | this.ComponentsTextArea.Selection = new Range(0);
181 | }
182 |
183 | private void FillLicenseTextArea()
184 | {
185 | this.LicenseTextArea.ReadOnly = true;
186 | this.LicenseTextArea.Append(@"TimeStampClient " + SharedUtils.AppVersion + Environment.NewLine);
187 | this.LicenseTextArea.Append(@"Copyright (c) 2016-2021 Disig a.s." + Environment.NewLine);
188 | this.LicenseTextArea.Append(Environment.NewLine + "Licensed under the Apache License, Version 2.0 (the \"License\"); " +
189 | "you may not use this file except in compliance with the License. You may obtain a copy of the License at " +
190 | Environment.NewLine + Environment.NewLine + "https://www.apache.org/licenses/LICENSE-2.0" + Environment.NewLine + Environment.NewLine +
191 | "Unless required by applicable law or agreed to in writing, software distributed under the License is " +
192 | "distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. " +
193 | "See the License for the specific language governing permissions and limitations under the License." + Environment.NewLine);
194 |
195 | if (SharedUtils.RunningOnMacOs())
196 | this.LicenseTextArea.Selection = new Range(0);
197 | }
198 |
199 | private void CloseDialogWindow()
200 | {
201 | this.Close();
202 | }
203 |
204 | private void CloseButtonOnClick(object sender, EventArgs e)
205 | {
206 | this.CloseDialogWindow();
207 | }
208 |
209 | private static void OpenWebSite()
210 | {
211 | Process.Start(new ProcessStartInfo {
212 | FileName = "https://github.com/disig/TimeStampClient",
213 | UseShellExecute = true
214 | });
215 | }
216 |
217 | private void WebsiteButtonOnClick(object sender, EventArgs e)
218 | {
219 | OpenWebSite();
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/TimeStampClient/Response.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using Org.BouncyCastle.Tsp;
23 | using System;
24 |
25 | namespace Disig.TimeStampClient
26 | {
27 | ///
28 | /// Time stamp response
29 | ///
30 | internal class Response
31 | {
32 | #region Timestamp reponse according to RFC3161
33 | /*
34 | TimeStampResp::= SEQUENCE {
35 | status PKIStatusInfo,
36 | timeStampToken TimeStampToken OPTIONAL }
37 |
38 | PKIStatusInfo ::= SEQUENCE {
39 | status PKIStatus,
40 | statusString PKIFreeText OPTIONAL,
41 | failInfo PKIFailureInfo OPTIONAL }
42 |
43 | PKIStatus ::= INTEGER {
44 | granted (0),
45 | -- when the PKIStatus contains the value zero a TimeStampToken, as
46 | requested, is present.
47 | grantedWithMods (1),
48 | -- when the PKIStatus contains the value one a TimeStampToken,
49 | with modifications, is present.
50 | rejection (2),
51 | waiting (3),
52 | revocationWarning (4),
53 | -- this message contains a warning that a revocation is imminent
54 | revocationNotification (5)
55 | -- notification that a revocation has occurred }
56 |
57 |
58 | PKIFailureInfo ::= BIT STRING {
59 | badAlg (0),
60 | -- unrecognized or unsupported Algorithm Identifier
61 | badRequest (2),
62 | -- transaction not permitted or supported
63 | badDataFormat (5),
64 | -- the data submitted has the wrong format
65 | timeNotAvailable (14),
66 | -- the TSA's time source is not available
67 | unacceptedPolicy (15),
68 | -- the requested TSA policy is not supported by the TSA
69 | unacceptedExtension (16),
70 | -- the requested extension is not supported by the TSA
71 | addInfoNotAvailable (17)
72 | -- the additional information requested could not be understood
73 | -- or is not available
74 | systemFailure (25)
75 | -- the request cannot be handled due to system failure }
76 |
77 |
78 | TSTInfo ::= SEQUENCE {
79 | version INTEGER { v1(1) },
80 | policy TSAPolicyId,
81 | messageImprint MessageImprint,
82 | -- MUST have the same value as the similar field in
83 | -- TimeStampReq
84 | serialNumber INTEGER,
85 | -- Time-Stamping users MUST be ready to accommodate integers
86 | -- up to 160 bits.
87 | genTime GeneralizedTime,
88 | accuracy Accuracy OPTIONAL,
89 | ordering BOOLEAN DEFAULT FALSE,
90 | nonce INTEGER OPTIONAL,
91 | -- MUST be present if the similar field was present
92 | -- in TimeStampReq. In that case it MUST have the same value.
93 | tsa [0] GeneralName OPTIONAL,
94 | extensions [1] IMPLICIT Extensions OPTIONAL }
95 |
96 |
97 |
98 | Accuracy ::= SEQUENCE {
99 | seconds INTEGER OPTIONAL,
100 | millis [0] INTEGER (1..999) OPTIONAL,
101 | micros [1] INTEGER (1..999) OPTIONAL }
102 | */
103 | #endregion
104 |
105 | ///
106 | /// RFC 3161 Time Stamp Response object.
107 | ///
108 | private TimeStampResponse response;
109 |
110 | ///
111 | /// Initializes a new instance of the class.
112 | ///
113 | /// DER encoded time stamp response.
114 | public Response(byte[] response)
115 | {
116 | if (null == response)
117 | {
118 | throw new ArgumentNullException("response");
119 | }
120 |
121 | this.response = new TimeStampResponse(response);
122 |
123 | if (null != this.response.TimeStampToken)
124 | {
125 | // NOTICE: this.response.TimeStampToken.GetEncoded() returns malformed byte array; therefore, we use low level interface
126 | // this.TST = new TimeStampToken(this.response.TimeStampToken.GetEncoded());
127 | Org.BouncyCastle.Asn1.Tsp.TimeStampResp asn1Response = Org.BouncyCastle.Asn1.Tsp.TimeStampResp.GetInstance(Org.BouncyCastle.Asn1.Asn1Sequence.FromByteArray(response));
128 | var derTst = asn1Response.TimeStampToken.GetDerEncoded();
129 |
130 | this.TST = new TimeStampToken(derTst);
131 | }
132 | }
133 |
134 | ///
135 | /// Gets PKIStatus
136 | ///
137 | public PkiStatus PKIStatus
138 | {
139 | get
140 | {
141 | return (PkiStatus)this.response.Status;
142 | }
143 | }
144 |
145 | ///
146 | /// Gets additional status information.
147 | ///
148 | internal string PKIStatusString
149 | {
150 | get
151 | {
152 | return this.response.GetStatusString();
153 | }
154 | }
155 |
156 | ///
157 | /// Gets the reason why the time-stamp request was rejected.
158 | ///
159 | internal PkiFailureInfo? PKIFailureInfo
160 | {
161 | get
162 | {
163 | if (null != this.response.GetFailInfo())
164 | {
165 | PkiFailureInfo res;
166 | switch (this.response.GetFailInfo().IntValue)
167 | {
168 | case Org.BouncyCastle.Asn1.Cmp.PkiFailureInfo.BadAlg:
169 | res = PkiFailureInfo.BadAlg;
170 | break;
171 | case Org.BouncyCastle.Asn1.Cmp.PkiFailureInfo.BadRequest:
172 | res = PkiFailureInfo.BadRequest;
173 | break;
174 | case Org.BouncyCastle.Asn1.Cmp.PkiFailureInfo.BadDataFormat:
175 | res = PkiFailureInfo.BadDataFormat;
176 | break;
177 | case Org.BouncyCastle.Asn1.Cmp.PkiFailureInfo.TimeNotAvailable:
178 | res = PkiFailureInfo.TimeNotAvailable;
179 | break;
180 | case Org.BouncyCastle.Asn1.Cmp.PkiFailureInfo.UnacceptedPolicy:
181 | res = PkiFailureInfo.UnacceptedPolicy;
182 | break;
183 | case Org.BouncyCastle.Asn1.Cmp.PkiFailureInfo.UnacceptedExtension:
184 | res = PkiFailureInfo.UnacceptedExtension;
185 | break;
186 | case Org.BouncyCastle.Asn1.Cmp.PkiFailureInfo.AddInfoNotAvailable:
187 | res = PkiFailureInfo.AddInfoNotAvailable;
188 | break;
189 | case Org.BouncyCastle.Asn1.Cmp.PkiFailureInfo.SystemFailure:
190 | res = PkiFailureInfo.SystemFailure;
191 | break;
192 | default:
193 | res = PkiFailureInfo.Unknown;
194 | break;
195 | }
196 | return res;
197 | }
198 | return null;
199 | }
200 | }
201 |
202 | internal TimeStampToken TST
203 | {
204 | get;
205 | set;
206 | }
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/src/TimeStampClientCmd/SharedUtils.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 | using System.IO;
24 | using System.Reflection;
25 | using System.Security.Cryptography;
26 | using System.Text;
27 |
28 | namespace Disig.TimeStampClient
29 | {
30 | public delegate void LogDelegate(string msg);
31 |
32 | public static class SharedUtils
33 | {
34 | private const string DateTimeFormat = "dd MMM yyyy HH':'mm':'ss 'GMT'";
35 | private static string appVersion = null;
36 |
37 | public enum ResultFormat
38 | {
39 | RawTST,
40 | ASIC_S
41 | }
42 |
43 | public static string AppVersion
44 | {
45 | get
46 | {
47 | Assembly assembly = Assembly.GetExecutingAssembly();
48 | if (string.IsNullOrEmpty(appVersion))
49 | {
50 | appVersion = assembly.GetName().Version.ToString();
51 | }
52 |
53 | return appVersion;
54 | }
55 | }
56 |
57 | public static TimeStampToken RequestTimeStamp(string tsaService, string fileName, string hashAlg, string policy, string nonce, bool certReq, UserCredentials credentials, LogDelegate logger, bool logExceptions)
58 | {
59 | Oid hashOid = null;
60 | switch (hashAlg)
61 | {
62 | case "sha1":
63 | hashOid = Oid.SHA1;
64 | break;
65 | case "sha256":
66 | hashOid = Oid.SHA256;
67 | break;
68 | case "sha512":
69 | hashOid = Oid.SHA512;
70 | break;
71 | case "md5":
72 | hashOid = Oid.MD5;
73 | break;
74 | default:
75 | hashOid = Oid.SHA256;
76 | break;
77 | }
78 |
79 | return RequestTimeStamp(tsaService, fileName, hashOid, policy, nonce, certReq, credentials, logger, logExceptions);
80 | }
81 |
82 | public static TimeStampToken RequestTST(string fileName, string tsaService, Oid hashAlg, string policy, string nonce, bool certReq, UserCredentials credentials)
83 | {
84 | byte[] nonce_bytes = null;
85 | byte[] hashedMessage = DigestUtils.ComputeDigest(fileName, hashAlg);
86 | if (!string.IsNullOrEmpty(nonce))
87 | {
88 | nonce_bytes = SharedUtils.HexStringToBytes(nonce);
89 | }
90 |
91 | Request request = new Request(hashedMessage, hashAlg, nonce_bytes, policy, certReq);
92 | return TimeStampClient.RequestTimeStampToken(tsaService, request, credentials);
93 | }
94 |
95 | public static void SaveResponse(string destinationFile, TimeStampToken timeStampToken)
96 | {
97 | if (string.IsNullOrEmpty(destinationFile))
98 | {
99 | throw new ArgumentNullException("destinationFile");
100 | }
101 |
102 | if (null == timeStampToken)
103 | {
104 | throw new ArgumentNullException("timeStampToken");
105 | }
106 |
107 | System.IO.File.WriteAllBytes(destinationFile, timeStampToken.ToByteArray());
108 | }
109 |
110 | public static string BytesToHexString(byte[] value)
111 | {
112 | return BitConverter.ToString(value).Replace("-", string.Empty);
113 | }
114 |
115 | public static byte[] HexStringToBytes(string value)
116 | {
117 | if (value == null)
118 | {
119 | return null;
120 | }
121 |
122 | if (!System.Text.RegularExpressions.Regex.IsMatch(value, @"\A\b[0-9a-fA-F]+\b\Z"))
123 | {
124 | throw new ArgumentException("Nonce field can contain only hex characters");
125 | }
126 |
127 | if (0 != value.Length % 2)
128 | {
129 | value = string.Format("0{0}", value);
130 | }
131 |
132 | byte[] bytes = new byte[value.Length / 2];
133 |
134 | for (int i = 0; i < value.Length; i += 2)
135 | {
136 | bytes[i / 2] = Convert.ToByte(value.Substring(i, 2), 16);
137 | }
138 |
139 | return bytes;
140 | }
141 |
142 | public static TimeStampToken RequestTimeStamp(
143 | string tsaAddress,
144 | string fileToTimestamp,
145 | Oid hashAlg,
146 | string requestedPolicy,
147 | string nonce,
148 | bool certReq,
149 | UserCredentials credentials,
150 | LogDelegate logger,
151 | bool logExceptions)
152 | {
153 | try
154 | {
155 | if (logger == null)
156 | {
157 | return null;
158 | }
159 |
160 | logger(string.Format("=== {0:" + DateTimeFormat + "} =============================================", DateTime.UtcNow));
161 | logger(string.Format("Requesting time stamp from {0}", tsaAddress));
162 |
163 | TimeStampToken token = SharedUtils.RequestTST(fileToTimestamp, tsaAddress, hashAlg, requestedPolicy, nonce, certReq, credentials);
164 | logger(string.Format("Time stamp successfully received:"));
165 | logger(string.Format(" Serial number: {0}", SharedUtils.BytesToHexString(token.SerialNumber)));
166 | logger(string.Format(" Time: {0:dd MMM yyyy HH':'mm':'ss 'GMT'}", (token.Time).ToUniversalTime()));
167 |
168 | logger(string.Format("TSA certificate:"));
169 | logger(string.Format(" Issuer: {0}", token.TsaInformation.TsaCertIssuerName.Name));
170 | logger(string.Format(" Serial: {0}", SharedUtils.BytesToHexString(token.TsaInformation.TsaCertSerialNumber)));
171 |
172 | if (null != token.TsaInformation.TsaCert)
173 | {
174 | logger(string.Format(" Subject: {0}", token.TsaInformation.TsaCert.Subject));
175 | logger(string.Format(" Valid from: {0}", token.TsaInformation.TsaCert.NotBefore));
176 | logger(string.Format(" Valid to: {0}", token.TsaInformation.TsaCert.NotAfter));
177 | }
178 |
179 | return token;
180 | }
181 | catch (Exception e)
182 | {
183 | logger(string.Format("Error occurred:"));
184 | if (logExceptions)
185 | {
186 | logger(e.ToString());
187 | }
188 | else
189 | {
190 | logger(e.Message);
191 | }
192 |
193 | throw;
194 | }
195 | }
196 |
197 | public static void SaveInFormat(string fileName, TimeStampToken timeStampToken, ResultFormat format, string outFile)
198 | {
199 | if (format == ResultFormat.ASIC_S)
200 | {
201 | SharedUtils.SaveToAsicSimple(fileName, timeStampToken, outFile);
202 | }
203 | else
204 | {
205 | SharedUtils.SaveResponse(outFile, timeStampToken);
206 | }
207 | }
208 |
209 | public static byte[] GenerateNonceBytes()
210 | {
211 | byte[] nonce = new byte[10];
212 | using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
213 | {
214 | rng.GetBytes(nonce);
215 | return nonce;
216 | }
217 | }
218 |
219 | public static void SaveToAsicSimple(string inputFile, TimeStampToken timeStampToken, string outputFile)
220 | {
221 | using (Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile(UTF8Encoding.UTF8))
222 | {
223 | zipFile.ParallelDeflateThreshold = -1;
224 | zipFile.UseZip64WhenSaving = Ionic.Zip.Zip64Option.Never;
225 | zipFile.EmitTimesInUnixFormatWhenSaving = false;
226 | zipFile.EmitTimesInWindowsFormatWhenSaving = false;
227 | zipFile.Comment = @"mimetype=application/vnd.etsi.asic-s+zip";
228 |
229 | using (System.IO.FileStream inputStream = new System.IO.FileStream(inputFile, System.IO.FileMode.Open, System.IO.FileAccess.Read))
230 | using (System.IO.FileStream outputStream = new System.IO.FileStream(outputFile, System.IO.FileMode.Create, System.IO.FileAccess.Write))
231 | {
232 | zipFile.AddEntry(@"mimetype", System.Text.UTF8Encoding.UTF8.GetBytes(@"application/vnd.etsi.asic-s+zip"));
233 | zipFile.AddEntry(System.IO.Path.GetFileName(inputFile), inputStream);
234 | zipFile.AddEntry(@"META-INF/timestamp.tst", timeStampToken.ToByteArray());
235 | zipFile.Save(outputStream);
236 | }
237 | }
238 | }
239 |
240 | public static bool RunningOnWindows()
241 | {
242 | string windir = Environment.GetEnvironmentVariable("windir");
243 | return !string.IsNullOrEmpty(windir) && windir.Contains(@"\") && Directory.Exists(windir);
244 | }
245 |
246 | public static bool RunningOnLinux()
247 | {
248 | if (File.Exists(@"/proc/sys/kernel/ostype"))
249 | {
250 | string osType = File.ReadAllText(@"/proc/sys/kernel/ostype");
251 | return osType.StartsWith("Linux", StringComparison.OrdinalIgnoreCase);
252 | }
253 |
254 | return false;
255 | }
256 |
257 | public static bool RunningOnMacOs()
258 | {
259 | return File.Exists(@"/System/Library/CoreServices/SystemVersion.plist");
260 | }
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/src/TimeStampClient/Client.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 | using System.Globalization;
24 | using System.IO;
25 | using System.Net;
26 | using System.Net.Sockets;
27 |
28 | namespace Disig.TimeStampClient
29 | {
30 | ///
31 | /// Provides interface to time-stamp data.
32 | ///
33 | public static class TimeStampClient
34 | {
35 | #region RequestTimeStamp
36 |
37 | ///
38 | /// Requests time stamp for the specified time stamp request.
39 | ///
40 | /// URL of a TSA service.
41 | /// The time stamp request.
42 | /// User's credentials to access TSA service.
43 | /// Time-stamp token
44 | public static TimeStampToken RequestTimeStampToken(string tsaUri, Request request, UserCredentials credentials = null)
45 | {
46 | if (null == request)
47 | {
48 | throw new ArgumentNullException("request");
49 | }
50 |
51 | if (null == tsaUri)
52 | {
53 | throw new ArgumentNullException("tsaUri");
54 | }
55 |
56 | return RequestTST(tsaUri, request, credentials);
57 | }
58 |
59 | ///
60 | /// Requests time stamp for the data from stream.
61 | ///
62 | /// URL of a TSA service.
63 | /// Specifies the data to time-stamp.
64 | /// Time-stamp token
65 | public static TimeStampToken RequestTimeStampToken(string tsaUri, Stream dataToTimestamp)
66 | {
67 | return RequestTimeStampToken(tsaUri, dataToTimestamp, null, null);
68 | }
69 |
70 | ///
71 | /// Requests time stamp for the data from stream.
72 | ///
73 | /// URL of a TSA service.
74 | /// Specifies the data to time-stamp.
75 | /// User's credentials to access TSA service.
76 | /// Time-stamp token
77 | public static TimeStampToken RequestTimeStampToken(string tsaUri, Stream dataToTimestamp, UserCredentials credentials)
78 | {
79 | return RequestTimeStampToken(tsaUri, dataToTimestamp, null, credentials);
80 | }
81 |
82 | ///
83 | /// Requests time stamp for the data from stream.
84 | ///
85 | /// URL of a TSA service.
86 | /// Specifies the data to time-stamp.
87 | /// Specifies the hash algorithm to be used to compute digest from data.
88 | /// Time-stamp token
89 | public static TimeStampToken RequestTimeStampToken(string tsaUri, Stream dataToTimestamp, Oid digestType)
90 | {
91 | return RequestTimeStampToken(tsaUri, dataToTimestamp, digestType, null);
92 | }
93 |
94 | ///
95 | /// Requests time stamp for the data from stream.
96 | ///
97 | /// URL of a TSA service.
98 | /// Specifies the data to time-stamp.
99 | /// Specifies the hash algorithm to be used to compute digest from data.
100 | /// User's credentials to access TSA service.
101 | /// Time-stamp token
102 | public static TimeStampToken RequestTimeStampToken(string tsaUri, Stream dataToTimestamp, Oid digestType, UserCredentials credentials)
103 | {
104 | if (null == tsaUri)
105 | {
106 | throw new ArgumentNullException("tsaUri");
107 | }
108 |
109 | if (null == dataToTimestamp)
110 | {
111 | throw new ArgumentNullException("dataToTimestamp");
112 | }
113 |
114 | if (null == digestType)
115 | {
116 | digestType = Oid.SHA512;
117 | }
118 |
119 | byte[] digest = DigestUtils.ComputeDigest(dataToTimestamp, digestType);
120 | Request request = new Request(digest, digestType.OID);
121 | return RequestTST(tsaUri, request, credentials);
122 | }
123 |
124 | ///
125 | /// Requests time stamp for the data stored in the byte array.
126 | ///
127 | /// URL of a TSA service.
128 | /// Specifies the data to time-stamp.
129 | /// Time-stamp token
130 | public static TimeStampToken RequestTimeStampToken(string tsaUri, byte[] dataToTimestamp)
131 | {
132 | return RequestTimeStampToken(tsaUri, dataToTimestamp, null, null);
133 | }
134 |
135 | ///
136 | /// Requests time stamp for the data stored in the byte array.
137 | ///
138 | /// URL of a TSA service.
139 | /// Specifies the data to time-stamp.
140 | /// Specifies the hash algorithm to be used to compute digest from data.
141 | /// Time-stamp token
142 | public static TimeStampToken RequestTimeStampToken(string tsaUri, byte[] dataToTimestamp, Oid digestType)
143 | {
144 | return RequestTimeStampToken(tsaUri, dataToTimestamp, digestType, null);
145 | }
146 |
147 | ///
148 | /// Requests time stamp for the data stored in the byte array.
149 | ///
150 | /// URL of a TSA service.
151 | /// Specifies the data to time-stamp.
152 | /// User's credentials to access TSA service.
153 | /// Time-stamp token
154 | public static TimeStampToken RequestTimeStampToken(string tsaUri, byte[] dataToTimestamp, UserCredentials credentials)
155 | {
156 | return RequestTimeStampToken(tsaUri, dataToTimestamp, null, credentials);
157 | }
158 |
159 | ///
160 | /// Requests time stamp for the data stored in the byte array.
161 | ///
162 | /// URL of a TSA service.
163 | /// Specifies the data to time-stamp.
164 | /// Specifies the hash algorithm to be used to compute digest from data.
165 | /// User's credentials to access TSA service.
166 | /// Time-stamp token
167 | public static TimeStampToken RequestTimeStampToken(string tsaUri, byte[] dataToTimestamp, Oid digestType, UserCredentials credentials)
168 | {
169 | if (null == dataToTimestamp)
170 | {
171 | throw new ArgumentNullException("dataToTimestamp");
172 | }
173 |
174 | using (MemoryStream ms = new MemoryStream(dataToTimestamp))
175 | {
176 | return RequestTimeStampToken(tsaUri, ms, digestType, credentials);
177 | }
178 | }
179 |
180 | ///
181 | /// Requests time stamp for the file specified by the path.
182 | ///
183 | /// URL of a TSA service.
184 | /// Specifies the file to time-stamp.
185 | /// Time-stamp token
186 | public static TimeStampToken RequestTimeStampToken(string tsaUri, string pathToFile)
187 | {
188 | return RequestTimeStampToken(tsaUri, pathToFile, null, null);
189 | }
190 |
191 | ///
192 | /// Requests time stamp for the file specified by the path.
193 | ///
194 | /// URL of a TSA service.
195 | /// Specifies the file to time-stamp.
196 | /// Specifies the hash algorithm to be used to compute digest from data.
197 | /// User's credentials to access TSA service.
198 | /// Time-stamp token
199 | public static TimeStampToken RequestTimeStampToken(string tsaUri, string pathToFileToTimestamp, Oid digestType, UserCredentials credentials)
200 | {
201 | if (null == pathToFileToTimestamp)
202 | {
203 | throw new ArgumentNullException("pathToFileToTimestamp");
204 | }
205 |
206 | using (FileStream fs = new FileStream(pathToFileToTimestamp, FileMode.Open, FileAccess.Read))
207 | {
208 | return RequestTimeStampToken(tsaUri, fs, digestType, credentials);
209 | }
210 | }
211 |
212 | ///
213 | /// Requests time stamp for the file specified by the path.
214 | ///
215 | /// URL of a TSA service.
216 | /// Specifies the file to time-stamp.
217 | /// Specifies the hash algorithm to be used to compute digest from data.
218 | /// Time-stamp token
219 | public static TimeStampToken RequestTimeStampToken(string tsaUri, string pathToFileToTimestamp, Oid digestType)
220 | {
221 | return RequestTimeStampToken(tsaUri, pathToFileToTimestamp, digestType, null);
222 | }
223 |
224 | ///
225 | /// Requests time stamp for the file specified by the path.
226 | ///
227 | /// URL of a TSA service.
228 | /// Specifies the file to time-stamp.
229 | /// User's credentials to access TSA service.
230 | /// Time-stamp token
231 | public static TimeStampToken RequestTimeStampToken(string tsaUri, string pathToFileToTimestamp, UserCredentials credentials)
232 | {
233 | return RequestTimeStampToken(tsaUri, pathToFileToTimestamp, null, credentials);
234 | }
235 |
236 | #endregion
237 |
238 | ///
239 | /// Requests time-stamp from TSA service
240 | ///
241 | /// URL of a TSA service.
242 | /// Time-stamp request.
243 | /// User's credentials to access TSA service.
244 | /// Time-stamp token
245 | private static TimeStampToken RequestTST(string tsaUri, Request request, UserCredentials credentials = null)
246 | {
247 | byte[] responseBytes = null;
248 | UriBuilder urib = new UriBuilder(tsaUri);
249 |
250 | switch (urib.Uri.Scheme)
251 | {
252 | case "http":
253 | case "https":
254 | responseBytes = GetHttpResponse(tsaUri, request.ToByteArray(), credentials);
255 | break;
256 | case "tcp":
257 | responseBytes = GetTcpResponse(tsaUri, request.ToByteArray());
258 | break;
259 | default:
260 | throw new TimeStampException("Unknown protocol.");
261 | }
262 |
263 | Response response = new Response(responseBytes);
264 | ValidateResponse(request, response);
265 | return response.TST;
266 | }
267 |
268 | #region Connections
269 |
270 | ///
271 | /// Creates TCP request and processes TCP response.
272 | ///
273 | /// URL of a TSA service.
274 | /// DER encoded time stamp request.
275 | /// DER encoded time stamp response.
276 | private static byte[] GetTcpResponse(string tsaUri, byte[] tsr)
277 | {
278 | Stream returnStream = null;
279 | UriBuilder urib = new UriBuilder(tsaUri);
280 |
281 | byte[] lenByte = new byte[] { 0, 0, 0, 0, 0 };
282 | int reqLen = tsr.Length + 1;
283 | lenByte[0] = (byte)(reqLen >> 24);
284 | lenByte[1] = (byte)(reqLen >> 16 & 255);
285 | lenByte[2] = (byte)(reqLen >> 8 & 255);
286 | lenByte[3] = (byte)(reqLen & 255);
287 | byte[] request = new byte[tsr.Length + lenByte.Length];
288 |
289 | lenByte.CopyTo(request, 0);
290 | tsr.CopyTo(request, lenByte.Length);
291 |
292 | using (TcpClient tcpClnt = new TcpClient())
293 | {
294 | tcpClnt.Connect(urib.Host, urib.Port);
295 | if (tcpClnt.Connected)
296 | {
297 | using (MemoryStream respStream = new MemoryStream())
298 | {
299 | using (NetworkStream stm = tcpClnt.GetStream())
300 | {
301 | stm.Write(request, 0, request.Length);
302 | byte[] buff = new byte[1024];
303 | int read;
304 | int offset = 5;
305 |
306 | while (0 < (read = stm.Read(buff, 0, buff.Length)))
307 | {
308 | respStream.Write(buff, offset, read - offset);
309 | offset = 0;
310 | }
311 | }
312 |
313 | returnStream = new BufferedStream(respStream);
314 | tcpClnt.Close();
315 | if (returnStream != null)
316 | {
317 | returnStream.Position = 0;
318 | }
319 |
320 | byte[] buffer = new byte[16 * 1024];
321 | using (MemoryStream ms = new MemoryStream())
322 | {
323 | int read;
324 | while ((read = returnStream.Read(buffer, 0, buffer.Length)) > 0)
325 | {
326 | ms.Write(buffer, 0, read);
327 | }
328 |
329 | returnStream.Close();
330 | return ms.ToArray();
331 | }
332 | }
333 | }
334 | else
335 | {
336 | return null;
337 | }
338 | }
339 | }
340 |
341 | ///
342 | /// Creates HTTP request and processes HTTP response.
343 | ///
344 | /// URL of a TSA service.
345 | /// DER encoded time stamp request.
346 | /// User's credentials to access TSA service.
347 | /// DER encoded time stamp response
348 | private static byte[] GetHttpResponse(string tsaUri, byte[] tsr, UserCredentials credentials = null)
349 | {
350 | HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(tsaUri);
351 | httpReq.Method = "POST";
352 | httpReq.ContentType = "application/timestamp-query";
353 |
354 | if (null != credentials)
355 | {
356 | if (null != credentials.UserSslCert)
357 | {
358 | httpReq.ClientCertificates.Add(credentials.UserSslCert);
359 | }
360 |
361 | if (null != credentials.HttpCredentials)
362 | {
363 | httpReq.Credentials = credentials.HttpCredentials;
364 | }
365 | }
366 |
367 | Stream reqStream = httpReq.GetRequestStream();
368 | reqStream.Write(tsr, 0, tsr.Length);
369 | reqStream.Close();
370 | WebResponse httpResp = httpReq.GetResponse();
371 | Stream respStream = httpResp.GetResponseStream();
372 |
373 | byte[] buffer = new byte[16 * 1024];
374 | using (MemoryStream ms = new MemoryStream())
375 | {
376 | int read;
377 | while ((read = respStream.Read(buffer, 0, buffer.Length)) > 0)
378 | {
379 | ms.Write(buffer, 0, read);
380 | }
381 |
382 | respStream.Close();
383 | return ms.ToArray();
384 | }
385 | }
386 |
387 | #endregion
388 |
389 | ///
390 | /// Validates time stamp response against time stamp request.
391 | ///
392 | /// Time-stamp request.
393 | /// Time-stamp response.
394 | private static void ValidateResponse(Request request, Response response)
395 | {
396 | if (PkiStatus.Granted == response.PKIStatus || PkiStatus.GrantedWithMods == response.PKIStatus)
397 | {
398 | if (null == response.TST)
399 | {
400 | throw new TimeStampException("Invalid TS response: missing time stamp token", response.PKIStatus);
401 | }
402 |
403 | if (!Utils.CompareByteArray(response.TST.Nonce, request.Nonce))
404 | {
405 | throw new TimeStampException("Invalid TS response: nonce mismatch", response.PKIStatus);
406 | }
407 |
408 | if (!string.IsNullOrEmpty(request.ReqPolicy) && 0 != string.CompareOrdinal(response.TST.PolicyOid, request.ReqPolicy))
409 | {
410 | throw new TimeStampException("Invalid TS response: policy mismatch", response.PKIStatus);
411 | }
412 |
413 | if (!MsgImprint.CompareImprints(response.TST.MessageImprint, request.MessageImprint))
414 | {
415 | throw new TimeStampException("Invalid TS response: message imprint mismatch", response.PKIStatus);
416 | }
417 | }
418 | else
419 | {
420 | throw new TimeStampException(string.Format(CultureInfo.InvariantCulture, "Invalid TS response. Response status: {0}", response.PKIStatus), response.PKIStatus, response.PKIStatusString, response.PKIFailureInfo);
421 | }
422 | }
423 | }
424 | }
425 |
--------------------------------------------------------------------------------
/src/TimeStampClientGui.Windows/MainForm.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2016-2021 Disig a.s.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /*
18 | * Written by:
19 | * Marek KLEIN
20 | */
21 |
22 | using System;
23 | using System.IO;
24 | using System.Net;
25 | using System.Reflection;
26 | using System.Security.Cryptography.X509Certificates;
27 | using Eto.Drawing;
28 | using Eto.Forms;
29 |
30 | namespace Disig.TimeStampClient.Gui
31 | {
32 | public class MainForm : Form
33 | {
34 | public MainForm()
35 | {
36 | this.InitializeComponent();
37 | return;
38 | }
39 |
40 | private Label SourceFileLabel
41 | {
42 | get;
43 | set;
44 | }
45 |
46 | private TextBox SourceFile
47 | {
48 | get;
49 | set;
50 | }
51 |
52 | private Label ServerAddressLabel
53 | {
54 | get;
55 | set;
56 | }
57 |
58 | private TextBox ServerAddress
59 | {
60 | get;
61 | set;
62 | }
63 |
64 | private Label HashAlgorithmLabel
65 | {
66 | get;
67 | set;
68 | }
69 |
70 | private DropDown HashAlgorithm
71 | {
72 | get;
73 | set;
74 | }
75 |
76 | private Label RequestedPolicyLabel
77 | {
78 | get;
79 | set;
80 | }
81 |
82 | private TextBox RequestedPolicy
83 | {
84 | get;
85 | set;
86 | }
87 |
88 | private Label NonceLabel
89 | {
90 | get;
91 | set;
92 | }
93 |
94 | private TextBox Nonce
95 | {
96 | get;
97 | set;
98 | }
99 |
100 | private Label TSACertificateLabel
101 | {
102 | get;
103 | set;
104 | }
105 |
106 | private DropDown TSACertificate
107 | {
108 | get;
109 | set;
110 | }
111 |
112 | private Button RequestTimeStampButton
113 | {
114 | get;
115 | set;
116 | }
117 |
118 | private Button SourceFileButton
119 | {
120 | get;
121 | set;
122 | }
123 |
124 | private Button GenerateNonce
125 | {
126 | get;
127 | set;
128 | }
129 |
130 | private Label LogAreaLabel
131 | {
132 | get;
133 | set;
134 | }
135 |
136 | private TextArea LogArea
137 | {
138 | get;
139 | set;
140 | }
141 |
142 | private Label UserCertLabel
143 | {
144 | get;
145 | set;
146 | }
147 |
148 | private TextBox UserCert
149 | {
150 | get;
151 | set;
152 | }
153 |
154 | private Button UserCertButton
155 | {
156 | get;
157 | set;
158 | }
159 |
160 | private PasswordBox UserCertPassword
161 | {
162 | get;
163 | set;
164 | }
165 |
166 | private Label UserCertPasswordLabel
167 | {
168 | get;
169 | set;
170 | }
171 |
172 | private TextBox UserName
173 | {
174 | get;
175 | set;
176 | }
177 |
178 | private Label UserNameLabel
179 | {
180 | get;
181 | set;
182 | }
183 |
184 | private PasswordBox UserPassword
185 | {
186 | get;
187 | set;
188 | }
189 |
190 | private Label UserPasswordLabel
191 | {
192 | get;
193 | set;
194 | }
195 |
196 | private Label ResponseFormatLabel
197 | {
198 | get;
199 | set;
200 | }
201 |
202 | private DropDown ResponseFormat
203 | {
204 | get;
205 | set;
206 | }
207 |
208 | private TextBox OutFile
209 | {
210 | get;
211 | set;
212 | }
213 |
214 | private Label OutFileLabel
215 | {
216 | get;
217 | set;
218 | }
219 |
220 | private Button OutFileButton
221 | {
222 | get;
223 | set;
224 | }
225 |
226 | private CheckMenuItem LogExceptions
227 | {
228 | get;
229 | set;
230 | }
231 |
232 | private void InitializeComponent()
233 | {
234 | this.InitControls();
235 | this.Content = this.CreateMainContent();
236 |
237 | Command loadXml = new Command { MenuText = "Load..." };
238 | loadXml.Executed += this.MenuConfigurationLoadOnclick;
239 |
240 | Command saveXml = new Command { MenuText = "Save as..." };
241 | saveXml.Executed += this.MenuConfigurationSaveOnclick;
242 |
243 | Command about = new Command { MenuText = "About" };
244 | about.Executed += this.AboutCommandOnClick;
245 |
246 | Command exit = new Command { MenuText = "Exit" };
247 | exit.Executed += this.ExitCommandOnClick;
248 |
249 | Command clearLog = new Command { MenuText = "Clear log" };
250 | clearLog.Executed += this.ClearLogOnClick;
251 |
252 | this.Menu = new MenuBar
253 | {
254 | Items =
255 | {
256 | new ButtonMenuItem { Text = "Application", Items = { new ButtonMenuItem(about), new ButtonMenuItem(exit) } },
257 | new ButtonMenuItem { Text = "Configuration", Items = { loadXml, saveXml } },
258 | new ButtonMenuItem { Text = "Log", Items = { new ButtonMenuItem(clearLog), this.LogExceptions } },
259 | }
260 | };
261 |
262 | Assembly assembly = Assembly.GetAssembly(typeof(MainForm));
263 | using (Stream stream = assembly.GetManifestResourceStream("Disig.TimeStampClient.Gui.TimeStampClient.ico"))
264 | {
265 | this.Icon = new Icon(stream);
266 | }
267 |
268 | this.Title = string.Format("TimeStampClient {0}", SharedUtils.AppVersion);
269 | this.MinimumSize = new Size(700, 500);
270 | this.BringToFront();
271 | }
272 |
273 | private void TextAreaLog(string msg)
274 | {
275 | this.AppendLog(msg);
276 | }
277 |
278 | private void RequestTimeStamp()
279 | {
280 | try
281 | {
282 | X509Certificate2 sslCert = null;
283 | string password = null;
284 |
285 | if (!string.IsNullOrEmpty(this.UserCertPassword.Text))
286 | {
287 | password = this.UserCertPassword.Text;
288 | }
289 |
290 | if (!string.IsNullOrEmpty(this.UserCert.Text))
291 | {
292 | sslCert = new X509Certificate2(this.UserCert.Text, password);
293 | }
294 |
295 | NetworkCredential networkCredential = null;
296 | if (!string.IsNullOrEmpty(this.UserPassword.Text) || !string.IsNullOrEmpty(this.UserName.Text))
297 | {
298 | networkCredential = new NetworkCredential(this.UserName.Text, this.UserPassword.Text);
299 | }
300 |
301 | UserCredentials credentials = null;
302 | if (networkCredential != null || sslCert != null)
303 | {
304 | credentials = new UserCredentials(sslCert, networkCredential);
305 | }
306 |
307 | TimeStampToken token = SharedUtils.RequestTimeStamp(
308 | this.ServerAddress.Text,
309 | this.SourceFile.Text,
310 | ((ComboItemHash)this.HashAlgorithm.SelectedValue).Value,
311 | this.RequestedPolicy.Text,
312 | this.Nonce.Text,
313 | ((ComboItemCertReq)this.TSACertificate.SelectedValue).Value,
314 | credentials,
315 | new LogDelegate(this.TextAreaLog),
316 | this.LogExceptions.Checked);
317 |
318 | SharedUtils.SaveInFormat(this.SourceFile.Text, token, ((ComboItemFormat)this.ResponseFormat.SelectedValue).Format, this.OutFile.Text);
319 | this.TextAreaLog(string.Format("Response saved in {0}", this.OutFile.Text));
320 | MessageBox.Show(string.Format("Time-stamp successfully received."), "Info", MessageBoxType.Information);
321 | }
322 | catch (Exception e)
323 | {
324 | MessageBox.Show(string.Format("{0}: {1}", e.GetType(), e.Message), "Error", MessageBoxType.Error);
325 | }
326 | }
327 |
328 | private void RequestTimeStampButtonOnClick(object sender, EventArgs e)
329 | {
330 | this.RequestTimeStamp();
331 | }
332 |
333 | private void InitControls()
334 | {
335 | this.SourceFileLabel = new Label { Text = "File to time-stamp" };
336 | this.SourceFile = new TextBox();
337 | this.SourceFile.TextChanged += this.SourceFileOnTextChanged;
338 |
339 | this.LogExceptions = new CheckMenuItem();
340 | this.LogExceptions.Text = "Log exceptions";
341 |
342 | this.SourceFileButton = new Button();
343 | this.SourceFileButton.Text = @"Browse";
344 | this.SourceFileButton.Click += this.SourceFileButtonOnClick;
345 |
346 | this.SourceFileButton = new Button();
347 | this.SourceFileButton.Text = @"Browse";
348 | this.SourceFileButton.Click += this.SourceFileButtonOnClick;
349 |
350 | this.OutFileLabel = new Label { Text = "Output file" };
351 | this.OutFile = new TextBox();
352 |
353 | this.OutFileButton = new Button();
354 | this.OutFileButton.Text = @"Browse";
355 | this.OutFileButton.Click += this.OutFileButtonOnClick;
356 |
357 | this.ServerAddressLabel = new Label { Text = "TSA service URL" };
358 | this.ServerAddress = new TextBox();
359 |
360 | this.HashAlgorithmLabel = new Label { Text = "Hash algorithm" };
361 | this.HashAlgorithm = new DropDown();
362 | this.HashAlgorithm.Items.Add(new ComboItemHash("SHA-1", Oid.SHA1));
363 | this.HashAlgorithm.Items.Add(new ComboItemHash("SHA-256", Oid.SHA256));
364 | this.HashAlgorithm.Items.Add(new ComboItemHash("SHA-512", Oid.SHA512));
365 | this.HashAlgorithm.Items.Add(new ComboItemHash("MD5", Oid.MD5));
366 | this.HashAlgorithm.SelectedKey = "SHA-256";
367 |
368 | this.ResponseFormatLabel = new Label { Text = "Output format" };
369 | this.ResponseFormat = new DropDown();
370 | this.ResponseFormat.Items.Add(new ComboItemFormat("Raw time-stamp token", SharedUtils.ResultFormat.RawTST));
371 | this.ResponseFormat.Items.Add(new ComboItemFormat("ASiC-S TST (ZIP file with source file and time-stamp token)", SharedUtils.ResultFormat.ASIC_S));
372 | this.ResponseFormat.SelectedKey = SharedUtils.ResultFormat.RawTST.ToString();
373 | this.ResponseFormat.SelectedValueChanged += this.ResponseFormatOnValueChanged;
374 |
375 | this.RequestedPolicyLabel = new Label { Text = "Requested policy" };
376 | this.RequestedPolicy = new TextBox();
377 |
378 | this.NonceLabel = new Label { Text = "Nonce (hex)" };
379 | this.Nonce = new TextBox();
380 |
381 | this.TSACertificateLabel = new Label { Text = "Request TSA certificate" };
382 | this.TSACertificate = new DropDown();
383 |
384 | ComboItemCertReq yes = new ComboItemCertReq(true);
385 | ComboItemCertReq no = new ComboItemCertReq(false);
386 | this.TSACertificate.Items.Add(no);
387 | this.TSACertificate.Items.Add(yes);
388 | this.TSACertificate.SelectedKey = yes.Key;
389 |
390 | this.RequestTimeStampButton = new Button();
391 | this.RequestTimeStampButton.Text = @"Request time-stamp";
392 | this.RequestTimeStampButton.Click += this.RequestTimeStampButtonOnClick;
393 |
394 | this.GenerateNonce = new Button();
395 | this.GenerateNonce.Text = @"Generate";
396 | this.GenerateNonce.Click += this.GenerateNonceBytesOnClick;
397 |
398 | this.LogAreaLabel = new Label { Text = @"Activity log" };
399 | this.LogArea = new TextArea();
400 | this.LogArea.ReadOnly = true;
401 |
402 | this.UserCertLabel = new Label { Text = @"PKCS#12 file with SSL client certificate" };
403 | this.UserCert = new TextBox();
404 |
405 | this.UserCertButton = new Button();
406 | this.UserCertButton.Text = @"Browse";
407 | this.UserCertButton.Click += this.UserCertButtonOnClick;
408 |
409 | this.UserCertPasswordLabel = new Label { Text = @"PKCS#12 file password" };
410 | this.UserCertPassword = new PasswordBox();
411 |
412 | this.UserNameLabel = new Label { Text = @"HTTP user name" };
413 | this.UserName = new TextBox();
414 |
415 | this.UserPasswordLabel = new Label { Text = @"HTTP password" };
416 | this.UserPassword = new PasswordBox();
417 | }
418 |
419 | private Layout CreateMainContent()
420 | {
421 | TabControl tabControl = new TabControl();
422 | TabPage basicPage = new TabPage();
423 | TabPage advancedPage = new TabPage();
424 | TabPage authPage = new TabPage();
425 |
426 | basicPage.Text = "TSA service";
427 | basicPage.Content = new TableLayout
428 | {
429 | Padding = 10,
430 | Spacing = new Size(5, 5),
431 | Rows =
432 | {
433 | new TableRow
434 | {
435 | Cells =
436 | {
437 | new TableCell(
438 | new TableLayout
439 | {
440 | Rows =
441 | {
442 | new TableRow(this.ServerAddressLabel),
443 | new TableRow(new TableCell(this.ServerAddress, true), string.Empty)
444 | },
445 | }, true),
446 | }
447 | },
448 |
449 | new TableRow
450 | {
451 | Cells =
452 | {
453 | new TableCell(
454 | new TableLayout
455 | {
456 | Rows =
457 | {
458 | new TableRow(this.SourceFileLabel),
459 | new TableRow(new TableCell(this.SourceFile, true), this.SourceFileButton),
460 | new TableRow { ScaleHeight = true }
461 | },
462 | }, true),
463 | }
464 | },
465 |
466 | new TableRow
467 | {
468 | Cells =
469 | {
470 | new TableCell(
471 | new TableLayout
472 | {
473 | Rows =
474 | {
475 | new TableRow(this.ResponseFormatLabel),
476 | new TableRow(new TableCell(this.ResponseFormat, true)),
477 | new TableRow { ScaleHeight = true }
478 | },
479 | }, true),
480 | }
481 | },
482 |
483 | new TableRow
484 | {
485 | Cells =
486 | {
487 | new TableCell(
488 | new TableLayout
489 | {
490 | Rows =
491 | {
492 | new TableRow(this.OutFileLabel),
493 | new TableRow(new TableCell(this.OutFile, true), this.OutFileButton),
494 | new TableRow { ScaleHeight = true }
495 | },
496 | }, true),
497 | }
498 | },
499 | }
500 | };
501 |
502 | advancedPage.Text = "Advanced";
503 | advancedPage.Content = new TableLayout
504 | {
505 | Padding = 10,
506 | Spacing = new Size(5, 5),
507 | Rows =
508 | {
509 | new TableRow
510 | {
511 | Cells =
512 | {
513 | new TableCell(
514 | new TableLayout
515 | {
516 | Rows =
517 | {
518 | new TableRow(this.HashAlgorithmLabel),
519 | new TableRow(new TableCell(this.HashAlgorithm, true), string.Empty)
520 | },
521 | }, true),
522 | }
523 | },
524 |
525 | new TableRow
526 | {
527 | Cells =
528 | {
529 | new TableCell(
530 | new TableLayout
531 | {
532 | Rows =
533 | {
534 | new TableRow(this.RequestedPolicyLabel),
535 | new TableRow(new TableCell(this.RequestedPolicy, true), string.Empty)
536 | },
537 | }, true),
538 | }
539 | },
540 |
541 | new TableRow
542 | {
543 | Cells =
544 | {
545 | new TableCell(
546 | new TableLayout
547 | {
548 | Rows =
549 | {
550 | new TableRow(this.NonceLabel),
551 | new TableRow(new TableCell(this.Nonce, true), this.GenerateNonce)
552 | },
553 | }, true),
554 | }
555 | },
556 |
557 | new TableRow
558 | {
559 | Cells =
560 | {
561 | new TableCell(
562 | new TableLayout
563 | {
564 | Rows =
565 | {
566 | new TableRow(this.TSACertificateLabel),
567 | new TableRow(new TableCell(this.TSACertificate, true), string.Empty)
568 | },
569 | }, true),
570 | }
571 | },
572 | },
573 | };
574 |
575 | authPage.Text = "Authentication";
576 | authPage.Content = new TableLayout
577 | {
578 | Padding = 10,
579 | Spacing = new Size(5, 5),
580 | Rows =
581 | {
582 | new TableRow
583 | {
584 | Cells =
585 | {
586 | new TableCell(
587 | new TableLayout
588 | {
589 | Rows =
590 | {
591 | new TableRow(this.UserNameLabel),
592 | new TableRow(new TableCell(this.UserName, true), string.Empty)
593 | },
594 | }, true),
595 | }
596 | },
597 |
598 | new TableRow
599 | {
600 | Cells =
601 | {
602 | new TableCell(
603 | new TableLayout
604 | {
605 | Rows =
606 | {
607 | new TableRow(this.UserPasswordLabel),
608 | new TableRow(new TableCell(this.UserPassword, true), string.Empty)
609 | },
610 | }, true),
611 | }
612 | },
613 |
614 | new TableRow
615 | {
616 | Cells =
617 | {
618 | new TableCell(
619 | new TableLayout
620 | {
621 | Rows =
622 | {
623 | new TableRow(this.UserCertLabel),
624 | new TableRow(new TableCell(this.UserCert, true), this.UserCertButton)
625 | },
626 | }, true),
627 | }
628 | },
629 |
630 | new TableRow
631 | {
632 | Cells =
633 | {
634 | new TableCell(
635 | new TableLayout
636 | {
637 | Rows =
638 | {
639 | new TableRow(this.UserCertPasswordLabel),
640 | new TableRow(new TableCell(this.UserCertPassword, true), string.Empty)
641 | }
642 | }, true),
643 | },
644 | },
645 | },
646 | };
647 |
648 | tabControl.Pages.Add(basicPage);
649 | tabControl.Pages.Add(advancedPage);
650 | tabControl.Pages.Add(authPage);
651 |
652 | return new TableLayout
653 | {
654 | Padding = 10,
655 | Spacing = new Size(5, 5),
656 | Rows =
657 | {
658 | tabControl,
659 |
660 | new TableRow
661 | {
662 | Cells =
663 | {
664 | new TableCell(
665 | new TableLayout
666 | {
667 | Rows =
668 | {
669 | new TableRow(new TableCell(this.RequestTimeStampButton, true))
670 | },
671 | }, true)
672 | }
673 | },
674 | new TableRow
675 | {
676 | Cells =
677 | {
678 | new TableCell(
679 | new TableLayout
680 | {
681 | Rows =
682 | {
683 | new TableRow(this.LogAreaLabel),
684 | new TableRow(new TableCell(this.LogArea, true))
685 | },
686 | }, true),
687 | },
688 | },
689 | },
690 | };
691 | }
692 |
693 | private void UserCertButtonOnClick(object sender, EventArgs e)
694 | {
695 | string fileName = this.OpenFileDialog("Select SSL certificate");
696 | if (null != fileName)
697 | {
698 | this.UserCert.Text = fileName;
699 | }
700 | }
701 |
702 | private void SourceFileButtonOnClick(object sender, EventArgs e)
703 | {
704 | string fileName = this.OpenFileDialog("Select file to time-stamp");
705 | if (null != fileName)
706 | {
707 | this.SourceFile.Text = fileName;
708 | }
709 | }
710 |
711 | private void OutFileButtonOnClick(object sender, EventArgs e)
712 | {
713 | string fileName;
714 | if (((ComboItemFormat)this.ResponseFormat.SelectedValue).Format == SharedUtils.ResultFormat.ASIC_S)
715 | {
716 | fileName = this.SaveFileDialog("Select file to save time-stamp token", new FileFilter[] { new FileFilter("ASiC-S (*.asics)", "*.asics") }, false);
717 | }
718 | else
719 | {
720 | fileName = this.SaveFileDialog("Select file to save time-stamp token", new FileFilter[] { new FileFilter("Raw time-stamp tokens (*.tst)", "*.tst") }, false);
721 | }
722 |
723 | if (null != fileName)
724 | {
725 | this.OutFile.Text = fileName;
726 | }
727 | }
728 |
729 | private string OpenFileDialog(string title, FileFilter[] filters = null, bool mustExist = true)
730 | {
731 | using (FileDialog dialog = new OpenFileDialog())
732 | {
733 | dialog.CheckFileExists = mustExist;
734 | dialog.Title = title;
735 | if (filters != null)
736 | {
737 | foreach (FileFilter filter in filters)
738 | {
739 | dialog.Filters.Add(filter);
740 | }
741 | }
742 |
743 | dialog.Filters.Add(new FileFilter("All files (*.*)", "*.*"));
744 | if (DialogResult.Ok == dialog.ShowDialog(this))
745 | {
746 | return dialog.FileName;
747 | }
748 |
749 | return null;
750 | }
751 | }
752 |
753 | private string SaveFileDialog(string title, FileFilter[] filters = null, bool mustExist = false)
754 | {
755 | using (FileDialog dialog = new SaveFileDialog())
756 | {
757 | dialog.CheckFileExists = mustExist;
758 | dialog.Title = title;
759 | if (filters != null)
760 | {
761 | foreach (FileFilter filter in filters)
762 | {
763 | dialog.Filters.Add(filter);
764 | }
765 | }
766 |
767 | dialog.Filters.Add(new FileFilter("All files (*.*)", "*.*"));
768 | if (DialogResult.Ok == dialog.ShowDialog(this))
769 | {
770 | return dialog.FileName;
771 | }
772 |
773 | return null;
774 | }
775 | }
776 |
777 | private void GenerateNonceBytesOnClick(object sender, EventArgs e)
778 | {
779 | this.Nonce.Text = SharedUtils.BytesToHexString(SharedUtils.GenerateNonceBytes());
780 | }
781 |
782 | private void SetOutFile()
783 | {
784 | this.OutFile.Text = string.Format("{0}", this.SourceFile.Text);
785 | this.SetOutFileExtension();
786 | }
787 |
788 | private void SourceFileOnTextChanged(object sender, EventArgs e)
789 | {
790 | this.SetOutFile();
791 | }
792 |
793 | private void SetOutFileExtension()
794 | {
795 | string outFileExt;
796 | string fileName;
797 | string extension;
798 |
799 | if (string.IsNullOrEmpty(this.OutFile.Text))
800 | {
801 | return;
802 | }
803 |
804 | if (((ComboItemFormat)this.ResponseFormat.SelectedValue).Format == SharedUtils.ResultFormat.ASIC_S)
805 | {
806 | outFileExt = "asics";
807 | }
808 | else
809 | {
810 | outFileExt = "tst";
811 | }
812 |
813 | extension = Path.GetExtension(this.OutFile.Text);
814 | if (extension == ".asics" || extension == ".tst")
815 | {
816 | fileName = Path.ChangeExtension(this.OutFile.Text, outFileExt);
817 | }
818 | else
819 | {
820 | fileName = string.Format("{0}.{1}", this.OutFile.Text, outFileExt);
821 | }
822 |
823 | this.OutFile.Text = string.Format("{0}", fileName);
824 | }
825 |
826 | private void ResponseFormatOnValueChanged(object sender, EventArgs e)
827 | {
828 | this.SetOutFileExtension();
829 | }
830 |
831 | private void AppendLog(string text)
832 | {
833 | this.LogArea.Append(string.Format("{0}{1}", text, Environment.NewLine), true);
834 | }
835 |
836 | private static ClientSettings LoadSettings(string fileName)
837 | {
838 | ClientSettings settings = new ClientSettings();
839 | using (System.IO.FileStream inStream = new System.IO.FileStream(fileName, System.IO.FileMode.Open))
840 | {
841 | settings = XmlSerializer.Deserialize(inStream);
842 | }
843 |
844 | return settings;
845 | }
846 |
847 | private void SetFormValues(ClientSettings settings)
848 | {
849 | this.TSACertificate.SelectedKey = settings.CertReq.ToString();
850 | this.HashAlgorithm.SelectedKey = settings.HashAlgorithm;
851 | this.Nonce.Text = settings.Nonce;
852 | this.RequestedPolicy.Text = settings.Policy;
853 | this.ServerAddress.Text = settings.ServerAddress;
854 | this.SourceFile.Text = settings.SourceFile;
855 | this.UserCert.Text = settings.UserCert;
856 | this.UserCertPassword.Text = settings.UserCertPassword;
857 | this.UserName.Text = settings.UserName;
858 | this.UserPassword.Text = settings.UserPassword;
859 | this.ResponseFormat.SelectedKey = settings.Format.ToString();
860 | this.OutFile.Text = settings.OutFile;
861 | }
862 |
863 | private void MenuConfigurationLoadOnclick(object sender, EventArgs e)
864 | {
865 | try
866 | {
867 | string fileName = this.OpenFileDialog("Select configuration", new FileFilter[] { new FileFilter("Configuration files (*.cfg)", "*.cfg") }, true);
868 | if (null == fileName)
869 | {
870 | return;
871 | }
872 |
873 | ClientSettings settings = LoadSettings(fileName);
874 | if (null == settings)
875 | {
876 | return;
877 | }
878 |
879 | this.SetFormValues(settings);
880 | }
881 | catch (Exception ex)
882 | {
883 | MessageBox.Show(ex.Message, "Error", MessageBoxType.Error);
884 | }
885 | }
886 |
887 | private static void SaveSettings(ClientSettings settings, string fileName)
888 | {
889 | using (System.IO.FileStream outStream = new System.IO.FileStream(fileName, System.IO.FileMode.Create))
890 | {
891 | XmlSerializer.Serialize(settings, outStream);
892 | }
893 | }
894 |
895 | private static bool SaveSensitiveData()
896 | {
897 | DialogResult result = MessageBox.Show("Your passwords will be saved in a plain text.\nDo you want to save your passwords in a configuration file?", "Warning", MessageBoxButtons.YesNo, MessageBoxType.Warning, MessageBoxDefaultButton.No);
898 | return (result == DialogResult.Yes);
899 | }
900 |
901 | private ClientSettings GetFormValues()
902 | {
903 | ClientSettings settings = new ClientSettings();
904 |
905 | settings.CertReq = ((ComboItemCertReq)this.TSACertificate.SelectedValue).Value;
906 | settings.HashAlgorithm = ((ComboItemHash)this.HashAlgorithm.SelectedValue).Text;
907 | settings.Nonce = this.Nonce.Text;
908 | settings.Policy = this.RequestedPolicy.Text;
909 | settings.ServerAddress = this.ServerAddress.Text;
910 | settings.SourceFile = this.SourceFile.Text;
911 | settings.UserCert = this.UserCert.Text;
912 | settings.UserName = this.UserName.Text;
913 | settings.Format = ((ComboItemFormat)this.ResponseFormat.SelectedValue).Format;
914 | settings.OutFile = this.OutFile.Text;
915 |
916 | if (!string.IsNullOrEmpty(this.UserCertPassword.Text) || !string.IsNullOrEmpty(this.UserPassword.Text))
917 | {
918 | if (SaveSensitiveData())
919 | {
920 | settings.UserCertPassword = this.UserCertPassword.Text;
921 | settings.UserPassword = this.UserPassword.Text;
922 | }
923 | }
924 |
925 | return settings;
926 | }
927 |
928 | private void MenuConfigurationSaveOnclick(object sender, EventArgs e)
929 | {
930 | try
931 | {
932 | string fileName = this.SaveFileDialog("Select file to save configuration", new FileFilter[] { new FileFilter("Configuration files (*.cfg)", "*.cfg") }, false);
933 | if (string.IsNullOrEmpty(fileName))
934 | {
935 | return;
936 | }
937 |
938 | ClientSettings settings = this.GetFormValues();
939 | SaveSettings(settings, fileName);
940 | }
941 | catch (Exception ex)
942 | {
943 | MessageBox.Show(ex.Message, MessageBoxType.Error);
944 | }
945 | }
946 |
947 | private void TerminateApplication()
948 | {
949 | this.Close();
950 |
951 | if (SharedUtils.RunningOnMacOs())
952 | Environment.Exit(0);
953 | }
954 |
955 | private void ExitCommandOnClick(object sender, EventArgs e)
956 | {
957 | this.TerminateApplication();
958 | }
959 |
960 | private void ShowAboutWindow()
961 | {
962 | using (AboutDialog about = new AboutDialog())
963 | {
964 | about.ShowModal(this);
965 | }
966 | }
967 |
968 | private void AboutCommandOnClick(object sender, EventArgs e)
969 | {
970 | this.ShowAboutWindow();
971 | }
972 |
973 | private void ClearLogArea()
974 | {
975 | this.LogArea.Text = string.Empty;
976 | }
977 |
978 | private void ClearLogOnClick(object sender, EventArgs e)
979 | {
980 | this.ClearLogArea();
981 | }
982 | }
983 | }
--------------------------------------------------------------------------------