1#! /usr/bin/env perl 2# Copyright 2016-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 9use strict; 10use warnings; 11 12use OpenSSL::Test qw/:DEFAULT srctop_file bldtop_dir with/; 13use OpenSSL::Test::Utils; 14 15use Encode; 16 17setup("test_pkcs12"); 18 19my $pass = "σύνθημα γνώρισμα"; 20 21my $savedcp; 22if (eval { require Win32::API; 1; }) { 23 # Trouble is that Win32 perl uses CreateProcessA, which 24 # makes it problematic to pass non-ASCII arguments, from perl[!] 25 # that is. This is because CreateProcessA is just a wrapper for 26 # CreateProcessW and will call MultiByteToWideChar and use 27 # system default locale. Since we attempt Greek pass-phrase 28 # conversion can be done only with Greek locale. 29 30 Win32::API->Import("kernel32","UINT GetSystemDefaultLCID()"); 31 if (GetSystemDefaultLCID() != 0x408) { 32 plan skip_all => "Non-Greek system locale"; 33 } else { 34 # Ensure correct code page so that VERBOSE output is right. 35 Win32::API->Import("kernel32","UINT GetConsoleOutputCP()"); 36 Win32::API->Import("kernel32","BOOL SetConsoleOutputCP(UINT cp)"); 37 $savedcp = GetConsoleOutputCP(); 38 SetConsoleOutputCP(1253); 39 $pass = Encode::encode("cp1253",Encode::decode("utf-8",$pass)); 40 } 41} elsif ($^O eq "MSWin32") { 42 plan skip_all => "Win32::API unavailable"; 43} elsif ($^O ne "VMS") { 44 # Running MinGW tests transparently under Wine apparently requires 45 # UTF-8 locale... 46 47 foreach(`locale -a`) { 48 s/\R$//; 49 if ($_ =~ m/^C\.UTF\-?8/i) { 50 $ENV{LC_ALL} = $_; 51 last; 52 } 53 } 54} 55$ENV{OPENSSL_WIN32_UTF8}=1; 56 57my $no_fips = disabled('fips') || ($ENV{NO_FIPS} // 0); 58 59plan tests => $no_fips ? 46 : 52; 60 61# Test different PKCS#12 formats 62ok(run(test(["pkcs12_format_test"])), "test pkcs12 formats"); 63# Test with legacy APIs 64ok(run(test(["pkcs12_format_test", "-legacy"])), "test pkcs12 formats using legacy APIs"); 65# Test with a non-default library context (and no loaded providers in the default context) 66ok(run(test(["pkcs12_format_test", "-context"])), "test pkcs12 formats using a non-default library context"); 67 68SKIP: { 69 skip "VMS doesn't have command line UTF-8 support yet in DCL", 1 70 if $^O eq "VMS"; 71 72 # just see that we can read shibboleth.pfx protected with $pass 73 ok(run(app(["openssl", "pkcs12", "-noout", 74 "-password", "pass:$pass", 75 "-in", srctop_file("test", "shibboleth.pfx")])), 76 "test_load_cert_pkcs12"); 77} 78 79my @path = qw(test certs); 80my $outfile1 = "out1.p12"; 81my $outfile2 = "out2.p12"; 82my $outfile3 = "out3.p12"; 83my $outfile4 = "out4.p12"; 84my $outfile5 = "out5.p12"; 85my $outfile6 = "out6.p12"; 86my $outfile7 = "out7.p12"; 87 88# Test the -chain option with -untrusted 89ok(run(app(["openssl", "pkcs12", "-export", "-chain", 90 "-CAfile", srctop_file(@path, "sroot-cert.pem"), 91 "-untrusted", srctop_file(@path, "ca-cert.pem"), 92 "-in", srctop_file(@path, "ee-cert.pem"), 93 "-nokeys", "-passout", "pass:", "-out", $outfile1])), 94 "test_pkcs12_chain_untrusted"); 95 96# Test the -passcerts option 97SKIP: { 98 skip "Skipping PKCS#12 test because DES is disabled in this build", 1 99 if disabled("des"); 100 ok(run(app(["openssl", "pkcs12", "-export", 101 "-in", srctop_file(@path, "ee-cert.pem"), 102 "-certfile", srctop_file(@path, "v3-certs-TDES.p12"), 103 "-passcerts", "pass:v3-certs", 104 "-nokeys", "-passout", "pass:v3-certs", "-descert", 105 "-out", $outfile2])), 106 "test_pkcs12_passcerts"); 107} 108 109SKIP: { 110 skip "Skipping legacy PKCS#12 test because the required algorithms are disabled", 1 111 if disabled("des") || disabled("rc2") || disabled("legacy"); 112 # Test reading legacy PKCS#12 file 113 ok(run(app(["openssl", "pkcs12", "-export", 114 "-in", srctop_file(@path, "v3-certs-RC2.p12"), 115 "-passin", "pass:v3-certs", 116 "-provider", "default", "-provider", "legacy", 117 "-nokeys", "-passout", "pass:v3-certs", "-descert", 118 "-out", $outfile3])), 119 "test_pkcs12_passcerts_legacy"); 120} 121 122# Test export of PEM file with both cert and key 123# -nomac necessary to avoid legacy provider requirement 124ok(run(app(["openssl", "pkcs12", "-export", 125 "-inkey", srctop_file(@path, "cert-key-cert.pem"), 126 "-in", srctop_file(@path, "cert-key-cert.pem"), 127 "-passout", "pass:v3-certs", 128 "-nomac", "-out", $outfile4], stderr => "outerr.txt")), 129 "test_export_pkcs12_cert_key_cert"); 130open DATA, "outerr.txt"; 131my @match = grep /:error:/, <DATA>; 132close DATA; 133ok(scalar @match > 0 ? 0 : 1, "test_export_pkcs12_outerr_empty"); 134 135ok(run(app(["openssl", "pkcs12", 136 "-in", $outfile4, 137 "-passin", "pass:v3-certs", 138 "-nomacver", "-nodes"])), 139 "test_import_pkcs12_cert_key_cert"); 140 141ok(run(app(["openssl", "pkcs12", "-export", "-out", $outfile5, 142 "-in", srctop_file(@path, "ee-cert.pem"), "-caname", "testname", 143 "-nokeys", "-passout", "pass:", "-certpbe", "NONE"])), 144 "test nokeys single cert"); 145 146my @pkcs12info = run(app(["openssl", "pkcs12", "-info", "-in", $outfile5, 147 "-passin", "pass:"]), capture => 1); 148 149# Test that with one input certificate, we get one output certificate 150ok(grep(/subject=CN\s*=\s*server.example/, @pkcs12info) == 1, 151 "test one cert in output"); 152 153# Test that the expected friendly name is present in the output 154ok(grep(/testname/, @pkcs12info) == 1, "test friendly name in output"); 155 156# Test there's no Oracle Trusted Key Usage bag attribute 157ok(grep(/Trusted key usage (Oracle)/, @pkcs12info) == 0, 158 "test no oracle trusted key usage"); 159 160# Test export of PEM file with both cert and key, without password. 161# -nomac necessary to avoid legacy provider requirement 162{ 163 ok(run(app(["openssl", "pkcs12", "-export", 164 "-inkey", srctop_file(@path, "cert-key-cert.pem"), 165 "-in", srctop_file(@path, "cert-key-cert.pem"), 166 "-passout", "pass:", 167 "-nomac", "-out", $outfile6], stderr => "outerr6.txt")), 168 "test_export_pkcs12_cert_key_cert_no_pass"); 169 open DATA, "outerr6.txt"; 170 my @match = grep /:error:/, <DATA>; 171 close DATA; 172 ok(scalar @match > 0 ? 0 : 1, "test_export_pkcs12_outerr6_empty"); 173} 174 175my %pbmac1_tests = ( 176 pbmac1_defaults => {args => [], lookup => "hmacWithSHA256"}, 177 pbmac1_nondefaults => {args => ["-pbmac1_pbkdf2_md", "sha512", "-macalg", "sha384"], lookup => "hmacWithSHA512"}, 178); 179 180for my $instance (sort keys %pbmac1_tests) { 181 my $extra_args = $pbmac1_tests{$instance}{args}; 182 my $lookup = $pbmac1_tests{$instance}{lookup}; 183 # Test export of PEM file with both cert and key, with password. 184 { 185 my $pbmac1_id = $instance; 186 ok(run(app(["openssl", "pkcs12", "-export", "-pbmac1_pbkdf2", 187 "-inkey", srctop_file(@path, "cert-key-cert.pem"), 188 "-in", srctop_file(@path, "cert-key-cert.pem"), 189 "-passout", "pass:1234", 190 @$extra_args, 191 "-out", "$pbmac1_id.p12"], stderr => "${pbmac1_id}_err.txt")), 192 "test_export_pkcs12_${pbmac1_id}"); 193 open DATA, "${pbmac1_id}_err.txt"; 194 my @match = grep /:error:/, <DATA>; 195 close DATA; 196 ok(scalar @match > 0 ? 0 : 1, "test_export_pkcs12_${pbmac1_id}_err.empty"); 197 198 ok(run(app(["openssl", "pkcs12", "-in", "$pbmac1_id.p12", "-info", "-noout", 199 "-passin", "pass:1234"], stderr => "${pbmac1_id}_info.txt")), 200 "test_export_pkcs12_${pbmac1_id}_info"); 201 open DATA, "${pbmac1_id}_info.txt"; 202 my @match = grep /$lookup/, <DATA>; 203 close DATA; 204 ok(scalar @match > 0 ? 1 : 0, "test_export_pkcs12_${pbmac1_id}_info"); 205 } 206} 207 208# Test pbmac1 pkcs12 good files, RFC 9579 209for my $file ("pbmac1_256_256.good.p12", "pbmac1_512_256.good.p12", "pbmac1_512_512.good.p12") 210{ 211 my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file); 212 ok(run(app(["openssl", "pkcs12", "-in", $path, "-password", "pass:1234", "-noenc"])), 213 "test pbmac1 pkcs12 file $file"); 214} 215 216 217unless ($no_fips) { 218 my $provpath = bldtop_dir("providers"); 219 my $provconf = srctop_file("test", "fips-and-base.cnf"); 220 my $provname = 'fips'; 221 my @prov = ("-provider-path", $provpath, 222 "-provider", $provname); 223 local $ENV{OPENSSL_CONF} = $provconf; 224 225# Test pbmac1 pkcs12 good files, RFC 9579 226 for my $file ("pbmac1_256_256.good.p12", "pbmac1_512_256.good.p12", "pbmac1_512_512.good.p12") 227 { 228 my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file); 229 ok(run(app(["openssl", "pkcs12", @prov, "-in", $path, "-password", "pass:1234", "-noenc"])), 230 "test pbmac1 pkcs12 file $file"); 231 232 ok(run(app(["openssl", "pkcs12", @prov, "-in", $path, "-info", "-noout", 233 "-passin", "pass:1234"], stderr => "${file}_info.txt")), 234 "test_export_pkcs12_${file}_info"); 235 } 236} 237 238# Test pbmac1 pkcs12 bad files, RFC 9579 239for my $file ("pbmac1_256_256.bad-iter.p12", "pbmac1_256_256.bad-salt.p12", "pbmac1_256_256.no-len.p12") 240{ 241 my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file); 242 with({ exit_checker => sub { return shift == 1; } }, 243 sub { 244 ok(run(app(["openssl", "pkcs12", "-in", $path, "-password", "pass:1234", "-noenc"])), 245 "test pbmac1 pkcs12 bad file $file"); 246 } 247 ); 248} 249 250# Test pbmac1 pkcs12 file with absent PBKDF2 PRF, usually omitted when selecting sha1 251{ 252 my $file = "pbmac1_sha1_hmac_and_prf.p12"; 253 my $path = srctop_file("test", "recipes", "80-test_pkcs12_data", $file); 254 ok(run(app(["openssl", "pkcs12", "-in", $path, "-password", "pass:1234", "-noenc"])), 255 "test pbmac1 pkcs12 file $file"); 256} 257 258# Test some bad pkcs12 files 259my $bad1 = srctop_file("test", "recipes", "80-test_pkcs12_data", "bad1.p12"); 260my $bad2 = srctop_file("test", "recipes", "80-test_pkcs12_data", "bad2.p12"); 261my $bad3 = srctop_file("test", "recipes", "80-test_pkcs12_data", "bad3.p12"); 262 263with({ exit_checker => sub { return shift == 1; } }, 264 sub { 265 ok(run(app(["openssl", "pkcs12", "-in", $bad1, "-password", "pass:"])), 266 "test bad pkcs12 file 1"); 267 268 ok(run(app(["openssl", "pkcs12", "-in", $bad1, "-password", "pass:", 269 "-nomacver"])), 270 "test bad pkcs12 file 1 (nomacver)"); 271 272 ok(run(app(["openssl", "pkcs12", "-in", $bad1, "-password", "pass:", 273 "-info"])), 274 "test bad pkcs12 file 1 (info)"); 275 276 ok(run(app(["openssl", "pkcs12", "-in", $bad2, "-password", "pass:"])), 277 "test bad pkcs12 file 2"); 278 279 ok(run(app(["openssl", "pkcs12", "-in", $bad2, "-password", "pass:", 280 "-info"])), 281 "test bad pkcs12 file 2 (info)"); 282 283 ok(run(app(["openssl", "pkcs12", "-in", $bad3, "-password", "pass:"])), 284 "test bad pkcs12 file 3"); 285 286 ok(run(app(["openssl", "pkcs12", "-in", $bad3, "-password", "pass:", 287 "-info"])), 288 "test bad pkcs12 file 3 (info)"); 289 }); 290 291# Test with Oracle Trusted Key Usage specified in openssl.cnf 292{ 293 ok(run(app(["openssl", "pkcs12", "-export", "-out", $outfile7, 294 "-jdktrust", "anyExtendedKeyUsage", "-in", srctop_file(@path, "ee-cert.pem"), 295 "-nokeys", "-passout", "pass:", "-certpbe", "NONE"])), 296 "test nokeys single cert"); 297 298 my @pkcs12info = run(app(["openssl", "pkcs12", "-info", "-in", $outfile7, 299 "-passin", "pass:"]), capture => 1); 300 ok(grep(/Trusted key usage \(Oracle\): Any Extended Key Usage \(2.5.29.37.0\)/, @pkcs12info) == 1, 301 "test oracle trusted key usage is set"); 302 303 delete $ENV{OPENSSL_CONF} 304} 305 306# Tests for pkcs12_parse 307ok(run(test(["pkcs12_api_test", 308 "-in", $outfile1, 309 "-has-ca", 1, 310 ])), "Test pkcs12_parse()"); 311 312SKIP: { 313 skip "Skipping PKCS#12 parse test because DES is disabled in this build", 1 314 if disabled("des"); 315 ok(run(test(["pkcs12_api_test", 316 "-in", $outfile2, 317 "-pass", "v3-certs", 318 "-has-ca", 1, 319 ])), "Test pkcs12_parse()"); 320} 321 322SKIP: { 323 skip "Skipping PKCS#12 parse test because the required algorithms are disabled", 1 324 if disabled("des") || disabled("rc2") || disabled("legacy"); 325 ok(run(test(["pkcs12_api_test", 326 "-in", $outfile3, 327 "-pass", "v3-certs", 328 "-has-ca", 1, 329 ])), "Test pkcs12_parse()"); 330} 331 332ok(run(test(["pkcs12_api_test", 333 "-in", $outfile4, 334 "-pass", "v3-certs", 335 "-has-ca", 1, 336 "-has-key", 1, 337 "-has-cert", 1, 338 ])), "Test pkcs12_parse()"); 339 340ok(run(test(["pkcs12_api_test", 341 "-in", $outfile5, 342 "-has-ca", 1, 343 ])), "Test pkcs12_parse()"); 344 345ok(run(test(["pkcs12_api_test", 346 "-in", $outfile6, 347 "-pass", "", 348 "-has-ca", 1, 349 "-has-key", 1, 350 "-has-cert", 1, 351 ])), "Test pkcs12_parse()"); 352 353SetConsoleOutputCP($savedcp) if (defined($savedcp)); 354