ca13ba23a331f237ebbdfe255ea57216836cafeb
2 LibreSSL - CAcert web application
3 Copyright (C) 2004-2011 CAcert Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 require_once 'general.php';
24 * Checks whether the given CSR contains a vulnerable key
27 * The CSR to be checked
28 * @param $encoding string [optional]
29 * The encoding the CSR is in (for the "-inform" parameter of OpenSSL,
30 * currently only "PEM" (default) or "DER" allowed)
31 * @return string containing the reason if the key is considered weak,
32 * empty string otherwise
34 function checkWeakKeyCSR($csr, $encoding = "PEM")
36 $encoding = escapeshellarg($encoding);
37 $status = runCommand("openssl req -inform $encoding -text -noout",
39 if ($status === true
) {
40 return failWithId("checkWeakKeyCSR(): Failed to start OpenSSL");
43 if ($status !== 0 ||
$csrText === "") {
44 return _("I didn't receive a valid Certificate Request. Hit ".
45 "the back button and try again.");
48 return checkWeakKeyText($csrText);
52 * Checks whether the given X509 certificate contains a vulnerable key
55 * The X509 certificate to be checked
56 * @param $encoding string [optional]
57 * The encoding the certificate is in (for the "-inform" parameter of
58 * OpenSSL, currently only "PEM" (default), "DER" or "NET" allowed)
59 * @return string containing the reason if the key is considered weak,
60 * empty string otherwise
62 function checkWeakKeyX509($cert, $encoding = "PEM")
64 $encoding = escapeshellarg($encoding);
65 $status = runCommand("openssl x509 -inform $encoding -text -noout",
67 if ($status === true
) {
68 return failWithId("checkWeakKeyX509(): Failed to start OpenSSL");
71 if ($status !== 0 ||
$certText === "") {
72 return _("I didn't receive a valid Certificate Request. Hit ".
73 "the back button and try again.");
76 return checkWeakKeyText($certText);
80 * Checks whether the given SPKAC contains a vulnerable key
82 * @param $spkac string
83 * The SPKAC to be checked
84 * @param $spkacname string [optional]
85 * The name of the variable that contains the SPKAC. The default is
87 * @return string containing the reason if the key is considered weak,
88 * empty string otherwise
90 function checkWeakKeySPKAC($spkac, $spkacname = "SPKAC")
92 $spkacname = escapeshellarg($spkacname);
93 $status = runCommand("openssl spkac -spkac $spkacname", $spkac, $spkacText);
94 if ($status === true
) {
95 return failWithId("checkWeakKeySPKAC(): Failed to start OpenSSL");
98 if ($status !== 0 ||
$spkacText === "") {
99 return _("I didn't receive a valid Certificate Request. Hit the ".
100 "back button and try again.");
103 return checkWeakKeyText($spkacText);
107 * Checks whether the given text representation of a CSR or a SPKAC contains
110 * @param $text string
111 * The text representation of a key as output by the
112 * "openssl <foo> -text -noout" commands
113 * @return string containing the reason if the key is considered weak,
114 * empty string otherwise
116 function checkWeakKeyText($text)
118 /* Which public key algorithm? */
119 if (!preg_match('/^\s*Public Key Algorithm: ([^\s]+)$/m', $text,
122 return failWithId("checkWeakKeyText(): Couldn't extract the ".
123 "public key algorithm used.\nData:\n$text");
125 $algorithm = $algorithm[1];
129 if ($algorithm === "rsaEncryption")
131 if (!preg_match('/^\s*RSA Public Key: \((\d+) bit\)$/m', $text,
134 return failWithId("checkWeakKeyText(): Couldn't parse the RSA ".
135 "key size.\nData:\n$text");
137 $keysize = intval($keysize[1]);
142 return sprintf(_("The keys that you use are very small ".
143 "and therefore insecure. Please generate stronger ".
144 "keys. More information about this issue can be ".
145 "found in %sthe wiki%s"),
146 "<a href='//wiki.cacert.org/WeakKeys#SmallKey'>",
148 } elseif ($keysize < 2048) {
149 // not critical but log so we have some statistics about
151 trigger_error("checkWeakKeyText(): Certificate for small ".
152 "key (< 2048 bit) requested", E_USER_NOTICE
);
156 $debianVuln = checkDebianVulnerability($text, $keysize);
157 if ($debianVuln === true
)
159 return sprintf(_("The keys you use have very likely been ".
160 "generated with a vulnerable version of OpenSSL which ".
161 "was distributed by debian. Please generate new keys. ".
162 "More information about this issue can be found in ".
164 "<a href='//wiki.cacert.org/WeakKeys#DebianVulnerability'>",
166 } elseif ($debianVuln === false
) {
167 // not vulnerable => do nothing
169 return failWithId("checkWeakKeyText(): Something went wrong in".
170 "checkDebianVulnerability().\nKeysize: $keysize\n".
174 if (!preg_match('/^\s*Exponent: (\d+) \(0x[0-9a-fA-F]+\)$/m', $text,
177 return failWithId("checkWeakKeyText(): Couldn't parse the RSA ".
178 "exponent.\nData:\n$text");
180 $exponent = $exponent[1]; // exponent might be very big =>
181 //handle as string using bc*()
183 if (bccomp($exponent, "3") === 0)
185 return sprintf(_("The keys you use might be insecure. ".
186 "Although there is currently no known attack for ".
187 "reasonable encryption schemes, we're being ".
188 "cautious and don't allow certificates for such ".
189 "keys. Please generate stronger keys. More ".
190 "information about this issue can be found in ".
192 "<a href='//wiki.cacert.org/WeakKeys#SmallExponent'>",
194 } elseif (!(bccomp($exponent, "65537") >= 0 &&
195 (bccomp($exponent, "100000") === -1 ||
196 // speed things up if way smaller than 2^256
197 bccomp($exponent, bcpow("2", "256")) === -1) )) {
198 // 65537 <= exponent < 2^256 recommended by NIST
199 // not critical but log so we have some statistics about
201 trigger_error("checkWeakKeyText(): Certificate for ".
202 "unsuitable exponent '$exponent' requested",
208 /* No weakness found */
213 * Reimplement the functionality of the openssl-vulnkey tool
215 * @param $text string
216 * The text representation of a key as output by the
217 * "openssl <foo> -text -noout" commands
218 * @param $keysize int [optional]
219 * If the key size is already known it can be provided so it doesn't
220 * have to be parsed again. This also skips the check whether the key
221 * is an RSA key => use wisely
222 * @return TRUE if key is vulnerable, FALSE otherwise, NULL in case of error
224 function checkDebianVulnerability($text, $keysize = 0)
226 $keysize = intval($keysize);
230 /* Which public key algorithm? */
231 if (!preg_match('/^\s*Public Key Algorithm: ([^\s]+)$/m', $text,
234 trigger_error("checkDebianVulnerability(): Couldn't extract ".
235 "the public key algorithm used.\nData:\n$text",
239 $algorithm = $algorithm[1];
242 if ($algorithm !== "rsaEncryption") return false
;
244 /* Extract public key size */
245 if (!preg_match('/^\s*RSA Public Key: \((\d+) bit\)$/m', $text,
248 trigger_error("checkDebianVulnerability(): Couldn't parse the ".
249 "RSA key size.\nData:\n$text", E_USER_WARNING
);
252 $keysize = intval($keysize[1]);
256 // $keysize has been made sure to contain an int
257 $blacklist = "/usr/share/openssl-blacklist/blacklist.RSA-$keysize";
258 if (!(is_file($blacklist) && is_readable($blacklist)))
260 if (in_array($keysize, array(512, 1024, 2048, 4096)))
262 trigger_error("checkDebianVulnerability(): Blacklist for ".
263 "$keysize bit keys not accessible. Expected at ".
264 "$blacklist", E_USER_ERROR
);
268 trigger_error("checkDebianVulnerability(): $blacklist is not ".
269 "readable. Unsupported key size?", E_USER_WARNING
);
274 /* Extract RSA modulus */
275 if (!preg_match('/^\s*Modulus \(\d+ bit\):\n'.
276 '((?:\s*[0-9a-f][0-9a-f]:(?:\n)?)+[0-9a-f][0-9a-f])$/m',
279 trigger_error("checkDebianVulnerability(): Couldn't extract the ".
280 "RSA modulus.\nData:\n$text", E_USER_WARNING
);
283 $modulus = $modulus[1];
284 // strip whitespace and colon leftovers
285 $modulus = str_replace(array(" ", "\t", "\n", ":"), "", $modulus);
287 // when using "openssl xxx -text" first byte was 00 in all my test
288 // cases but 00 not present in the "openssl xxx -modulus" output
289 if ($modulus[0] === "0" && $modulus[1] === "0")
291 $modulus = substr($modulus, 2);
293 trigger_error("checkDebianVulnerability(): First byte is not ".
294 "zero", E_USER_NOTICE
);
297 $modulus = strtoupper($modulus);
301 /* calculate checksum and look it up in the blacklist */
302 $checksum = substr(sha1("Modulus=$modulus\n"), 20);
304 // $checksum and $blacklist should be safe, but just to make sure
305 $checksum = escapeshellarg($checksum);
306 $blacklist = escapeshellarg($blacklist);
307 $debianVuln = runCommand("grep $checksum $blacklist");
308 if ($debianVuln === 0) // grep returned something => it is on the list
311 } elseif ($debianVuln === 1) {
312 // grep returned nothing
315 trigger_error("checkDebianVulnerability(): Something went wrong ".
316 "when looking up the key with checksum $checksum in the ".
317 "blacklist $blacklist", E_USER_ERROR
);
321 // Should not get here