├── 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 | ![TimeStampClient screenshot](doc/images/screenshot-windows.png?raw=true) 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 | 19 | 21 | 24 | 28 | 29 | 30 | 52 | 54 | 55 | 57 | image/svg+xml 58 | 60 | 61 | 62 | 63 | 64 | 69 | 72 | 77 | 78 | 81 | 86 | 87 | 90 | 95 | 96 | 99 | 104 | 105 | 108 | 113 | 114 | 115 | 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 | } --------------------------------------------------------------------------------