1#! /usr/bin/env perl 2# Copyright 2015-2022 The OpenSSL Project Authors. All Rights Reserved. 3# 4# Licensed under the Apache License 2.0 (the "License"). You may not use 5# this file except in compliance with the License. You can obtain a copy 6# in the file LICENSE in the source distribution or at 7# https://www.openssl.org/source/license.html 8 9 10use strict; 11use warnings; 12 13use OpenSSL::Test::Utils; 14use OpenSSL::Test qw/:DEFAULT srctop_file/; 15 16setup("test_req"); 17 18plan tests => 44; 19 20require_ok(srctop_file('test', 'recipes', 'tconversion.pl')); 21 22my @certs = qw(test certs); 23 24# What type of key to generate? 25my @req_new; 26if (disabled("rsa")) { 27 @req_new = ("-newkey", "dsa:".srctop_file("apps", "dsa512.pem")); 28} else { 29 @req_new = ("-new"); 30 note("There should be a 2 sequences of .'s and some +'s."); 31 note("There should not be more that at most 80 per line"); 32} 33 34# Prevent MSys2 filename munging for arguments that look like file paths but 35# aren't 36$ENV{MSYS2_ARG_CONV_EXCL} = "/CN="; 37 38# Check for duplicate -addext parameters, and one "working" case. 39my @addext_args = ( "openssl", "req", "-new", "-out", "testreq.pem", 40 "-key", srctop_file("test", "certs", "ee-key.pem"), 41 "-config", srctop_file("test", "test.cnf"), @req_new ); 42my $val = "subjectAltName=DNS:example.com"; 43my $val2 = " " . $val; 44my $val3 = $val; 45$val3 =~ s/=/ =/; 46ok( run(app([@addext_args, "-addext", $val]))); 47ok(!run(app([@addext_args, "-addext", $val, "-addext", $val]))); 48ok(!run(app([@addext_args, "-addext", $val, "-addext", $val2]))); 49ok(!run(app([@addext_args, "-addext", $val, "-addext", $val3]))); 50ok(!run(app([@addext_args, "-addext", $val2, "-addext", $val3]))); 51 52# If a CSR is provided with neither of -key or -CA/-CAkey, this should fail. 53ok(!run(app(["openssl", "req", "-x509", 54 "-in", srctop_file(@certs, "x509-check.csr"), 55 "-out", "testreq.pem"]))); 56 57subtest "generating alt certificate requests with RSA" => sub { 58 plan tests => 3; 59 60 SKIP: { 61 skip "RSA is not supported by this OpenSSL build", 2 62 if disabled("rsa"); 63 64 ok(run(app(["openssl", "req", 65 "-config", srctop_file("test", "test.cnf"), 66 "-section", "altreq", 67 "-new", "-out", "testreq-rsa.pem", "-utf8", 68 "-key", srctop_file("test", "testrsa.pem")])), 69 "Generating request"); 70 71 ok(run(app(["openssl", "req", 72 "-config", srctop_file("test", "test.cnf"), 73 "-verify", "-in", "testreq-rsa.pem", "-noout"])), 74 "Verifying signature on request"); 75 76 ok(run(app(["openssl", "req", 77 "-config", srctop_file("test", "test.cnf"), 78 "-section", "altreq", 79 "-verify", "-in", "testreq-rsa.pem", "-noout"])), 80 "Verifying signature on request"); 81 } 82}; 83 84 85subtest "generating certificate requests with RSA" => sub { 86 plan tests => 8; 87 88 SKIP: { 89 skip "RSA is not supported by this OpenSSL build", 2 90 if disabled("rsa"); 91 92 ok(!run(app(["openssl", "req", 93 "-config", srctop_file("test", "test.cnf"), 94 "-new", "-out", "testreq-rsa.pem", "-utf8", 95 "-key", srctop_file("test", "testrsa.pem"), 96 "-keyform", "DER"])), 97 "Checking that mismatching keyform fails"); 98 99 ok(run(app(["openssl", "req", 100 "-config", srctop_file("test", "test.cnf"), 101 "-new", "-out", "testreq-rsa.pem", "-utf8", 102 "-key", srctop_file("test", "testrsa.pem"), 103 "-keyform", "PEM"])), 104 "Generating request"); 105 106 ok(run(app(["openssl", "req", 107 "-config", srctop_file("test", "test.cnf"), 108 "-verify", "-in", "testreq-rsa.pem", "-noout"])), 109 "Verifying signature on request"); 110 111 ok(run(app(["openssl", "req", 112 "-config", srctop_file("test", "test.cnf"), 113 "-modulus", "-in", "testreq-rsa.pem", "-noout"])), 114 "Printing a modulus of the request key"); 115 116 ok(run(app(["openssl", "req", 117 "-config", srctop_file("test", "test.cnf"), 118 "-new", "-out", "testreq_withattrs_pem.pem", "-utf8", 119 "-key", srctop_file("test", "testrsa_withattrs.pem")])), 120 "Generating request from a key with extra attributes - PEM"); 121 122 ok(run(app(["openssl", "req", 123 "-config", srctop_file("test", "test.cnf"), 124 "-verify", "-in", "testreq_withattrs_pem.pem", "-noout"])), 125 "Verifying signature on request from a key with extra attributes - PEM"); 126 127 ok(run(app(["openssl", "req", 128 "-config", srctop_file("test", "test.cnf"), 129 "-new", "-out", "testreq_withattrs_der.pem", "-utf8", 130 "-key", srctop_file("test", "testrsa_withattrs.der"), 131 "-keyform", "DER"])), 132 "Generating request from a key with extra attributes - PEM"); 133 134 ok(run(app(["openssl", "req", 135 "-config", srctop_file("test", "test.cnf"), 136 "-verify", "-in", "testreq_withattrs_der.pem", "-noout"])), 137 "Verifying signature on request from a key with extra attributes - PEM"); 138 } 139}; 140 141subtest "generating certificate requests with RSA-PSS" => sub { 142 plan tests => 12; 143 144 SKIP: { 145 skip "RSA is not supported by this OpenSSL build", 2 146 if disabled("rsa"); 147 148 ok(run(app(["openssl", "req", 149 "-config", srctop_file("test", "test.cnf"), 150 "-new", "-out", "testreq-rsapss.pem", "-utf8", 151 "-key", srctop_file("test", "testrsapss.pem")])), 152 "Generating request"); 153 ok(run(app(["openssl", "req", 154 "-config", srctop_file("test", "test.cnf"), 155 "-verify", "-in", "testreq-rsapss.pem", "-noout"])), 156 "Verifying signature on request"); 157 158 ok(run(app(["openssl", "req", 159 "-config", srctop_file("test", "test.cnf"), 160 "-new", "-out", "testreq-rsapss2.pem", "-utf8", 161 "-sigopt", "rsa_padding_mode:pss", 162 "-sigopt", "rsa_pss_saltlen:-1", 163 "-key", srctop_file("test", "testrsapss.pem")])), 164 "Generating request"); 165 ok(run(app(["openssl", "req", 166 "-config", srctop_file("test", "test.cnf"), 167 "-verify", "-in", "testreq-rsapss2.pem", "-noout"])), 168 "Verifying signature on request"); 169 170 ok(run(app(["openssl", "req", 171 "-config", srctop_file("test", "test.cnf"), 172 "-new", "-out", "testreq-rsapssmand.pem", "-utf8", 173 "-sigopt", "rsa_padding_mode:pss", 174 "-key", srctop_file("test", "testrsapssmandatory.pem")])), 175 "Generating request"); 176 ok(run(app(["openssl", "req", 177 "-config", srctop_file("test", "test.cnf"), 178 "-verify", "-in", "testreq-rsapssmand.pem", "-noout"])), 179 "Verifying signature on request"); 180 181 ok(run(app(["openssl", "req", 182 "-config", srctop_file("test", "test.cnf"), 183 "-new", "-out", "testreq-rsapssmand2.pem", "-utf8", 184 "-sigopt", "rsa_pss_saltlen:100", 185 "-key", srctop_file("test", "testrsapssmandatory.pem")])), 186 "Generating request"); 187 ok(run(app(["openssl", "req", 188 "-config", srctop_file("test", "test.cnf"), 189 "-verify", "-in", "testreq-rsapssmand2.pem", "-noout"])), 190 "Verifying signature on request"); 191 192 ok(!run(app(["openssl", "req", 193 "-config", srctop_file("test", "test.cnf"), 194 "-new", "-out", "testreq-rsapss3.pem", "-utf8", 195 "-sigopt", "rsa_padding_mode:pkcs1", 196 "-key", srctop_file("test", "testrsapss.pem")])), 197 "Generating request with expected failure"); 198 199 ok(!run(app(["openssl", "req", 200 "-config", srctop_file("test", "test.cnf"), 201 "-new", "-out", "testreq-rsapss3.pem", "-utf8", 202 "-sigopt", "rsa_pss_saltlen:-4", 203 "-key", srctop_file("test", "testrsapss.pem")])), 204 "Generating request with expected failure"); 205 206 ok(!run(app(["openssl", "req", 207 "-config", srctop_file("test", "test.cnf"), 208 "-new", "-out", "testreq-rsapssmand3.pem", "-utf8", 209 "-sigopt", "rsa_pss_saltlen:10", 210 "-key", srctop_file("test", "testrsapssmandatory.pem")])), 211 "Generating request with expected failure"); 212 213 ok(!run(app(["openssl", "req", 214 "-config", srctop_file("test", "test.cnf"), 215 "-new", "-out", "testreq-rsapssmand3.pem", "-utf8", 216 "-sha256", 217 "-key", srctop_file("test", "testrsapssmandatory.pem")])), 218 "Generating request with expected failure"); 219 } 220}; 221 222subtest "generating certificate requests with DSA" => sub { 223 plan tests => 2; 224 225 SKIP: { 226 skip "DSA is not supported by this OpenSSL build", 2 227 if disabled("dsa"); 228 229 ok(run(app(["openssl", "req", 230 "-config", srctop_file("test", "test.cnf"), 231 "-new", "-out", "testreq-dsa.pem", "-utf8", 232 "-key", srctop_file("test", "testdsa.pem")])), 233 "Generating request"); 234 235 ok(run(app(["openssl", "req", 236 "-config", srctop_file("test", "test.cnf"), 237 "-verify", "-in", "testreq-dsa.pem", "-noout"])), 238 "Verifying signature on request"); 239 } 240}; 241 242subtest "generating certificate requests with ECDSA" => sub { 243 plan tests => 2; 244 245 SKIP: { 246 skip "ECDSA is not supported by this OpenSSL build", 2 247 if disabled("ec"); 248 249 ok(run(app(["openssl", "req", 250 "-config", srctop_file("test", "test.cnf"), 251 "-new", "-out", "testreq-ec.pem", "-utf8", 252 "-key", srctop_file("test", "testec-p256.pem")])), 253 "Generating request"); 254 255 ok(run(app(["openssl", "req", 256 "-config", srctop_file("test", "test.cnf"), 257 "-verify", "-in", "testreq-ec.pem", "-noout"])), 258 "Verifying signature on request"); 259 } 260}; 261 262subtest "generating certificate requests with Ed25519" => sub { 263 plan tests => 2; 264 265 SKIP: { 266 skip "Ed25519 is not supported by this OpenSSL build", 2 267 if disabled("ec"); 268 269 ok(run(app(["openssl", "req", 270 "-config", srctop_file("test", "test.cnf"), 271 "-new", "-out", "testreq-ed25519.pem", "-utf8", 272 "-key", srctop_file("test", "tested25519.pem")])), 273 "Generating request"); 274 275 ok(run(app(["openssl", "req", 276 "-config", srctop_file("test", "test.cnf"), 277 "-verify", "-in", "testreq-ed25519.pem", "-noout"])), 278 "Verifying signature on request"); 279 } 280}; 281 282subtest "generating certificate requests with Ed448" => sub { 283 plan tests => 2; 284 285 SKIP: { 286 skip "Ed448 is not supported by this OpenSSL build", 2 287 if disabled("ec"); 288 289 ok(run(app(["openssl", "req", 290 "-config", srctop_file("test", "test.cnf"), 291 "-new", "-out", "testreq-ed448.pem", "-utf8", 292 "-key", srctop_file("test", "tested448.pem")])), 293 "Generating request"); 294 295 ok(run(app(["openssl", "req", 296 "-config", srctop_file("test", "test.cnf"), 297 "-verify", "-in", "testreq-ed448.pem", "-noout"])), 298 "Verifying signature on request"); 299 } 300}; 301 302subtest "generating certificate requests" => sub { 303 plan tests => 2; 304 305 ok(run(app(["openssl", "req", "-config", srctop_file("test", "test.cnf"), 306 "-key", srctop_file("test", "certs", "ee-key.pem"), 307 @req_new, "-out", "testreq.pem"])), 308 "Generating request"); 309 310 ok(run(app(["openssl", "req", "-config", srctop_file("test", "test.cnf"), 311 "-verify", "-in", "testreq.pem", "-noout"])), 312 "Verifying signature on request"); 313}; 314 315subtest "generating SM2 certificate requests" => sub { 316 plan tests => 4; 317 318 SKIP: { 319 skip "SM2 is not supported by this OpenSSL build", 4 320 if disabled("sm2"); 321 ok(run(app(["openssl", "req", 322 "-config", srctop_file("test", "test.cnf"), 323 "-new", "-key", srctop_file(@certs, "sm2.key"), 324 "-sigopt", "distid:1234567812345678", 325 "-out", "testreq-sm2.pem", "-sm3"])), 326 "Generating SM2 certificate request"); 327 328 ok(run(app(["openssl", "req", 329 "-config", srctop_file("test", "test.cnf"), 330 "-verify", "-in", "testreq-sm2.pem", "-noout", 331 "-vfyopt", "distid:1234567812345678", "-sm3"])), 332 "Verifying signature on SM2 certificate request"); 333 334 ok(run(app(["openssl", "req", 335 "-config", srctop_file("test", "test.cnf"), 336 "-new", "-key", srctop_file(@certs, "sm2.key"), 337 "-sigopt", "hexdistid:DEADBEEF", 338 "-out", "testreq-sm2.pem", "-sm3"])), 339 "Generating SM2 certificate request with hex id"); 340 341 ok(run(app(["openssl", "req", 342 "-config", srctop_file("test", "test.cnf"), 343 "-verify", "-in", "testreq-sm2.pem", "-noout", 344 "-vfyopt", "hexdistid:DEADBEEF", "-sm3"])), 345 "Verifying signature on SM2 certificate request"); 346 } 347}; 348 349my @openssl_args = ("req", "-config", srctop_file("apps", "openssl.cnf")); 350 351run_conversion('req conversions', 352 "testreq.pem"); 353run_conversion('req conversions -- testreq2', 354 srctop_file("test", "testreq2.pem")); 355 356sub run_conversion { 357 my $title = shift; 358 my $reqfile = shift; 359 360 subtest $title => sub { 361 run(app(["openssl", @openssl_args, 362 "-in", $reqfile, "-inform", "p", 363 "-noout", "-text"], 364 stderr => "req-check.err", stdout => undef)); 365 open DATA, "req-check.err"; 366 SKIP: { 367 plan skip_all => "skipping req conversion test for $reqfile" 368 if grep /Unknown Public Key/, map { s/\R//; } <DATA>; 369 370 tconversion( -type => 'req', -in => $reqfile, 371 -args => [ @openssl_args ] ); 372 } 373 close DATA; 374 unlink "req-check.err"; 375 376 done_testing(); 377 }; 378} 379 380# Test both generation and verification of certs w.r.t. RFC 5280 requirements 381 382my $ca_cert; # will be set below 383sub generate_cert { 384 my $cert = shift @_; 385 my $ss = $cert =~ m/self-signed/; 386 my $is_ca = $cert =~ m/CA/; 387 my $cn = $is_ca ? "CA" : "EE"; 388 my $ca_key = srctop_file(@certs, "ca-key.pem"); 389 my $key = $is_ca ? $ca_key : srctop_file(@certs, "ee-key.pem"); 390 my @cmd = ("openssl", "req", "-config", "", "-x509", 391 "-subj", "/CN=$cn", @_, "-out", $cert); 392 push(@cmd, ("-key", $key)) if $ss; 393 push(@cmd, ("-CA", $ca_cert, "-CAkey", $ca_key)) unless $ss; 394 ok(run(app([@cmd])), "generate $cert"); 395} 396sub has_SKID { 397 my $cert = shift @_; 398 my $expect = shift @_; 399 cert_contains($cert, "Subject Key Identifier", $expect); 400} 401sub has_AKID { 402 my $cert = shift @_; 403 my $expect = shift @_; 404 cert_contains($cert, "Authority Key Identifier", $expect); 405} 406sub has_keyUsage { 407 my $cert = shift @_; 408 my $expect = shift @_; 409 cert_contains($cert, "Key Usage", $expect); 410} 411sub strict_verify { 412 my $cert = shift @_; 413 my $expect = shift @_; 414 my $trusted = shift @_; 415 $trusted = $cert unless $trusted; 416 ok(run(app(["openssl", "verify", "-x509_strict", "-trusted", $trusted, 417 "-partial_chain", $cert])) == $expect, 418 "strict verify allow $cert"); 419} 420 421my @v3_ca = ("-addext", "basicConstraints = critical,CA:true", 422 "-addext", "keyUsage = keyCertSign"); 423my $SKID_AKID = "subjectKeyIdentifier,authorityKeyIdentifier"; 424my $cert = "self-signed_v1_CA_no_KIDs.pem"; 425generate_cert($cert); 426cert_ext_has_n_different_lines($cert, 0, $SKID_AKID); # no SKID and no AKID 427#TODO strict_verify($cert, 1); # self-signed v1 root cert should be accepted as CA 428 429$ca_cert = "self-signed_v3_CA_default_SKID.pem"; 430generate_cert($ca_cert, @v3_ca); 431has_SKID($ca_cert, 1); 432has_AKID($ca_cert, 0); 433strict_verify($ca_cert, 1); 434 435$cert = "self-signed_v3_CA_no_SKID.pem"; 436generate_cert($cert, @v3_ca, "-addext", "subjectKeyIdentifier = none"); 437cert_ext_has_n_different_lines($cert, 0, $SKID_AKID); # no SKID and no AKID 438#TODO strict_verify($cert, 0); 439 440$cert = "self-signed_v3_CA_both_KIDs.pem"; 441generate_cert($cert, @v3_ca, "-addext", "subjectKeyIdentifier = hash", 442 "-addext", "authorityKeyIdentifier = keyid:always"); 443cert_ext_has_n_different_lines($cert, 3, $SKID_AKID); # SKID == AKID 444strict_verify($cert, 1); 445 446$cert = "self-signed_v3_EE_wrong_keyUsage.pem"; 447generate_cert($cert, "-addext", "keyUsage = keyCertSign"); 448#TODO strict_verify($cert, 1); # should be accepted because RFC 5280 does not apply 449 450$cert = "v3_EE_default_KIDs.pem"; 451generate_cert($cert, "-addext", "keyUsage = dataEncipherment", 452 "-key", srctop_file(@certs, "ee-key.pem")); 453cert_ext_has_n_different_lines($cert, 4, $SKID_AKID); # SKID != AKID 454strict_verify($cert, 1, $ca_cert); 455 456$cert = "v3_EE_no_AKID.pem"; 457generate_cert($cert, "-addext", "authorityKeyIdentifier = none", 458 "-key", srctop_file(@certs, "ee-key.pem")); 459has_SKID($cert, 1); 460has_AKID($cert, 0); 461strict_verify($cert, 0, $ca_cert); 462 463$cert = "self-issued_v3_EE_default_KIDs.pem"; 464generate_cert($cert, "-addext", "keyUsage = dataEncipherment", 465 "-in", srctop_file(@certs, "x509-check.csr")); 466cert_ext_has_n_different_lines($cert, 4, $SKID_AKID); # SKID != AKID 467strict_verify($cert, 1); 468 469my $cert = "self-signed_CA_no_keyUsage.pem"; 470generate_cert($cert, "-in", srctop_file(@certs, "ext-check.csr")); 471has_keyUsage($cert, 0); 472my $cert = "self-signed_CA_with_keyUsages.pem"; 473generate_cert($cert, "-in", srctop_file(@certs, "ext-check.csr"), 474 "-copy_extensions", "copy"); 475has_keyUsage($cert, 1); 476