/*
* Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 6802846 8172529
* @summary jarsigner needs enhanced cert validation(options)
* @library /test/lib
* @run main/timeout=240 ConciseJarsigner
*/
import jdk.test.lib.Asserts;
import jdk.test.lib.SecurityTools;
import jdk.test.lib.process.OutputAnalyzer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Calendar;
import java.util.List;
public class ConciseJarsigner {
static OutputAnalyzer kt(String cmd) throws Exception {
// Choose 1024-bit RSA to make sure it runs fine and fast. In
// fact, every keyalg/keysize combination is OK for this test.
return SecurityTools.keytool("-storepass changeit -keypass changeit "
+ "-keystore ks -keyalg rsa -keysize 1024 " + cmd);
}
static void gencert(String owner, String cmd) throws Exception {
kt("-certreq -alias " + owner + " -file tmp.req");
kt("-gencert -infile tmp.req -outfile tmp.cert " + cmd);
kt("-import -alias " + owner + " -file tmp.cert");
}
static OutputAnalyzer js(String cmd) throws Exception {
return SecurityTools.jarsigner("-debug " + cmd);
}
public static void main(String[] args) throws Exception {
Files.write(Path.of("A1"), List.of("a1"));
Files.write(Path.of("A2"), List.of("a2"));
Files.write(Path.of("A3"), List.of("a3"));
Files.write(Path.of("A4"), List.of("a4"));
Files.write(Path.of("A5"), List.of("a5"));
Files.write(Path.of("A6"), List.of("a6"));
String year = "" + Calendar.getInstance().get(Calendar.YEAR);
// ==========================================================
// First part: output format
// ==========================================================
kt("-genkeypair -alias a1 -dname CN=a1 -validity 366");
kt("-genkeypair -alias a2 -dname CN=a2 -validity 366");
// a.jar includes 8 unsigned, 2 signed by a1 and a2, 2 signed by a3
SecurityTools.jar("cvf a.jar A1 A2");
js("-keystore ks -storepass changeit a.jar a1");
SecurityTools.jar("uvf a.jar A3 A4");
js("-keystore ks -storepass changeit a.jar a2");
SecurityTools.jar("uvf a.jar A5 A6");
// Verify OK
js("-verify a.jar").shouldHaveExitValue(0);
// 4(chainNotValidated)+16(hasUnsignedEntry)
js("-verify a.jar -strict").shouldHaveExitValue(20);
// 16(hasUnsignedEntry)
js("-verify a.jar -strict -keystore ks -storepass changeit")
.shouldHaveExitValue(16);
// 16(hasUnsignedEntry)+32(notSignedByAlias)
js("-verify a.jar a1 -strict -keystore ks -storepass changeit")
.shouldHaveExitValue(48);
// 16(hasUnsignedEntry)
js("-verify a.jar a1 a2 -strict -keystore ks -storepass changeit")
.shouldHaveExitValue(16);
// 12 entries all together
Asserts.assertTrue(js("-verify a.jar -verbose")
.asLines().stream()
.filter(s -> s.contains(year))
.count() == 12);
// 12 entries all listed
Asserts.assertTrue(js("-verify a.jar -verbose:grouped")
.asLines().stream()
.filter(s -> s.contains(year))
.count() == 12);
// 4 groups: MANIFST, unrelated, signed, unsigned
Asserts.assertTrue(js("-verify a.jar -verbose:summary")
.asLines().stream()
.filter(s -> s.contains(year))
.count() == 4);
// still 4 groups, but MANIFEST group has no other file
Asserts.assertTrue(js("-verify a.jar -verbose:summary")
.asLines().stream()
.filter(s -> s.contains("more)"))
.count() == 3);
// 5 groups: MANIFEST, unrelated, signed by a1/a2, signed by a2, unsigned
Asserts.assertTrue(js("-verify a.jar -verbose:summary -certs")
.asLines().stream()
.filter(s -> s.contains(year))
.count() == 5);
// 2 for MANIFEST, 2*2 for A1/A2, 2 for A3/A4
Asserts.assertTrue(js("-verify a.jar -verbose -certs")
.asLines().stream()
.filter(s -> s.contains("[certificate"))
.count() == 8);
// a1,a2 for MANIFEST, a1,a2 for A1/A2, a2 for A3/A4
Asserts.assertTrue(js("-verify a.jar -verbose:grouped -certs")
.asLines().stream()
.filter(s -> s.contains("[certificate"))
.count() == 5);
// a1,a2 for MANIFEST, a1,a2 for A1/A2, a2 for A3/A4
Asserts.assertTrue(js("-verify a.jar -verbose:summary -certs")
.asLines().stream()
.filter(s -> s.contains("[certificate"))
.count() == 5);
// still 5 groups, but MANIFEST group has no other file
Asserts.assertTrue(js("-verify a.jar -verbose:summary -certs")
.asLines().stream()
.filter(s -> s.contains("more)"))
.count() == 4);
// ==========================================================
// Second part: exit code 2, 4, 8.
// 16 and 32 already covered in the first part
// ==========================================================
kt("-genkeypair -alias ca -dname CN=ca -ext bc -validity 365");
kt("-genkeypair -alias expired -dname CN=expired");
gencert("expired", "-alias ca -startdate -10m");
kt("-genkeypair -alias notyetvalid -dname CN=notyetvalid");
gencert("notyetvalid", "-alias ca -startdate +1m");
kt("-genkeypair -alias badku -dname CN=badku");
gencert("badku", "-alias ca -ext KU=cRLSign -validity 365");
kt("-genkeypair -alias badeku -dname CN=badeku");
gencert("badeku", "-alias ca -ext EKU=sa -validity 365");
kt("-genkeypair -alias goodku -dname CN=goodku");
gencert("goodku", "-alias ca -ext KU=dig -validity 365");
kt("-genkeypair -alias goodeku -dname CN=goodeku");
gencert("goodeku", "-alias ca -ext EKU=codesign -validity 365");
js("-strict -keystore ks -storepass changeit a.jar expired")
.shouldHaveExitValue(4);
js("-strict -keystore ks -storepass changeit a.jar notyetvalid")
.shouldHaveExitValue(4);
js("-strict -keystore ks -storepass changeit a.jar badku")
.shouldHaveExitValue(8);
js("-strict -keystore ks -storepass changeit a.jar badeku")
.shouldHaveExitValue(8);
js("-strict -keystore ks -storepass changeit a.jar goodku")
.shouldHaveExitValue(0);
js("-strict -keystore ks -storepass changeit a.jar goodeku")
.shouldHaveExitValue(0);
// badchain signed by ca1, but ca1 is removed later
kt("-genkeypair -alias badchain -dname CN=badchain -validity 365");
kt("-genkeypair -alias ca1 -dname CN=ca1 -ext bc -validity 365");
gencert("badchain", "-alias ca1 -validity 365");
// save ca1.cert for easy replay
kt("-exportcert -file ca1.cert -alias ca1");
kt("-delete -alias ca1");
js("-strict -keystore ks -storepass changeit a.jar badchain")
.shouldHaveExitValue(4);
js("-verify a.jar").shouldHaveExitValue(0);
// ==========================================================
// Third part: -certchain test
// ==========================================================
// altchain signed by ca2
kt("-genkeypair -alias altchain -dname CN=altchain -validity 365");
kt("-genkeypair -alias ca2 -dname CN=ca2 -ext bc -validity 365");
kt("-certreq -alias altchain -file altchain.req");
Files.write(Path.of("certchain"), List.of(
kt("-gencert -alias ca2 -validity 365 -rfc -infile altchain.req")
.getOutput(),
kt("-exportcert -alias ca2 -rfc").getOutput()));
// Self-signed cert does not work
js("-strict -keystore ks -storepass changeit a.jar altchain")
.shouldHaveExitValue(4);
// -certchain works
js("-strict -keystore ks -storepass changeit -certchain certchain "
+ "a.jar altchain")
.shouldHaveExitValue(0);
// if ca2 is removed, -certchain still work because altchain is a
// self-signed entry and it is trusted by jarsigner
// save ca2.cert for easy replay
kt("-exportcert -file ca2.cert -alias ca2");
kt("-delete -alias ca2");
js("-strict -keystore ks -storepass changeit "
+ "-certchain certchain a.jar altchain")
.shouldHaveExitValue(0);
// if cert is imported, -certchain won't work because this
// certificate entry is not trusted
kt("-importcert -file certchain -alias altchain -noprompt");
js("-strict -keystore ks -storepass changeit "
+ "-certchain certchain a.jar altchain")
.shouldHaveExitValue(4);
js("-verify a.jar").shouldHaveExitValue(0);
// ==========================================================
// 8172529
// ==========================================================
kt("-genkeypair -alias ee -dname CN=ee");
kt("-genkeypair -alias caone -dname CN=caone");
kt("-genkeypair -alias catwo -dname CN=catwo");
kt("-certreq -alias ee -file ee.req");
kt("-certreq -alias catwo -file catwo.req");
// This certchain contains a cross-signed weak catwo.cert
Files.write(Path.of("ee2"), List.of(
kt("-gencert -alias catwo -rfc -infile ee.req").getOutput(),
kt("-gencert -alias caone -sigalg MD5withRSA -rfc "
+ "-infile catwo.req").getOutput()));
kt("-importcert -alias ee -file ee2");
SecurityTools.jar("cvf a.jar A1");
js("-strict -keystore ks -storepass changeit a.jar ee")
.shouldHaveExitValue(0);
js("-strict -keystore ks -storepass changeit -verify a.jar")
.shouldHaveExitValue(0);
}
}