1
2
3
4
5
6
7
8
9
10
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
//! Illustrates how to create keys and certificates for group
//! conversations.
//!
//! This example, when run, generates four artifacts:
//!
//! ```text
//! % cargo run -p sequoia-openpgp --example generate-group-key cabal@example.org
//! [...]
//! Writing certificate to cabal@example.org.cert.pgp
//! Writing full key to cabal@example.org.key.pgp
//! Writing key with detached primary to cabal@example.org.only_subkey.pgp
//! Writing revocation certificate to cabal@example.org.revocation.pgp
//! ```
//!
//! - If you want to receive unsolicited encrypted messages on the
//! address (e.g. in case of a security contact or a public mailing
//! list), you should publish this in a suitable way, e.g. using
//! WKD. Consider authenticating this certificate using OpenPGP-CA.
//!
//! - Distribute the certificate freely if you want to receive
//! unsolicited encrypted messages.
//!
//! - Distribute the key with the detached primary among the group,
//! for example by including it in an email encrypted to all
//! members.
//!
//! - Make a backup of full key and revocation certificate.
//!
//! - Consider distributing the revocation certificate among a select,
//! trusted group that can help when disaster strikes. The security
//! implication of handing out the revocation certificate is a
//! denial-of-service vector.
use std::fs::File;
use std::time::Duration;
use sequoia_openpgp as openpgp;
use openpgp::armor;
use openpgp::cert::prelude::*;
use openpgp::types::KeyFlags;
use openpgp::serialize::Serialize;
fn main() -> openpgp::Result<()> {
let args: Vec<String> = std::env::args().collect();
let name = if let Some(n) = args.get(1).cloned() {
n
} else {
return Err(anyhow::anyhow!(
"Missing list address parameter.\n\n\
Usage: {} <LIST-ADDRESS>",
args.get(0).cloned().unwrap_or("generate-group-key".into())));
};
// Generate the key.
let (cert, revocation) = CertBuilder::new()
.set_validity_period(Duration::new(5 * 365 * 24 * 60 * 60, 0))
.add_userid(format!("<{}>", name))
.add_subkey(KeyFlags::empty()
.set_transport_encryption()
.set_group_key(),
None,
None)
.generate()?;
// First, emit the certificate.
let n = format!("{}.cert.pgp", name);
eprintln!("Writing certificate to {}", n);
cert.armored().serialize(&mut File::create(n)?)?;
// Second, emit the key. This includes all secret key material.
// Back this up.
let n = format!("{}.key.pgp", name);
eprintln!("Writing full key to {}", n);
cert.as_tsk().armored().serialize(&mut File::create(n)?)?;
// Third, emit they key, but only include the encryption subkey's
// secret key material.
let n = format!("{}.only_subkey.pgp", name);
eprintln!("Writing key with detached primary to {}", n);
cert.as_tsk()
.set_filter(|k| k.fingerprint() != cert.fingerprint())
.emit_secret_key_stubs(true) // Enable GnuPG-style.
.armored()
.serialize(&mut File::create(n)?)?;
// Finally, emit a revocation certificate. Back this up.
let n = format!("{}.revocation.pgp", name);
eprintln!("Writing revocation certificate to {}", n);
// Be fancy and include comments in the revocation cert.
let mut comments = cert.armor_headers();
comments.insert(0, "Revocation certificate for the following key:".into());
comments.insert(1, "".into());
let mut w = armor::Writer::with_headers(
File::create(n)?,
armor::Kind::PublicKey,
comments.iter().map(|c| ("Comment", c)))?;
openpgp::Packet::from(revocation).serialize(&mut w)?;
w.finalize()?;
Ok(())
}
|