/// <summary> /// Add the specified certificate record. /// </summary> /// <remarks> /// Adds the specified certificate record to the database. /// </remarks> /// <param name="record">The certificate record.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="record"/> is <c>null</c>. /// </exception> public void Add(X509CertificateRecord record) { if (record == null) { throw new ArgumentNullException("record"); } using (var command = GetInsertCommand(record)) { command.ExecuteNonQuery(); } }
/// <summary> /// Remove the specified certificate record. /// </summary> /// <remarks> /// Removes the specified certificate record from the database. /// </remarks> /// <param name="record">The certificate record.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="record"/> is <c>null</c>. /// </exception> public void Remove(X509CertificateRecord record) { if (record == null) { throw new ArgumentNullException(nameof(record)); } using (var command = GetDeleteCommand(record)) { command.ExecuteNonQuery(); } }
/// <summary> /// Update the specified certificate record. /// </summary> /// <remarks> /// Updates the specified fields of the record in the database. /// </remarks> /// <param name="record">The certificate record.</param> /// <param name="fields">The fields to update.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="record"/> is <c>null</c>. /// </exception> public void Update(X509CertificateRecord record, X509CertificateRecordFields fields) { if (record == null) { throw new ArgumentNullException(nameof(record)); } using (var command = GetUpdateCommand(record, fields)) { command.ExecuteNonQuery(); } }
/// <summary> /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. /// </summary> /// <remarks> /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. /// </remarks> /// <param name="certificate">The certificate.</param> /// <param name="algorithms">The encryption algorithm capabilities of the client (in preferred order).</param> /// <param name="timestamp">The timestamp in coordinated universal time (UTC).</param> protected override void UpdateSecureMimeCapabilities(X509Certificate certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp) { X509CertificateRecord record; if ((record = dbase.Find(certificate, AlgorithmFields)) == null) { record = new X509CertificateRecord(certificate); record.AlgorithmsUpdated = timestamp; record.Algorithms = algorithms; dbase.Add(record); } else if (timestamp > record.AlgorithmsUpdated) { record.AlgorithmsUpdated = timestamp; record.Algorithms = algorithms; dbase.Update(record, AlgorithmFields); } }
/// <summary> /// Gets the database command to insert the specified certificate record. /// </summary> /// <remarks> /// Gets the database command to insert the specified certificate record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> protected override DbCommand GetInsertCommand(X509CertificateRecord record) { var statement = new StringBuilder("INSERT INTO CERTIFICATES("); var variables = new StringBuilder("VALUES("); var command = Connection.CreateCommand(); var columns = CertificatesTable.Columns; for (int i = 1; i < columns.Count; i++) { if (i > 1) { statement.Append(", "); variables.Append(", "); } var value = GetValue(record, columns[i].ColumnName); if (value is DateTime dateTime && dateTime < DateUtils.UnixEpoch) { value = DateUtils.UnixEpoch; } if (columns[i].ColumnName == "PRIVATEKEY" && value is DBNull) { value = new byte[0]; } var variable = "@" + columns[i]; command.AddParameterWithValue(variable, value); statement.Append(columns[i]); variables.Append(variable); } statement.Append(')'); variables.Append(')'); command.CommandText = statement + " " + variables; command.CommandType = CommandType.Text; return(command); }
/// <summary> /// Imports a DER-encoded certificate stream. /// </summary> /// <remarks> /// Imports all of the certificates in the DER-encoded stream. /// </remarks> /// <param name="stream">The raw certificate(s).</param> /// <param name="trusted"><c>true</c> if the certificates are trusted.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="stream"/> is <c>null</c>. /// </exception> public void Import(Stream stream, bool trusted) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } var parser = new X509CertificateParser(); foreach (X509Certificate certificate in parser.ReadCertificates(stream)) { if (dbase.Find(certificate, X509CertificateRecordFields.Id) != null) { continue; } var record = new X509CertificateRecord(certificate); record.IsTrusted = trusted; dbase.Add(record); } }
/// <summary> /// Gets the value for the specified column. /// </summary> /// <remarks> /// Gets the value for the specified column. /// </remarks> /// <returns>The value.</returns> /// <param name="record">The certificate record.</param> /// <param name="columnName">The column name.</param> /// <exception cref="System.ArgumentException"> /// <paramref name="columnName"/> is not a known column name. /// </exception> protected object GetValue(X509CertificateRecord record, string columnName) { switch (columnName) { //case "ID": return record.Id; case "BASICCONSTRAINTS": return(record.BasicConstraints); case "TRUSTED": return(record.IsTrusted); case "ANCHOR": return(record.IsAnchor); case "KEYUSAGE": return((int)record.KeyUsage); case "NOTBEFORE": return(record.NotBefore.ToUniversalTime()); case "NOTAFTER": return(record.NotAfter.ToUniversalTime()); case "ISSUERNAME": return(record.IssuerName); case "SERIALNUMBER": return(record.SerialNumber); case "SUBJECTNAME": return(record.SubjectName); case "SUBJECTKEYIDENTIFIER": return(record.SubjectKeyIdentifier?.AsHex()); case "SUBJECTEMAIL": return(record.SubjectEmail != null?record.SubjectEmail.ToLowerInvariant() : string.Empty); case "FINGERPRINT": return(record.Fingerprint.ToLowerInvariant()); case "ALGORITHMS": return(EncodeEncryptionAlgorithms(record.Algorithms)); case "ALGORITHMSUPDATED": return(record.AlgorithmsUpdated); case "CERTIFICATE": return(record.Certificate.GetEncoded()); case "PRIVATEKEY": return(EncodePrivateKey(record.PrivateKey)); default: throw new ArgumentException(string.Format("Unknown column name: {0}", columnName), nameof(columnName)); } }
/// <summary> /// Imports a certificate. /// </summary> /// <remarks> /// <para>Imports the certificate.</para> /// <para>If the certificate already exists in the database and <paramref name="trusted"/> is <c>true</c>, /// then the IsTrusted state is updated otherwise the certificate is added to the database with the /// specified trust.</para> /// </remarks> /// <param name="certificate">The certificate.</param> /// <param name="trusted"><c>true</c> if the certificate is trusted; otherwise, <c>false</c>.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="certificate"/> is <c>null</c>. /// </exception> public void Import(X509Certificate certificate, bool trusted) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } X509CertificateRecord record; if ((record = dbase.Find(certificate, X509CertificateRecordFields.Id | X509CertificateRecordFields.Trusted)) != null) { if (trusted && !record.IsTrusted) { record.IsTrusted = trusted; dbase.Update(record, X509CertificateRecordFields.Trusted); } return; } record = new X509CertificateRecord(certificate); record.IsTrusted = trusted; dbase.Add(record); }
X509CertificateRecord LoadCertificateRecord(DbDataReader reader, X509CertificateParser parser, ref byte[] buffer) { var record = new X509CertificateRecord(); for (int i = 0; i < reader.FieldCount; i++) { switch (reader.GetName(i).ToUpperInvariant()) { case "CERTIFICATE": record.Certificate = DecodeCertificate(reader, parser, i, ref buffer); break; case "PRIVATEKEY": record.PrivateKey = DecodePrivateKey(reader, i, ref buffer); break; case "ALGORITHMS": record.Algorithms = DecodeEncryptionAlgorithms(reader, i); break; case "ALGORITHMSUPDATED": record.AlgorithmsUpdated = DateTime.SpecifyKind(reader.GetDateTime(i), DateTimeKind.Utc); break; case "TRUSTED": record.IsTrusted = reader.GetBoolean(i); break; case "ID": record.Id = reader.GetInt32(i); break; } } return(record); }
/// <summary> /// Remove the specified certificate record. /// </summary> /// <remarks> /// Removes the specified certificate record from the database. /// </remarks> /// <param name="record">The certificate record.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="record"/> is <c>null</c>. /// </exception> public void Remove (X509CertificateRecord record) { if (record == null) throw new ArgumentNullException ("record"); using (var command = GetDeleteCommand (record)) { command.ExecuteNonQuery (); } }
/// <summary> /// Gets the database command to update the specified record. /// </summary> /// <remarks> /// Gets the database command to update the specified record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> /// <param name="fields">The fields to update.</param> protected abstract IDbCommand GetUpdateCommand (X509CertificateRecord record, X509CertificateRecordFields fields);
/// <summary> /// Gets the database command to insert the specified certificate record. /// </summary> /// <remarks> /// Gets the database command to insert the specified certificate record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> protected abstract IDbCommand GetInsertCommand (X509CertificateRecord record);
/// <summary> /// Gets the value for the specified column. /// </summary> /// <remarks> /// Gets the value for the specified column. /// </remarks> /// <returns>The value.</returns> /// <param name="record">The certificate record.</param> /// <param name="columnName">The column name.</param> /// <exception cref="System.ArgumentException"> /// <paramref name="columnName"/> is not a known column name. /// </exception> protected object GetValue (X509CertificateRecord record, string columnName) { switch (columnName) { case "ID": return record.Id; case "BASICCONSTRAINTS": return record.BasicConstraints; case "TRUSTED": return record.IsTrusted; case "KEYUSAGE": return (int) record.KeyUsage; case "NOTBEFORE": return record.NotBefore; case "NOTAFTER": return record.NotAfter; case "ISSUERNAME": return record.IssuerName; case "SERIALNUMBER": return record.SerialNumber; case "SUBJECTEMAIL": return record.SubjectEmail != null ? record.SubjectEmail.ToLowerInvariant () : string.Empty; case "FINGERPRINT": return record.Fingerprint.ToLowerInvariant (); case "ALGORITHMS": return EncodeEncryptionAlgorithms (record.Algorithms); case "ALGORITHMSUPDATED": return record.AlgorithmsUpdated; case "CERTIFICATE": return record.Certificate.GetEncoded (); case "PRIVATEKEY": return EncodePrivateKey (record.PrivateKey); default: throw new ArgumentException (string.Format ("Unknown column name: {0}", columnName), "columnName"); } }
/// <summary> /// Gets the database command to delete the specified certificate record. /// </summary> /// <remarks> /// Gets the database command to delete the specified certificate record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> protected abstract IDbCommand GetDeleteCommand (X509CertificateRecord record);
X509CertificateRecord LoadCertificateRecord (IDataRecord reader, X509CertificateParser parser, ref byte[] buffer) { var record = new X509CertificateRecord (); for (int i = 0; i < reader.FieldCount; i++) { switch (reader.GetName (i).ToUpperInvariant ()) { case "CERTIFICATE": record.Certificate = DecodeCertificate (reader, parser, i, ref buffer); break; case "PRIVATEKEY": record.PrivateKey = DecodePrivateKey (reader, i, ref buffer); break; case "ALGORITHMS": record.Algorithms = DecodeEncryptionAlgorithms (reader, i); break; case "ALGORITHMSUPDATED": record.AlgorithmsUpdated = reader.GetDateTime (i); break; case "TRUSTED": record.IsTrusted = reader.GetBoolean (i); break; case "ID": record.Id = reader.GetInt32 (i); break; } } return record; }
/// <summary> /// Gets the database command to insert the specified certificate record. /// </summary> /// <remarks> /// Gets the database command to insert the specified certificate record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> protected abstract DbCommand GetInsertCommand(X509CertificateRecord record);
/// <summary> /// Imports certificates and keys from a pkcs12-encoded stream. /// </summary> /// <remarks> /// Imports all of the certificates and keys from the pkcs12-encoded stream. /// </remarks> /// <param name="stream">The raw certificate and key data.</param> /// <param name="password">The password to unlock the data.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="stream"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="password"/> is <c>null</c>.</para> /// </exception> /// <exception cref="Org.BouncyCastle.Cms.CmsException"> /// An error occurred in the cryptographic message syntax subsystem. /// </exception> public override void Import(Stream stream, string password) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (password == null) { throw new ArgumentNullException(nameof(password)); } var pkcs12 = new Pkcs12Store(stream, password.ToCharArray()); var enabledAlgorithms = EnabledEncryptionAlgorithms; X509CertificateRecord record; foreach (string alias in pkcs12.Aliases) { if (pkcs12.IsKeyEntry(alias)) { var chain = pkcs12.GetCertificateChain(alias); var entry = pkcs12.GetKey(alias); int startIndex = 0; if (entry.Key.IsPrivate) { if ((record = dbase.Find(chain[0].Certificate, ImportPkcs12Fields)) == null) { record = new X509CertificateRecord(chain[0].Certificate, entry.Key); record.AlgorithmsUpdated = DateTime.UtcNow; record.Algorithms = enabledAlgorithms; record.IsTrusted = true; dbase.Add(record); } else { record.AlgorithmsUpdated = DateTime.UtcNow; record.Algorithms = enabledAlgorithms; if (record.PrivateKey == null) { record.PrivateKey = entry.Key; } record.IsTrusted = true; dbase.Update(record, ImportPkcs12Fields); } startIndex = 1; } for (int i = startIndex; i < chain.Length; i++) { Import(chain[i].Certificate, true); } } else if (pkcs12.IsCertificateEntry(alias)) { var entry = pkcs12.GetCertificate(alias); Import(entry.Certificate, true); } } }
SqliteCommand GetDeleteCommand(X509CertificateRecord record) { var command = sqlite.CreateCommand (); command.CommandText = "DELETE FROM CERTIFICATES WHERE ID = @ID"; command.Parameters.AddWithValue ("@ID", record.Id); command.CommandType = CommandType.Text; return command; }
/// <summary> /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. /// </summary> /// <remarks> /// Updates the known S/MIME capabilities of the client used by the recipient that owns the specified certificate. /// </remarks> /// <param name="certificate">The certificate.</param> /// <param name="algorithms">The encryption algorithm capabilities of the client (in preferred order).</param> /// <param name="timestamp">The timestamp in coordinated universal time (UTC).</param> protected override void UpdateSecureMimeCapabilities (X509Certificate certificate, EncryptionAlgorithm[] algorithms, DateTime timestamp) { X509CertificateRecord record; if ((record = dbase.Find (certificate, AlgorithmFields)) == null) { record = new X509CertificateRecord (certificate); record.AlgorithmsUpdated = timestamp; record.Algorithms = algorithms; dbase.Add (record); } else if (timestamp > record.AlgorithmsUpdated) { record.AlgorithmsUpdated = timestamp; record.Algorithms = algorithms; dbase.Update (record, AlgorithmFields); } }
/// <summary> /// Imports certificates and keys from a pkcs12-encoded stream. /// </summary> /// <remarks> /// Imports all of the certificates and keys from the pkcs12-encoded stream. /// </remarks> /// <param name="stream">The raw certificate and key data.</param> /// <param name="password">The password to unlock the data.</param> /// <exception cref="System.ArgumentNullException"> /// <para><paramref name="stream"/> is <c>null</c>.</para> /// <para>-or-</para> /// <para><paramref name="password"/> is <c>null</c>.</para> /// </exception> /// <exception cref="Org.BouncyCastle.Cms.CmsException"> /// An error occurred in the cryptographic message syntax subsystem. /// </exception> public override void Import (Stream stream, string password) { if (stream == null) throw new ArgumentNullException ("stream"); if (password == null) throw new ArgumentNullException ("password"); var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ()); var enabledAlgorithms = EnabledEncryptionAlgorithms; X509CertificateRecord record; foreach (string alias in pkcs12.Aliases) { if (pkcs12.IsKeyEntry (alias)) { var chain = pkcs12.GetCertificateChain (alias); var entry = pkcs12.GetKey (alias); int startIndex = 0; if (entry.Key.IsPrivate) { if ((record = dbase.Find (chain[0].Certificate, ImportPkcs12Fields)) == null) { record = new X509CertificateRecord (chain[0].Certificate, entry.Key); record.AlgorithmsUpdated = DateTime.UtcNow; record.Algorithms = enabledAlgorithms; record.IsTrusted = true; dbase.Add (record); } else { record.AlgorithmsUpdated = DateTime.UtcNow; record.Algorithms = enabledAlgorithms; if (record.PrivateKey == null) record.PrivateKey = entry.Key; record.IsTrusted = true; dbase.Update (record, ImportPkcs12Fields); } startIndex = 1; } for (int i = startIndex; i < chain.Length; i++) { if ((record = dbase.Find (chain[i].Certificate, X509CertificateRecordFields.Id)) == null) dbase.Add (new X509CertificateRecord (chain[i].Certificate)); } } else if (pkcs12.IsCertificateEntry (alias)) { var entry = pkcs12.GetCertificate (alias); if ((record = dbase.Find (entry.Certificate, X509CertificateRecordFields.Id)) == null) dbase.Add (new X509CertificateRecord (entry.Certificate)); } } }
/// <summary> /// Update the specified certificate record. /// </summary> /// <remarks> /// Updates the specified fields of the record in the database. /// </remarks> /// <param name="record">The certificate record.</param> /// <param name="fields">The fields to update.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="record"/> is <c>null</c>. /// </exception> public void Update (X509CertificateRecord record, X509CertificateRecordFields fields) { if (record == null) throw new ArgumentNullException ("record"); using (var command = GetUpdateCommand (record, fields)) { command.ExecuteNonQuery (); } }
object GetValue(X509CertificateRecord record, string columnName) { switch (columnName) { case "ID": return record.Id; case "BASICCONSTRAINTS": return record.BasicConstraints; case "TRUSTED": return record.IsTrusted; case "KEYUSAGE": return record.KeyUsage; case "NOTBEFORE": return record.NotBefore; case "NOTAFTER": return record.NotAfter; case "ISSUERNAME": return record.IssuerName; case "SERIALNUMBER": return record.SerialNumber; case "SUBJECTEMAIL": return record.SubjectEmail; case "FINGERPRINT": return record.Fingerprint; case "ALGORITHMS": return EncodeEncryptionAlgorithms (record.Algorithms); case "ALGORITHMSUPDATED": return record.AlgorithmsUpdated; case "CERTIFICATE": return record.Certificate.GetEncoded (); case "PRIVATEKEY": return EncodePrivateKey (record.PrivateKey); default: throw new ArgumentOutOfRangeException (); } }
/// <summary> /// Imports a DER-encoded certificate stream. /// </summary> /// <remarks> /// Imports all of the certificates in the DER-encoded stream. /// </remarks> /// <param name="stream">The raw certificate(s).</param> /// <param name="trusted"><c>true</c> if the certificates are trusted.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="stream"/> is <c>null</c>. /// </exception> public void Import (Stream stream, bool trusted) { if (stream == null) throw new ArgumentNullException ("stream"); var parser = new X509CertificateParser (); foreach (X509Certificate certificate in parser.ReadCertificates (stream)) { if (dbase.Find (certificate, X509CertificateRecordFields.Id) != null) continue; var record = new X509CertificateRecord (certificate); record.IsTrusted = trusted; dbase.Add (record); } }
/// <summary> /// Gets the database command to delete the specified certificate record. /// </summary> /// <remarks> /// Gets the database command to delete the specified certificate record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> protected override DbCommand GetDeleteCommand (X509CertificateRecord record) { var command = connection.CreateCommand (); command.CommandText = "DELETE FROM CERTIFICATES WHERE ID = @ID"; command.AddParameterWithValue ("@ID", record.Id); command.CommandType = CommandType.Text; return command; }
/// <summary> /// Gets the database command to insert the specified certificate record. /// </summary> /// <remarks> /// Gets the database command to insert the specified certificate record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> protected override DbCommand GetInsertCommand (X509CertificateRecord record) { var statement = new StringBuilder ("INSERT INTO CERTIFICATES("); var columns = X509CertificateRecord.ColumnNames; var variables = new StringBuilder ("VALUES("); var command = connection.CreateCommand (); for (int i = 1; i < columns.Length; i++) { if (i > 1) { statement.Append (", "); variables.Append (", "); } var value = GetValue (record, columns[i]); var variable = "@" + columns[i]; command.AddParameterWithValue (variable, value); statement.Append (columns[i]); variables.Append (variable); } statement.Append (')'); variables.Append (')'); command.CommandText = statement + " " + variables; command.CommandType = CommandType.Text; return command; }
/// <summary> /// Gets the database command to delete the specified certificate record. /// </summary> /// <remarks> /// Gets the database command to delete the specified certificate record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> protected abstract DbCommand GetDeleteCommand(X509CertificateRecord record);
/// <summary> /// Gets the database command to update the specified record. /// </summary> /// <remarks> /// Gets the database command to update the specified record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> /// <param name="fields">The fields to update.</param> protected override DbCommand GetUpdateCommand (X509CertificateRecord record, X509CertificateRecordFields fields) { var statement = new StringBuilder ("UPDATE CERTIFICATES SET "); var columns = GetColumnNames (fields & ~X509CertificateRecordFields.Id); var command = connection.CreateCommand (); for (int i = 0; i < columns.Length; i++) { var value = GetValue (record, columns[i]); var variable = "@" + columns[i]; if (i > 0) statement.Append (", "); statement.Append (columns[i]); statement.Append (" = "); statement.Append (variable); command.AddParameterWithValue (variable, value); } statement.Append (" WHERE ID = @ID"); command.AddParameterWithValue ("@ID", record.Id); command.CommandText = statement.ToString (); command.CommandType = CommandType.Text; return command; }
/// <summary> /// Gets the database command to update the specified record. /// </summary> /// <remarks> /// Gets the database command to update the specified record. /// </remarks> /// <returns>The database command.</returns> /// <param name="record">The certificate record.</param> /// <param name="fields">The fields to update.</param> protected abstract DbCommand GetUpdateCommand(X509CertificateRecord record, X509CertificateRecordFields fields);
/// <summary> /// Add the specified certificate record. /// </summary> /// <remarks> /// Adds the specified certificate record to the database. /// </remarks> /// <param name="record">The certificate record.</param> /// <exception cref="System.ArgumentNullException"> /// <paramref name="record"/> is <c>null</c>. /// </exception> public void Add (X509CertificateRecord record) { if (record == null) throw new ArgumentNullException (nameof (record)); using (var command = GetInsertCommand (record)) { command.ExecuteNonQuery (); } }