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