aboutsummaryrefslogtreecommitdiff
path: root/extras/gnutls-ca.pl
blob: 21af9c974188adc2ebfe08be29d032c56dafa559 (about) (plain) (blame)
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#!/usr/bin/perl
use strict;
use warnings;

sub gen_key;
sub gen_ca;
sub gen_server;
sub ca_sign;
sub gen_templates;
sub key_info;;

mkdir "ssl";
chdir "ssl" or die $!;
mkdir "keys" and chmod 0700, "keys";
mkdir "certs";
gen_templates;

my $what = shift || '';
if ($what eq 'sign') {
	gen_ca;
	for my $req (@ARGV) {
		my $out = $req;
		$out =~ s/request/ca-sign/ or ($out .= '.sign');
		ca_sign $req, $out;
	}
} elsif ($what eq 'server') {
	gen_ca;
	gen_server;
	ca_sign 'server-request.pem', 'server-ca-sign.pem';
	print <<END;
SSL certificates have been generated.

It is a good idea to make your SSL certificate match your IRCd's hostname; some
IRC clients complain about a mismatch here. To do this, edit ssl/server.info
and rerun this script.

If you just want a simple server certificate, copy ssl/server-key.pem and
ssl/server-selfsign.pem to conf/key.pem and conf/cert.pem.

If you want a certificate signed by a CA, use ssl/server-key.pem and
ssl/server-ca-sign.pem. If you have multiple servers, choose one to hold the
root CA and copy the other server-request.pem files to that system. Then use
$0 sign <filename> to sign them with the single CA.
The file ssl/ca.pem can be used by clients to verify your servers.
END
} else {
	print "Use: \n";
	print " $0 server        Generate server certificates for one server\n";
	print " $0 sign cert     Sign another server's request with the local CA\n";
	exit (@ARGV != 0);
}

sub gen_key {
	my $key = shift;
	return if -e $key;
	print "[\e[32m*\e[0m] Generating private key $key\n";

	system "certtool --generate-privkey --outfile keys/tmp.pem 2>>log" and die;
	my $fp;
	open my $info, '-|', 'certtool --key-info < keys/tmp.pem' or die;
	while (<$info>) {
		m#Public Key ID: ([0-9A-F:]+)# and $fp = $1;
	}
	$fp or die "Cannot read key ID of the key we just made";
	$fp =~ s/://g;
	$fp = lc $fp;
	rename 'keys/tmp.pem', "keys/$fp.pem";
	unlink $key;
	symlink "keys/$fp.pem", $key;
}

sub gen_ca {
	gen_key 'ca-key.pem';
	my @ca = stat 'ca.pem';
	my @ca_t = stat 'ca.info';
	if (!@ca || $ca[9] < $ca_t[9]) {
		print "[\e[32m*\e[0m] Creating certificate authority (ca.pem)\n";
		system "certtool --generate-self-signed --template ca.info --load-privkey ca-key.pem --outfile tmp.pem 2>>log" and die;
		my($fn, $certfp, $keyfp) = key_info;

		unlink "ca.pem";
		symlink $fn, "ca.pem";
		@ca = stat 'ca.pem';
	}
}

sub gen_server {
	gen_key 'server-key.pem';

	my @server_t = stat 'server.info';
	my @server_req = stat 'server-request.pem';
	my @server_ss = stat 'server-selfsign.pem';

	if (!@server_req || $server_req[9] < $server_t[9]) {
		print "[\e[32m*\e[0m] Creating server certificate request (server-request.pem)\n";
		system "certtool --generate-request --template server.info --load-privkey server-key.pem --outfile server-request.pem 2>>log" and die;
		@server_req = stat 'server-request.pem';
	}

	if (!@server_ss || $server_ss[9] < $server_t[9]) {
		print "[\e[32m*\e[0m] Creating self-signed server certificate (server-selfsign.pem)\n";
		system "certtool --generate-self-signed --template server.info --load-privkey server-key.pem --outfile tmp.pem 2>>log" and die;
		my($fn, $certfp, $keyfp) = key_info;

		unlink "server-selfsign.pem";
		symlink $fn, "server-selfsign.pem";
	}
}

sub ca_sign {
	my($in, $out) = @_;
	my @ca = stat 'ca.pem';
	my @server_req = stat $in;
	my @server_cs = stat $out;
	if (!@server_cs || $server_cs[9] < $server_req[9] || $server_cs[9] < $ca[9]) {
		print "[\e[32m*\e[0m] Signing $in with ca.pem ($out)\n";
		system "certtool --generate-certificate --load-request '$in' --load-ca-certificate ca.pem --load-ca-privkey ca-key.pem --template ca-signing.info --outfile tmp.pem 2>>log" and die;
		my($fn, $certfp, $keyfp) = key_info;

		system "cat ca.pem $fn > certs/$certfp-full.pem";
		unlink $out;
		symlink "certs/$certfp-full.pem", $out;
	}
}

sub gen_templates {
	if (!-e 'server.info') {
		open F, '>', 'server.info';
		print F <<EOF;
# X.509 Certificate options

# CN (common name) - the name of your server. You want to set this.
#cn = "irc.example.com"

# How many days, counting from today, will certificate be valid?
# Default is 1 year
#expiration_days = 1000

#You can also set other fields, see certtool documentation for the rest
EOF
		close F;
	}
	if (!-e 'ca.info') {
		open F, '>', 'ca.info';
		print F <<EOF;
# X.509 Certificate options
# This is for a Certificate Authority
ca

# CN (common name) - the name of your root.
# While not strictly required, your IRC network is good to put here
#cn = "irc.example.com"

# How many days, counting from today, will certificate be valid?
# Default is 1 year
#expiration_days = 1000

#You can also set other fields, see certtool documentation for the rest
EOF
		close F;
	}
	if (!-e 'ca-signing.info') {
		open F, '>', 'ca-signing.info';
		print F <<EOF;
# X.509 Certificate options
# This is for certificate requests being signed by the CA.
# Blank to accept all fields from the request.
EOF
		close F;
	}
}

sub key_info {
	my($certfp, $keyfp);
	open my $info, '-|', 'certtool --certificate-info < tmp.pem' or die;
	while (<$info>) {
		if (m#SHA-1 fingerprint:#) {
			$certfp = <$info>;
		} elsif (m#Public Key Id:#) {
			$keyfp = <$info>;
		}
	}
	chomp($certfp, $keyfp);
	$certfp =~ s/^\t+//;
	$keyfp =~ s/^\t+//;
	my $fn = "certs/$certfp.pem";
	rename 'tmp.pem', $fn;
	return($fn, $certfp, $keyfp);
}