diff --git a/MimeKit/Cryptography/OpenPgpContext.cs b/MimeKit/Cryptography/OpenPgpContext.cs
index ff214f82da..d66835dce5 100644
--- a/MimeKit/Cryptography/OpenPgpContext.cs
+++ b/MimeKit/Cryptography/OpenPgpContext.cs
@@ -1114,30 +1114,62 @@ public void GenerateKeyPair (MailboxAddress mailbox, string password, DateTime?
Import (generator.GeneratePublicKeyRing ());
}
- //public void SignPublicKey (PgpSecretKey secretKey, PgpPublicKey publicKey, DigestAlgorithm digestAlgo = DigestAlgorithm.Sha1)
- //{
- // if (secretKey == null)
- // throw new ArgumentNullException (nameof (secretKey));
+ ///
+ /// Sign a public key.
+ ///
+ ///
+ /// Signs a public key using the specified secret key.
+ /// Most OpenPGP implementations use
+ /// to make their "key signatures". Some implementations are known to use the other
+ /// certification types, but few differentiate between them.
+ ///
+ /// The secret key to use for signing.
+ /// The public key to sign.
+ /// The digest algorithm.
+ /// The certification to give the signed key.
+ ///
+ /// is null.
+ /// -or-
+ /// is null.
+ ///
+ public void SignKey (PgpSecretKey secretKey, PgpPublicKey publicKey, DigestAlgorithm digestAlgo = DigestAlgorithm.Sha1, OpenPgpKeyCertification certification = OpenPgpKeyCertification.GenericCertification)
+ {
+ if (secretKey == null)
+ throw new ArgumentNullException (nameof (secretKey));
- // if (publicKey == null)
- // throw new ArgumentNullException (nameof (publicKey));
+ if (publicKey == null)
+ throw new ArgumentNullException (nameof (publicKey));
- // var privateKey = GetPrivateKey (secretKey);
- // var signatureGenerator = new PgpSignatureGenerator (secretKey.PublicKey.Algorithm, GetHashAlgorithm (digestAlgo));
+ var privateKey = GetPrivateKey (secretKey);
+ var signatureGenerator = new PgpSignatureGenerator (secretKey.PublicKey.Algorithm, GetHashAlgorithm (digestAlgo));
- // signatureGenerator.InitSign (PgpSignature.CasualCertification, privateKey);
- // signatureGenerator.GenerateOnePassVersion (false);
+ signatureGenerator.InitSign ((int) certification, privateKey);
+ signatureGenerator.GenerateOnePassVersion (false);
- // var subpacketGenerator = new PgpSignatureSubpacketGenerator ();
- // var subpacketVector = subpacketGenerator.Generate ();
+ var subpacketGenerator = new PgpSignatureSubpacketGenerator ();
+ var subpacketVector = subpacketGenerator.Generate ();
- // signatureGenerator.SetHashedSubpackets (subpacketVector);
+ signatureGenerator.SetHashedSubpackets (subpacketVector);
- // var signedKey = PgpPublicKey.AddCertification (publicKey, signatureGenerator.Generate ());
- // var keyring = new PgpPublicKeyRing (signedKey.GetEncoded ());
+ var signedKey = PgpPublicKey.AddCertification (publicKey, signatureGenerator.Generate ());
+ PgpPublicKeyRing keyring = null;
- // Import (keyring);
- //}
+ foreach (var ring in EnumeratePublicKeyRings ()) {
+ foreach (PgpPublicKey key in ring.GetPublicKeys ()) {
+ if (key.KeyId == publicKey.KeyId) {
+ PublicKeyRingBundle = PgpPublicKeyRingBundle.RemovePublicKeyRing (PublicKeyRingBundle, ring);
+ keyring = PgpPublicKeyRing.InsertPublicKey (ring, signedKey);
+ break;
+ }
+ }
+ }
+
+ if (keyring == null)
+ keyring = new PgpPublicKeyRing (signedKey.GetEncoded ());
+
+ PublicKeyRingBundle = PgpPublicKeyRingBundle.AddPublicKeyRing (PublicKeyRingBundle, keyring);
+ SavePublicKeyRingBundle ();
+ }
///
/// Gets the equivalent for the
diff --git a/MimeKit/Cryptography/OpenPgpKeyCertification.cs b/MimeKit/Cryptography/OpenPgpKeyCertification.cs
new file mode 100644
index 0000000000..01b88104ae
--- /dev/null
+++ b/MimeKit/Cryptography/OpenPgpKeyCertification.cs
@@ -0,0 +1,65 @@
+//
+// OpenPgpKeyCertification.cs
+//
+// Author: Jeffrey Stedfast
+//
+// Copyright (c) 2013-2017 Xamarin Inc. (www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+namespace MimeKit.Cryptography
+{
+ ///
+ /// An OpenPGP key certification.
+ ///
+ ///
+ /// An OpenPGP key certification.
+ ///
+ public enum OpenPgpKeyCertification {
+ ///
+ /// Generic certification of a User ID and Public-Key packet.
+ /// The issuer of this certification does not make any particular
+ /// assertion as to how well the certifier has checked that the owner
+ /// of the key is in fact the person described by the User ID.
+ ///
+ GenericCertification = 0x10,
+
+ ///
+ /// Persona certification of a User ID and Public-Key packet.
+ /// The issuer of this certification has not done any verification of
+ /// the claim that the owner of this key is the User ID specified.
+ ///
+ PersonaCertification = 0x11,
+
+ ///
+ /// Casual certification of a User ID and Public-Key packet.
+ /// The issuer of this certification has done some casual
+ /// verification of the claim of identity.
+ ///
+ CasualCertification = 0x12,
+
+ ///
+ /// Positive certification of a User ID and Public-Key packet.
+ /// The issuer of this certification has done substantial
+ /// verification of the claim of identity.
+ ///
+ PositiveCertification = 0x13
+ }
+}
diff --git a/MimeKit/MimeKit.Android.csproj b/MimeKit/MimeKit.Android.csproj
index 210b209fbe..d990c94ef6 100644
--- a/MimeKit/MimeKit.Android.csproj
+++ b/MimeKit/MimeKit.Android.csproj
@@ -96,6 +96,7 @@
+
diff --git a/MimeKit/MimeKit.Mac.csproj b/MimeKit/MimeKit.Mac.csproj
index f3fa222678..a7937566cc 100644
--- a/MimeKit/MimeKit.Mac.csproj
+++ b/MimeKit/MimeKit.Mac.csproj
@@ -86,6 +86,7 @@
+
diff --git a/MimeKit/MimeKit.Net35.csproj b/MimeKit/MimeKit.Net35.csproj
index e0c6a8fd9e..46615fd1ca 100644
--- a/MimeKit/MimeKit.Net35.csproj
+++ b/MimeKit/MimeKit.Net35.csproj
@@ -91,6 +91,7 @@
+
diff --git a/MimeKit/MimeKit.Net40.csproj b/MimeKit/MimeKit.Net40.csproj
index 2e07266efc..61b6cdd317 100644
--- a/MimeKit/MimeKit.Net40.csproj
+++ b/MimeKit/MimeKit.Net40.csproj
@@ -93,6 +93,7 @@
+
diff --git a/MimeKit/MimeKit.Net45.csproj b/MimeKit/MimeKit.Net45.csproj
index 40914c9626..0a35b5ab3e 100644
--- a/MimeKit/MimeKit.Net45.csproj
+++ b/MimeKit/MimeKit.Net45.csproj
@@ -94,6 +94,7 @@
+
diff --git a/MimeKit/MimeKit.NetStandard.csproj b/MimeKit/MimeKit.NetStandard.csproj
index 1182a62232..73d5a53ac7 100644
--- a/MimeKit/MimeKit.NetStandard.csproj
+++ b/MimeKit/MimeKit.NetStandard.csproj
@@ -81,6 +81,7 @@
+
diff --git a/MimeKit/MimeKit.Portable.csproj b/MimeKit/MimeKit.Portable.csproj
index edcd395342..fd560f8992 100644
--- a/MimeKit/MimeKit.Portable.csproj
+++ b/MimeKit/MimeKit.Portable.csproj
@@ -84,6 +84,7 @@
+
diff --git a/MimeKit/MimeKit.TvOS.csproj b/MimeKit/MimeKit.TvOS.csproj
index 80bbfe25ad..a045b58823 100644
--- a/MimeKit/MimeKit.TvOS.csproj
+++ b/MimeKit/MimeKit.TvOS.csproj
@@ -78,6 +78,7 @@
+
diff --git a/MimeKit/MimeKit.WatchOS.csproj b/MimeKit/MimeKit.WatchOS.csproj
index f75bea27f0..bf38c8c8c4 100644
--- a/MimeKit/MimeKit.WatchOS.csproj
+++ b/MimeKit/MimeKit.WatchOS.csproj
@@ -84,6 +84,7 @@
+
diff --git a/MimeKit/MimeKit.WindowsUniversal81.csproj b/MimeKit/MimeKit.WindowsUniversal81.csproj
index 3eadc50ac3..966e68eaea 100644
--- a/MimeKit/MimeKit.WindowsUniversal81.csproj
+++ b/MimeKit/MimeKit.WindowsUniversal81.csproj
@@ -90,6 +90,7 @@
+
diff --git a/MimeKit/MimeKit.iOS.csproj b/MimeKit/MimeKit.iOS.csproj
index 06fee186d7..836a288ed5 100644
--- a/MimeKit/MimeKit.iOS.csproj
+++ b/MimeKit/MimeKit.iOS.csproj
@@ -93,6 +93,7 @@
+
diff --git a/UnitTests/PgpMimeTests.cs b/UnitTests/PgpMimeTests.cs
index d5d70c108a..ba471a02cc 100644
--- a/UnitTests/PgpMimeTests.cs
+++ b/UnitTests/PgpMimeTests.cs
@@ -31,6 +31,7 @@
using NUnit.Framework;
+using Org.BouncyCastle.Bcpg;
using Org.BouncyCastle.Bcpg.OpenPgp;
using MimeKit;
@@ -136,6 +137,48 @@ public void TestKeyGeneration ()
}
}
+ [Test]
+ public void TestKeySigning ()
+ {
+ using (var ctx = new DummyOpenPgpContext ()) {
+ var seckey = ctx.EnumerateSecretKeys (new MailboxAddress ("", "mimekit@example.com")).FirstOrDefault ();
+ var mailbox = new MailboxAddress ("Snarky McSnarkypants", "snarky@snarkypants.net");
+
+ ctx.GenerateKeyPair (mailbox, "password", DateTime.Now.AddYears (1));
+
+ // delete the secret keyring, we don't need it
+ var secring = ctx.EnumerateSecretKeyRings (mailbox).FirstOrDefault ();
+ ctx.Delete (secring);
+
+ var pubring = ctx.EnumeratePublicKeyRings (mailbox).FirstOrDefault ();
+ var pubkey = pubring.GetPublicKey ();
+ int sigCount = 0;
+
+ foreach (PgpSignature sig in pubkey.GetKeySignatures ())
+ sigCount++;
+
+ Assert.AreEqual (0, sigCount);
+
+ ctx.SignKey (seckey, pubkey, DigestAlgorithm.Sha256, OpenPgpKeyCertification.CasualCertification);
+
+ pubring = ctx.EnumeratePublicKeyRings (mailbox).FirstOrDefault ();
+ pubkey = pubring.GetPublicKey ();
+
+ sigCount = 0;
+
+ foreach (PgpSignature sig in pubkey.GetKeySignatures ()) {
+ Assert.AreEqual (seckey.KeyId, sig.KeyId);
+ Assert.AreEqual (HashAlgorithmTag.Sha256, sig.HashAlgorithm);
+ Assert.AreEqual ((int) OpenPgpKeyCertification.CasualCertification, sig.SignatureType);
+ sigCount++;
+ }
+
+ Assert.AreEqual (1, sigCount);
+
+ ctx.Delete (pubring);
+ }
+ }
+
[Test]
public void TestMimeMessageSign ()
{