/// <summary> /// Creates the certificate via a proxy instead of calling the CryptoAPI directly. /// </summary> /// <param name="executablePath">The executable path.</param> /// <param name="storePath">The store path.</param> /// <param name="password">The password used to protect the certificate.</param> /// <param name="applicationUri">The application URI.</param> /// <param name="applicationName">Name of the application.</param> /// <param name="subjectName">Name of the subject.</param> /// <param name="domainNames">The domain names.</param> /// <param name="keySize">Size of the key.</param> /// <param name="startTime">The start time.</param> /// <param name="lifetimeInMonths">The lifetime in months.</param> /// <param name="hashSizeInBits">The hash size in bits.</param> /// <param name="isCA">if set to <c>true</c> if creating a certificate authority.</param> /// <param name="usePEMFormat">if set to <c>true</c> the private ket is store in the PEM format.</param> /// <param name="issuerKeyFilePath">The path to the PFX file containing the CA private key.</param> /// <param name="issuerKeyFilePassword">The password for the PFX file containing the CA private key.</param> /// <returns></returns> public static async Task<X509Certificate2Collection> CreateCertificateViaProxy( string executablePath, string storePath, string password, string applicationUri, string applicationName, string subjectName, IList<String> domainNames, ushort keySize, DateTime startTime, ushort lifetimeInMonths, ushort hashSizeInBits, bool isCA, bool usePEMFormat, string issuerKeyFilePath, string issuerKeyFilePassword) { // check if the proxy exists. FileInfo filePath = new FileInfo(executablePath); if (!filePath.Exists) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "Cannnot find the Opc.Ua.CertificateGenerator utility: {0}", executablePath); } // expand any strings. storePath = Utils.GetAbsoluteDirectoryPath(storePath, true, false, true); if (storePath == null) { throw ServiceResultException.Create(StatusCodes.BadInvalidArgument, "Certificate store does not exist: {0}", storePath); } // set default values. SetSuitableDefaults( ref applicationUri, ref applicationName, ref subjectName, ref domainNames, ref keySize, ref lifetimeInMonths); // reconstruct name using slash as delimeter. subjectName = ChangeSubjectNameDelimiter(subjectName, '/'); string tempFile = Path.GetTempFileName(); try { StreamWriter writer = new StreamWriter(new FileStream(tempFile, FileMode.OpenOrCreate)); writer.WriteLine("-cmd issue", storePath); if (!String.IsNullOrEmpty(storePath)) { writer.WriteLine("-storePath {0}", storePath); } if (!String.IsNullOrEmpty(applicationName)) { writer.WriteLine("-applicationName {0} ", applicationName); } if (!String.IsNullOrEmpty(subjectName)) { writer.WriteLine("-subjectName {0}", subjectName); } if (!String.IsNullOrEmpty(password)) { writer.WriteLine("-password {0}", password); } if (!isCA) { if (!String.IsNullOrEmpty(applicationUri)) { writer.WriteLine("-applicationUri {0}", applicationUri); } if (domainNames != null && domainNames.Count > 0) { StringBuilder buffer = new StringBuilder(); for (int ii = 0; ii < domainNames.Count; ii++) { if (buffer.Length > 0) { buffer.Append(","); } buffer.Append(domainNames[ii]); } writer.WriteLine("-domainNames {0}", buffer.ToString()); } } writer.WriteLine("-keySize {0}", keySize); if (startTime > DateTime.MinValue) { writer.WriteLine("-startTime {0}", startTime.Ticks - new DateTime(1601, 1, 1).Ticks); } writer.WriteLine("-lifetimeInMonths {0}", lifetimeInMonths); writer.WriteLine("-hashSize {0}", hashSizeInBits); if (isCA) { writer.WriteLine("-ca true"); } if (usePEMFormat) { writer.WriteLine("-pem true"); } if (!String.IsNullOrEmpty(issuerKeyFilePath)) { writer.WriteLine("-issuerKeyFilePath {0}", issuerKeyFilePath); } if (!String.IsNullOrEmpty(issuerKeyFilePassword)) { writer.WriteLine("-issuerKeyPassword {0}", issuerKeyFilePassword); } writer.WriteLine(""); writer.Flush(); writer.Dispose(); string result = null; string thumbprint = null; StreamReader reader = new StreamReader(new FileStream(tempFile, FileMode.Open)); try { while ((result = reader.ReadLine()) != null) { if (String.IsNullOrEmpty(result)) { continue; } if (result.StartsWith("-cmd")) { throw new ServiceResultException("Input file was not processed properly."); } if (result.StartsWith("-thumbprint")) { thumbprint = result.Substring("-thumbprint".Length).Trim(); break; } if (result.StartsWith("-error")) { throw new ServiceResultException(result); } } } finally { reader.Dispose(); } // load the new certificate from the store. ICertificateStore store = new Opc.Ua.DirectoryCertificateStore(); await store.Open(storePath); X509Certificate2Collection certificates = await store.FindByThumbprint(thumbprint); store.Close(); return certificates; } catch (Exception e) { throw new ServiceResultException("Could not create a certificate via a proxy: " + e.Message, e); } finally { if (tempFile != null) { try { File.Delete(tempFile); } catch {} } } }
/// <summary> /// Creates a self signed application instance certificate. /// </summary> /// <param name="storeType">Type of certificate store (Windows or Directory) <see cref="CertificateStoreType"/>.</param> /// <param name="storePath">The store path (syntax depends on storeType).</param> /// <param name="password">The password to use to protect the certificate.</param> /// <param name="applicationUri">The application uri (created if not specified).</param> /// <param name="applicationName">Name of the application (optional if subjectName is specified).</param> /// <param name="subjectName">The subject used to create the certificate (optional if applicationName is specified).</param> /// <param name="domainNames">The domain names that can be used to access the server machine (defaults to local computer name if not specified).</param> /// <param name="keySize">Size of the key (1024, 2048 or 4096).</param> /// <param name="startTime">The start time.</param> /// <param name="lifetimeInMonths">The lifetime of the key in months.</param> /// <param name="hashSizeInBits">The hash size in bits.</param> /// <param name="isCA">if set to <c>true</c> the a CA certificate is created.</param> /// <param name="usePEMFormat">if set to <c>true</c> the private ket is store in the PEM format.</param> /// <param name="issuerKeyFilePath">The path to the PFX file containing the CA private key.</param> /// <param name="issuerKeyFilePassword">The password for the PFX file containing the CA private key.</param> /// <returns>The certificate with a private key.</returns> public static async Task<X509Certificate2> CreateCertificate( string storeType, string storePath, string password, string applicationUri, string applicationName, string subjectName, IList<String> domainNames, ushort keySize, DateTime startTime, ushort lifetimeInMonths, ushort hashSizeInBits, bool isCA, bool usePEMFormat, string issuerKeyFilePath, string issuerKeyFilePassword) { // use the proxy if create a certificate in a directory store. if (storeType == CertificateStoreType.Directory) { string executablePath = GetCertificateGeneratorPath(); if (String.IsNullOrEmpty(executablePath)) { throw ServiceResultException.Create(StatusCodes.BadConfigurationError, "The CertificateGenerator utility is not installed."); } X509Certificate2Collection certificates = await CreateCertificateViaProxy( executablePath, storePath, password, applicationUri, applicationName, subjectName, domainNames, keySize, startTime, lifetimeInMonths, hashSizeInBits, isCA, usePEMFormat, issuerKeyFilePath, issuerKeyFilePassword); if (certificates.Count > 0) { return certificates[0]; } } if (isCA) { throw new NotSupportedException("Cannot create a CA certificate in a Windows store at this time."); } if (!String.IsNullOrEmpty(password)) { throw new NotSupportedException("Cannot add password when creating a certificate in a Windows store at this time."); } if (!String.IsNullOrEmpty(issuerKeyFilePath)) { throw new NotSupportedException("Cannot use a CA certificate to create a certificate in a Windows store at this time."); } // set default values. SetSuitableDefaults( ref applicationUri, ref applicationName, ref subjectName, ref domainNames, ref keySize, ref lifetimeInMonths); // create the certificate. X509Certificate2 certificate = await CreateCertificate( applicationUri, applicationName, subjectName.ToString(), domainNames, keySize, lifetimeInMonths); // add it to the store. if (!String.IsNullOrEmpty(storePath)) { ICertificateStore store = null; if (storeType == CertificateStoreType.Windows) { store = new Opc.Ua.WindowsCertificateStore(); } else { store = new Opc.Ua.DirectoryCertificateStore(); } await store.Open(storePath); await store.Add(certificate); } return certificate; }