/// <summary> /// Adds a Base64 signature to a feed or catalog stream. /// </summary> /// <param name="stream">The feed or catalog to sign.</param> /// <param name="secretKey">The secret key to use for signing the file.</param> /// <param name="passphrase">The passphrase to use to unlock the key.</param> /// <param name="openPgp">The OpenPGP-compatible system used to create signatures.</param> /// <exception cref="IOException">The OpenPGP implementation could not be launched or the file could not be read or written.</exception> /// <exception cref="WrongPassphraseException">Passphrase was incorrect.</exception> /// <remarks> /// The file is not parsed before signing; invalid XML files are signed as well. /// The existing file must end with a line break. /// Old signatures are not removed. /// </remarks> public static void SignFeed([NotNull] Stream stream, [NotNull] OpenPgpSecretKey secretKey, string passphrase, [NotNull] IOpenPgp openPgp) { #region Sanity checks if (stream == null) { throw new ArgumentNullException("stream"); } if (secretKey == null) { throw new ArgumentNullException("secretKey"); } if (openPgp == null) { throw new ArgumentNullException("openPgp"); } #endregion // Calculate the signature in-memory string signature = openPgp.DetachSign(stream, secretKey.Fingerprint, passphrase); // Add the signature to the end of the file var writer = new StreamWriter(stream, encoding: Store.Feeds.FeedUtils.Encoding) { NewLine = "\n" }; writer.Write(Store.Feeds.FeedUtils.SignatureBlockStart); writer.WriteLine(signature); writer.Write(Store.Feeds.FeedUtils.SignatureBlockEnd); writer.Flush(); }
/// <summary> /// Adds a Base64 signature to a feed or catalog stream. /// </summary> /// <param name="stream">The feed or catalog to sign.</param> /// <param name="secretKey">The secret key to use for signing the file.</param> /// <param name="passphrase">The passphrase to use to unlock the key.</param> /// <param name="openPgp">The OpenPGP-compatible system used to create signatures.</param> /// <exception cref="IOException">The file could not be read or written.</exception> /// <exception cref="UnauthorizedAccessException">Read or write access to the file is not permitted.</exception> /// <exception cref="KeyNotFoundException">The specified <paramref name="secretKey"/> could not be found on the system.</exception> /// <exception cref="WrongPassphraseException"><paramref name="passphrase"/> was incorrect.</exception> /// <remarks> /// The file is not parsed before signing; invalid XML files are signed as well. /// The existing file must end with a line break. /// Old signatures are not removed. /// </remarks> public static void SignFeed(Stream stream, OpenPgpSecretKey secretKey, string?passphrase, IOpenPgp openPgp) { #region Sanity checks if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (secretKey == null) { throw new ArgumentNullException(nameof(secretKey)); } if (openPgp == null) { throw new ArgumentNullException(nameof(openPgp)); } #endregion // Calculate the signature in-memory var signature = openPgp.Sign(stream.ReadAll(), secretKey, passphrase); // Add the signature to the end of the file var writer = new StreamWriter(stream, EncodingUtils.Utf8) { NewLine = "\n" }; writer.Write(StoreFeedUtils.SignatureBlockStart); writer.WriteLine(Convert.ToBase64String(signature)); writer.Write(StoreFeedUtils.SignatureBlockEnd); writer.Flush(); }
/// <summary> /// Creates a new signed feed. /// </summary> /// <param name="feed">The wrapped <see cref="Feed"/>.</param> /// <param name="secretKey">The secret key used to sign the <see cref="Feed"/>; <c>null</c> for no signature.</param> public SignedFeed([NotNull] Feed feed, [CanBeNull] OpenPgpSecretKey secretKey = null) { #region Sanity checks if (feed == null) throw new ArgumentNullException(nameof(feed)); #endregion Feed = feed; SecretKey = secretKey; }
/// <inheritdoc/> public string ExportKey(OpenPgpSecretKey secretKey) { #region Sanity checks if (secretKey == null) throw new ArgumentNullException("secretKey"); #endregion return new CliControl(_homeDir).Execute("--batch", "--no-secmem-warning", "--armor", "--export", secretKey.KeyID) .Replace(Environment.NewLine, "\n") + "\n"; }
/// <summary> /// Creates a new signed catalog. /// </summary> /// <param name="catalog">The wrapped <see cref="Catalog"/>.</param> /// <param name="secretKey">The secret key used to sign the <see cref="Catalog"/>; <c>null</c> for no signature.</param> public SignedCatalog([NotNull] Catalog catalog, [CanBeNull] OpenPgpSecretKey secretKey) { #region Sanity checks if (catalog == null) throw new ArgumentNullException(nameof(catalog)); #endregion Catalog = catalog; SecretKey = secretKey; }
/// <summary> /// Creates a new signed catalog. /// </summary> /// <param name="catalog">The wrapped <see cref="Catalog"/>.</param> /// <param name="secretKey">The secret key used to sign the <see cref="Catalog"/>; <see langword="null"/> for no signature.</param> public SignedCatalog([NotNull] Catalog catalog, [CanBeNull] OpenPgpSecretKey secretKey) { #region Sanity checks if (catalog == null) { throw new ArgumentNullException("catalog"); } #endregion Catalog = catalog; SecretKey = secretKey; }
/// <summary> /// Creates a new signed feed. /// </summary> /// <param name="feed">The wrapped <see cref="Feed"/>.</param> /// <param name="secretKey">The secret key used to sign the <see cref="Feed"/>; <c>null</c> for no signature.</param> public SignedFeed([NotNull] Feed feed, [CanBeNull] OpenPgpSecretKey secretKey = null) { #region Sanity checks if (feed == null) { throw new ArgumentNullException(nameof(feed)); } #endregion Feed = feed; SecretKey = secretKey; }
/// <summary> /// Signs a number of <see cref="Feed"/>s with a single <see cref="OpenPgpSecretKey"/>. /// </summary> /// <param name="secretKey">The private key to use for signing the files.</param> /// <param name="passphrase">The passphrase to use to unlock the key.</param> /// <exception cref="IOException">The feed file could not be read or written.</exception> /// <exception cref="UnauthorizedAccessException">Read or write access to the feed file is not permitted.</exception> private void SignFiles(OpenPgpSecretKey secretKey, string passphrase) { var task = ForEachTask.Create("Signing feeds", _files, file => { SignedFeed signedFeed; try { signedFeed = SignedFeed.Load(file.FullName); } #region Error handling catch (UnauthorizedAccessException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } catch (InvalidDataException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message + (ex.InnerException == null ? "" : Environment.NewLine + ex.InnerException.Message), ex); } #endregion signedFeed.SecretKey = secretKey; try { signedFeed.Save(file.FullName, passphrase); } #region Error handling catch (UnauthorizedAccessException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } catch (KeyNotFoundException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } catch (WrongPassphraseException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } #endregion }); using (var handler = new DialogTaskHandler(this)) handler.RunTask(task); Msg.Inform(this, "Successfully signed files.", MsgSeverity.Info); }
public void TestDeployPublicKey() { using (var tempDir = new TemporaryDirectory("0install-unit-tests")) { var secretKey = new OpenPgpSecretKey("fingerprint", "key", "*****@*****.**", new DateTime(2000, 1, 1), OpenPgpAlgorithm.Rsa, 128); const string publicKey = "public"; var openPgpMock = MockRepository.Create <IOpenPgp>(); openPgpMock.Setup(x => x.GetPublicKey(secretKey.Fingerprint)).Returns(publicKey); FeedUtils.DeployPublicKey(tempDir.Path, secretKey, openPgpMock.Object); Assert.AreEqual(publicKey, File.ReadAllText(tempDir + Path.DirectorySeparatorChar + secretKey.KeyID + ".gpg"), "Public key should be written to parallel file in directory"); } }
public void TestDeployPublicKey() { using (var tempDir = new TemporaryDirectory("0install-unit-tests")) { const string publicKey = "public"; var secretKey = new OpenPgpSecretKey(keyID: 123, fingerprint: new byte[] { 1, 2, 3 }, userID: "user"); var openPgpMock = CreateMock<IOpenPgp>(); openPgpMock.Setup(x => x.ExportKey(secretKey)).Returns(publicKey); openPgpMock.Object.DeployPublicKey(secretKey, tempDir.Path); File.ReadAllText(tempDir + Path.DirectorySeparatorChar + secretKey.FormatKeyID() + ".gpg") .Should().Be(publicKey, because: "Public key should be written to parallel file in directory"); } }
public void TestSignFeed() { using (var stream = new MemoryStream()) { var feed = FeedTest.CreateTestFeed(); const string passphrase = "passphrase123"; var signature = new byte[] { 1, 2, 3 }; var secretKey = new OpenPgpSecretKey(keyID: 123, fingerprint: new byte[] { 1, 2, 3 }, userID: "user"); var openPgpMock = CreateMock <IOpenPgp>(); openPgpMock.Setup(x => x.Sign(It.IsAny <byte[]>(), secretKey, passphrase)) .Returns(signature); feed.SaveXml(stream); FeedUtils.SignFeed(stream, secretKey, passphrase, openPgpMock.Object); string signedFeed = stream.ReadToString(); string expectedFeed = feed.ToXmlString() + Store.Feeds.FeedUtils.SignatureBlockStart + Convert.ToBase64String(signature) + "\n" + Store.Feeds.FeedUtils.SignatureBlockEnd; signedFeed.Should().Be(expectedFeed, because: "Feed should remain unchanged except for appended XML signatre"); } }
public void TestSignFeed() { using (var stream = new MemoryStream()) { var feed = FeedTest.CreateTestFeed(); const string passphrase = "passphrase123"; var signature = new byte[] {1, 2, 3}; var secretKey = new OpenPgpSecretKey(keyID: 123, fingerprint: new byte[] {1, 2, 3}, userID: "user"); var openPgpMock = CreateMock<IOpenPgp>(); openPgpMock.Setup(x => x.Sign(It.IsAny<byte[]>(), secretKey, passphrase)) .Returns(signature); feed.SaveXml(stream); FeedUtils.SignFeed(stream, secretKey, passphrase, openPgpMock.Object); string signedFeed = stream.ReadToString(); string expectedFeed = feed.ToXmlString() + Store.Feeds.FeedUtils.SignatureBlockStart + Convert.ToBase64String(signature) + "\n" + Store.Feeds.FeedUtils.SignatureBlockEnd; signedFeed.Should().Be(expectedFeed, because: "Feed should remain unchanged except for appended XML signatre"); } }
/// <inheritdoc/> public byte[] Sign(ArraySegment <byte> data, OpenPgpSecretKey secretKey, string?passphrase = null) { #region Sanity checks if (secretKey == null) { throw new ArgumentNullException(nameof(secretKey)); } #endregion var pgpSecretKey = SecretBundle.GetSecretKey(secretKey.KeyID); if (pgpSecretKey == null) { throw new KeyNotFoundException("Specified OpenPGP key not found on system"); } var pgpPrivateKey = GetPrivateKey(pgpSecretKey, passphrase); var signatureGenerator = new PgpSignatureGenerator(pgpSecretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1); signatureGenerator.InitSign(PgpSignature.BinaryDocument, pgpPrivateKey); signatureGenerator.Update(data.Array, data.Offset, data.Count); return(signatureGenerator.Generate().GetEncoded()); }
/// <summary> /// Exports an OpenPGP public key to a key file. /// </summary> /// <param name="path">The directory to write the key file to.</param> /// <param name="secretKey">The secret key to get the public kyey for.</param> /// <param name="openPgp">The OpenPGP-compatible system used to create signatures.</param> /// <exception cref="IOException">The OpenPGP implementation could not be launched or the file could not be read or written.</exception> /// <exception cref="UnauthorizedAccessException">Write access to the directory is not permitted.</exception> public static void DeployPublicKey([NotNull] string path, [NotNull] OpenPgpSecretKey secretKey, [NotNull] IOpenPgp openPgp) { #region Sanity checks if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } if (secretKey == null) { throw new ArgumentNullException("secretKey"); } if (openPgp == null) { throw new ArgumentNullException("openPgp"); } #endregion File.WriteAllText( path: Path.Combine(path, secretKey.KeyID + ".gpg"), contents: openPgp.GetPublicKey(secretKey.Fingerprint), encoding: Encoding.ASCII); }
public void TestSignFeed() { using (var stream = new MemoryStream()) { var feed = FeedTest.CreateTestFeed(); var secretKey = new OpenPgpSecretKey("fingerprint", "key", "*****@*****.**", new DateTime(2000, 1, 1), OpenPgpAlgorithm.Rsa, 128); var openPgpMock = MockRepository.Create <IOpenPgp>(); const string passphrase = "passphrase123"; const string signature = "iQEcB"; openPgpMock.Setup(x => x.DetachSign(It.IsAny <Stream>(), secretKey.Fingerprint, passphrase)) .Returns(signature); feed.SaveXml(stream); FeedUtils.SignFeed(stream, secretKey, passphrase, openPgpMock.Object); string signedFeed = stream.ReadToString(); string expectedFeed = feed.ToXmlString() + Store.Feeds.FeedUtils.SignatureBlockStart + signature + "\n" + Store.Feeds.FeedUtils.SignatureBlockEnd; Assert.AreEqual(expectedFeed, signedFeed, "Feed should remain unchanged except for appended XML signatre"); } }
/// <summary> /// Signs a number of <see cref="Feed"/>s with a single <see cref="OpenPgpSecretKey"/>. /// </summary> /// <param name="secretKey">The private key to use for signing the files.</param> /// <param name="passphrase">The passphrase to use to unlock the key.</param> /// <exception cref="IOException">The feed file could not be read or written.</exception> /// <exception cref="UnauthorizedAccessException">Read or write access to the feed file is not permitted.</exception> private void SignFiles(OpenPgpSecretKey secretKey, string passphrase) { var task = new ForEachTask<FileInfo>("Signing feeds", _files, file => { SignedFeed signedFeed; try { signedFeed = SignedFeed.Load(file.FullName); } #region Error handling catch (UnauthorizedAccessException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } catch (InvalidDataException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message + (ex.InnerException == null ? "" : "\n" + ex.InnerException.Message), ex); } #endregion signedFeed.SecretKey = secretKey; try { signedFeed.Save(file.FullName, passphrase); } #region Error handling catch (UnauthorizedAccessException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } catch (KeyNotFoundException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } catch (WrongPassphraseException ex) { // Wrap exception since only certain exception types are allowed throw new IOException(ex.Message, ex); } #endregion }); using (var handler = new GuiTaskHandler(this)) handler.RunTask(task); Msg.Inform(this, "Successfully signed files.", MsgSeverity.Info); }
/// <inheritdoc/> public byte[] Sign(byte[] data, OpenPgpSecretKey secretKey, string passphrase = null) { #region Sanity checks if (data == null) throw new ArgumentNullException("data"); if (secretKey == null) throw new ArgumentNullException("secretKey"); #endregion string output = new CliControl(_homeDir, data).Execute("--batch", "--no-secmem-warning", "--passphrase", passphrase ?? "", "--local-user", secretKey.KeyID, "--detach-sign", "--armor", "--output", "-", "-"); string signatureBase64 = output .GetRightPartAtFirstOccurrence(Environment.NewLine + Environment.NewLine) .GetLeftPartAtLastOccurrence(Environment.NewLine + "=") .Replace(Environment.NewLine, "\n"); return Convert.FromBase64String(signatureBase64); }