diff --git a/CMakeLists.txt b/CMakeLists.txt index c1096c19..08b540ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -590,7 +590,24 @@ add_custom_command( add_custom_target(tests-jar DEPENDS ${TESTS_JAR}) set_property(TARGET tests-jar PROPERTY JAR_FILE ${TESTS_JAR}) +# These flags are necessary in Java17+ in order for certain unit tests to +# perform deep reflection on nonpublic members + +if (Java_VERSION_MAJOR VERSION_GREATER_EQUAL 17) + message(STATUS "Adding needed flags for deep reflection") + set(TEST_ADD_OPENS + --add-opens java.base/javax.crypto=ALL-UNNAMED + --add-opens java.base/java.lang.invoke=ALL-UNNAMED + --add-opens java.base/java.security=ALL-UNNAMED + --add-opens java.base/sun.security.util=ALL-UNNAMED + --add-exports jdk.crypto.ec/sun.security.ec=java.base + ) +else() + message(STATUS "Not adding needed flags for deep reflection") +endif() + set(TEST_RUNNER_ARGUMENTS + ${TEST_ADD_OPENS} -javaagent:${JACOCO_AGENT_JAR}=destfile=coverage/jacoco.exec,classdumpdir=coverage/classes -Djava.library.path=$ -Dcom.amazon.corretto.crypto.provider.inTestSuite=hunter2 diff --git a/build.gradle b/build.gradle index f24385de..3a51c52c 100644 --- a/build.gradle +++ b/build.gradle @@ -9,9 +9,9 @@ plugins { } group = 'software.amazon.cryptools' -version = '1.6.1' +version = '1.6.2' -def openssl_version = '1.1.1j' +def openssl_version = '1.1.1t' def opensslSrcPath = "${buildDir}/openssl/openssl-${openssl_version}" configurations { @@ -55,8 +55,8 @@ dependencies { testDep 'org.junit.jupiter:junit-jupiter:5.6.2' testDep 'org.junit.vintage:junit-vintage-engine:5.6.2' - testDep 'org.bouncycastle:bcprov-debug-jdk15on:1.65' - testDep 'org.bouncycastle:bcpkix-jdk15on:1.65' + testDep 'org.bouncycastle:bcprov-debug-jdk15on:1.69' + testDep 'org.bouncycastle:bcpkix-jdk15on:1.69' testDep 'commons-codec:commons-codec:1.12' testDep 'org.hamcrest:hamcrest:2.1' testDep 'org.jacoco:org.jacoco.core:0.8.3' @@ -138,18 +138,19 @@ task executeCmake(type: Exec) { workingDir "${buildDir}/cmake" def prebuiltJar = null if (project.hasProperty('stagingUrl')) { + println "Using stagingUrl: ${stagingUrl}" mkdir "${buildDir}/tmp" exec { workingDir "${buildDir}/tmp" - commandLine 'wget', "${stagingUrl}/software/amazon/cryptools/${project.name}/${stagingVersion}/${project.name}-${stagingVersion}-linux-x86_64.jar" + commandLine 'wget', "${stagingUrl}/software/amazon/cryptools/${project.name}/${version}/${project.name}-${version}-linux-x86_64.jar" } - prebuiltJar = "${buildDir}/tmp/${project.name}-${stagingVersion}-linux-x86_64.jar" + prebuiltJar = "${buildDir}/tmp/${project.name}-${version}-linux-x86_64.jar" } else if (System.properties['prebuiltJar'] != null) { prebuiltJar = "${projectDir}/" + System.properties['prebuiltJar'] } - executable 'cmake' - args 'cmake', "-DTEST_CLASSPATH=${configurations.testDep.asPath}", "-DJACOCO_AGENT_JAR=${configurations.jacocoAgent.singleFile}" + executable 'cmake3' + args "-DTEST_CLASSPATH=${configurations.testDep.asPath}", "-DJACOCO_AGENT_JAR=${configurations.jacocoAgent.singleFile}" args "-DOPENSSL_ROOT_DIR=${buildDir}/openssl/bin", '-DCMAKE_BUILD_TYPE=Release', '-DPROVIDER_VERSION_STRING=' + version args "-DTEST_RUNNER_JAR=${configurations.testRunner.singleFile}" @@ -303,7 +304,7 @@ task coverage_cmake(type: Exec) { mkdir "${buildDir}/cmake-coverage" } workingDir "${buildDir}/cmake-coverage" - executable 'cmake' + executable 'cmake3' args "-DTEST_CLASSPATH=${configurations.testDep.asPath}" args "-DJACOCO_AGENT_JAR=${configurations.jacocoAgent.singleFile}" args "-DOPENSSL_ROOT_DIR=${buildDir}/openssl/bin" @@ -381,7 +382,8 @@ task overkill { } -if (project.hasProperty('mavenUser') && project.hasProperty('jcecertAlias')) { +// Avoid configuring publish related tasks when the Jar is provided +if (!project.hasProperty('stagingUrl') && project.hasProperty('mavenUser') && project.hasProperty('jcecertAlias')) { publishing { publications { mavenJava(MavenPublication) { @@ -430,19 +432,6 @@ if (project.hasProperty('mavenUser') && project.hasProperty('jcecertAlias')) { signing { sign publishing.publications.mavenJava } -} else { - task publish(overwrite: true) { - doFirst { - ant.fail('Insufficient configuration for publishing') - } - } - - task sign(overwrite: true) { - doFirst { - ant.fail('Insufficient configuration for signing') - } - } - } task clean(overwrite: true, type: Delete) { diff --git a/docker/Centos6ReleaseTest.Dockerfile b/docker/Centos6ReleaseTest.Dockerfile new file mode 100644 index 00000000..f20c3bfb --- /dev/null +++ b/docker/Centos6ReleaseTest.Dockerfile @@ -0,0 +1,29 @@ +FROM public.ecr.aws/docker/library/centos:centos6 + +RUN mkdir -p /var/cache/yum/x86_64/6/base +RUN mkdir -p /var/cache/yum/x86_64/6/extras +RUN mkdir -p /var/cache/yum/x86_64/6/updates +RUN mkdir -p /var/cache/yum/x86_64/6/centos-sclo-rh +RUN mkdir -p /var/cache/yum/x86_64/6/centos-sclo-sclo + +RUN echo "https://vault.centos.org/6.10/os/x86_64/" > /var/cache/yum/x86_64/6/base/mirrorlist.txt +RUN echo "http://vault.centos.org/6.10/extras/x86_64/" > /var/cache/yum/x86_64/6/extras/mirrorlist.txt +RUN echo "http://vault.centos.org/6.10/updates/x86_64/" > /var/cache/yum/x86_64/6/updates/mirrorlist.txt +RUN echo "http://vault.centos.org/6.10/sclo/x86_64/rh" > /var/cache/yum/x86_64/6/centos-sclo-rh/mirrorlist.txt +RUN echo "http://vault.centos.org/6.10/sclo/x86_64/sclo" > /var/cache/yum/x86_64/6/centos-sclo-sclo/mirrorlist.txt + +RUN yum -y update +RUN yum -y install centos-release-scl +RUN yum -y install devtoolset-7-gcc-c++ gsl wget perl patch + +RUN curl -L -O https://github.com/Kitware/CMake/releases/download/v3.14.3/cmake-3.14.3.tar.gz +RUN tar xzf cmake-3.14.3.tar.gz +RUN (cd cmake-3.14.3 && scl enable devtoolset-7 ./bootstrap && scl enable devtoolset-7 gmake && gmake install) + +RUN ln -s /usr/local/bin/cmake /usr/local/bin/cmake3 + +RUN curl -L -O https://downloads.sourceforge.net/ltp/lcov-1.14-1.noarch.rpm + +RUN curl -L -O https://d3pxv6yz143wms.cloudfront.net/11.0.3.7.1/java-11-amazon-corretto-devel-11.0.3.7-1.x86_64.rpm + +RUN rpm -i java-11-amazon-corretto-devel-11.0.3.7-1.x86_64.rpm diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 838e6bc8..e750102e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/openssl.sha256 b/openssl.sha256 index 65e92eaa..8e868495 100644 --- a/openssl.sha256 +++ b/openssl.sha256 @@ -1 +1 @@ -aaf2fcb575cdf6491b98ab4829abf78a3dec8402b8b81efc8f23c00d443981bf openssl-1.1.1j.tar.gz +8dee9b24bdb1dcbf0c3d1e9b02fb8f6bf22165e807f45adeb7c9677536859d3b openssl-1.1.1t.tar.gz diff --git a/tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java b/tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java index 7bb65aec..3692188f 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/EcGenTest.java @@ -37,6 +37,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.ExecutionMode; @@ -52,6 +54,12 @@ @ResourceLock(value = TestUtil.RESOURCE_GLOBAL, mode = ResourceAccessMode.READ) public class EcGenTest { public static final String[][] KNOWN_CURVES = new String[][] { + new String[]{"secp256r1", "NIST P-256", "X9.62 prime256v1", /* "prime256v1", */ "1.2.840.10045.3.1.7"}, + new String[]{"secp384r1", "NIST P-384", "1.3.132.0.34"}, + new String[]{"secp521r1", "NIST P-521", "1.3.132.0.35"}, + }; + + public static final String[][] LEGACY_CURVES = new String[][] { // Prime Curves new String[]{"secp112r1", "1.3.132.0.6"}, new String[]{"secp112r2", "1.3.132.0.7"}, @@ -66,10 +74,6 @@ public class EcGenTest { new String[]{"secp224k1", "1.3.132.0.32"}, new String[]{"secp224r1", "NIST P-224", "1.3.132.0.33"}, new String[]{"secp256k1", "1.3.132.0.10"}, - // Not supported by Openssl - new String[]{"secp256r1", "NIST P-256", "X9.62 prime256v1", /* "prime256v1", */ "1.2.840.10045.3.1.7"}, - new String[]{"secp384r1", "NIST P-384", "1.3.132.0.34"}, - new String[]{"secp521r1", "NIST P-521", "1.3.132.0.35"}, // Binary Curves new String[]{"sect113r1", "1.3.132.0.4"}, new String[]{"sect113r2", "1.3.132.0.5"}, @@ -98,6 +102,8 @@ public class EcGenTest { new String[]{"X9.62 c2tnb359v1", "1.2.840.10045.3.0.18"}, new String[]{"X9.62 c2tnb431r1", "1.2.840.10045.3.0.20"}, }; + + public static final ECParameterSpec EXPLICIT_CURVE; private static final KeyFactory KEY_FACTORY; @@ -142,44 +148,76 @@ private static String[][] knownCurveParams() { return KNOWN_CURVES; } + private static String[][] legacyCurveParams() { + return LEGACY_CURVES; + } + @ParameterizedTest @MethodSource("knownCurveParams") public void knownCurves(ArgumentsAccessor arguments) throws GeneralSecurityException { for (final Object name : arguments.toArray()) { - ECGenParameterSpec spec = new ECGenParameterSpec((String) name); - nativeGen.initialize(spec); - KeyPair nativePair = nativeGen.generateKeyPair(); - jceGen.initialize(spec); - KeyPair jcePair = jceGen.generateKeyPair(); - final ECParameterSpec jceParams = ((ECPublicKey) jcePair.getPublic()).getParams(); - final ECParameterSpec nativeParams = ((ECPublicKey) nativePair.getPublic()).getParams(); - assertECEquals((String) name, jceParams, nativeParams); - - // Ensure we can construct the curve using raw numbers rather than the name - nativeGen.initialize(jceParams); - nativePair = nativeGen.generateKeyPair(); - assertECEquals(name + "-explicit", jceParams, nativeParams); - - final SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(nativePair.getPublic().getEncoded()); - ASN1Encodable algorithmParameters = publicKeyInfo.getAlgorithm().getParameters(); - assertTrue(algorithmParameters instanceof ASN1ObjectIdentifier, "Public key uses named curve"); - - // PKCS #8 = SEQ [ Integer, AlgorithmIdentifier, Octet String, ???] - // AlgorithmIdentifier = SEQ [ OID, {OID | SEQ}] - final ASN1Sequence p8 = ASN1Sequence.getInstance(nativePair.getPrivate().getEncoded()); - final ASN1Sequence algIdentifier = (ASN1Sequence) p8.getObjectAt(1); - assertTrue(algIdentifier.getObjectAt(1) instanceof ASN1ObjectIdentifier, "Private key uses named curve"); - - // Check encoding/decoding - Key bouncedKey = KEY_FACTORY.generatePublic(new X509EncodedKeySpec(nativePair.getPublic().getEncoded())); - assertEquals(nativePair.getPublic(), bouncedKey, "Public key survives encoding"); - bouncedKey = KEY_FACTORY.generatePrivate(new PKCS8EncodedKeySpec(nativePair.getPrivate().getEncoded())); - assertEquals(nativePair.getPrivate(), bouncedKey, "Private key survives encoding"); + testCurveByName((String) name); + } + } + + @ParameterizedTest + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) + @MethodSource("legacyCurveParams") + public void legacyCurves(ArgumentsAccessor arguments) throws GeneralSecurityException { + for (final Object name : arguments.toArray()) { + testCurveByName((String) name); } } + private void testCurveByName(final String name) throws GeneralSecurityException { + ECGenParameterSpec spec = new ECGenParameterSpec(name); + nativeGen.initialize(spec); + KeyPair nativePair = nativeGen.generateKeyPair(); + jceGen.initialize(spec); + KeyPair jcePair = jceGen.generateKeyPair(); + final ECParameterSpec jceParams = ((ECPublicKey) jcePair.getPublic()).getParams(); + final ECParameterSpec nativeParams = ((ECPublicKey) nativePair.getPublic()).getParams(); + assertECEquals(name, jceParams, nativeParams); + // Ensure we can construct the curve using raw numbers rather than the name + nativeGen.initialize(jceParams); + nativePair = nativeGen.generateKeyPair(); + assertECEquals(name + "-explicit", jceParams, nativeParams); + + final SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(nativePair.getPublic().getEncoded()); + ASN1Encodable algorithmParameters = publicKeyInfo.getAlgorithm().getParameters(); + assertTrue(algorithmParameters instanceof ASN1ObjectIdentifier, "Public key uses named curve"); + + // PKCS #8 = SEQ [ Integer, AlgorithmIdentifier, Octet String, ???] + // AlgorithmIdentifier = SEQ [ OID, {OID | SEQ}] + final ASN1Sequence p8 = ASN1Sequence.getInstance(nativePair.getPrivate().getEncoded()); + final ASN1Sequence algIdentifier = (ASN1Sequence) p8.getObjectAt(1); + assertTrue(algIdentifier.getObjectAt(1) instanceof ASN1ObjectIdentifier, "Private key uses named curve"); + + // Check encoding/decoding + Key bouncedKey = KEY_FACTORY.generatePublic(new X509EncodedKeySpec(nativePair.getPublic().getEncoded())); + assertEquals(nativePair.getPublic(), bouncedKey, "Public key survives encoding"); + bouncedKey = KEY_FACTORY.generatePrivate(new PKCS8EncodedKeySpec(nativePair.getPrivate().getEncoded())); + assertEquals(nativePair.getPrivate(), bouncedKey, "Private key survives encoding"); + } + + @ParameterizedTest + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) + @ValueSource(ints = {192, 224}) + public void legacyKnownSizes(int keysize) throws GeneralSecurityException { + TestUtil.assumeMinimumVersion("1.2.0", nativeGen.getProvider()); + nativeGen.initialize(keysize); + jceGen.initialize(keysize); + + final KeyPair nativePair = nativeGen.generateKeyPair(); + final KeyPair jcePair = jceGen.generateKeyPair(); + + final ECParameterSpec jceParams = ((ECPublicKey) jcePair.getPublic()).getParams(); + final ECParameterSpec nativeParams = ((ECPublicKey) nativePair.getPublic()).getParams(); + assertECEquals(Integer.toString(keysize), jceParams, nativeParams); + } + @ParameterizedTest - @ValueSource(ints = {192, 224, 256, 384, 521}) + @ValueSource(ints = {256, 384, 521}) public void knownSizes(int keysize) throws GeneralSecurityException { TestUtil.assumeMinimumVersion("1.2.0", nativeGen.getProvider()); nativeGen.initialize(keysize); @@ -206,6 +244,7 @@ public void unknownSize() throws GeneralSecurityException { } @Test + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) public void explicitCurve() throws GeneralSecurityException { jceGen.initialize(EXPLICIT_CURVE); KeyPair jcePair = jceGen.generateKeyPair(); @@ -237,6 +276,7 @@ public void unknownCurveValidNid() throws GeneralSecurityException { } @Test + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) public void validBinaryCurve() throws GeneralSecurityException { final String name = "sect113r1"; ECGenParameterSpec spec = new ECGenParameterSpec(name); @@ -269,6 +309,28 @@ public void ecdsaValidation() throws GeneralSecurityException { } } + @Test + @EnabledForJreRange(min=JRE.JAVA_8, max=JRE.JAVA_14) + public void legacyEcdsaValidation() throws GeneralSecurityException { + final byte[] message = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + // We're purposefully using Java's ECDSA logic, since we trust it to be correct + final Signature ecdsa = Signature.getInstance("NONEwithECDSA", "SunEC"); + for (final String[] names : LEGACY_CURVES) { + for (final String name : names) { + nativeGen.initialize(new ECGenParameterSpec(name)); + final KeyPair keyPair = nativeGen.generateKeyPair(); + + ecdsa.initSign(keyPair.getPrivate()); + ecdsa.update(message); + final byte[] signature = ecdsa.sign(); + + ecdsa.initVerify(keyPair.getPublic()); + ecdsa.update(message); + assertTrue(ecdsa.verify(signature), name); + } + } + } + @Test public void defaultParams() throws GeneralSecurityException { nativeGen.generateKeyPair(); diff --git a/tst/com/amazon/corretto/crypto/provider/test/integration/ExternalHTTPSIntegrationTest.java b/tst/com/amazon/corretto/crypto/provider/test/integration/ExternalHTTPSIntegrationTest.java index f4299567..17235cf7 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/integration/ExternalHTTPSIntegrationTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/integration/ExternalHTTPSIntegrationTest.java @@ -11,9 +11,13 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; +import javax.security.auth.x500.X500Principal; import java.net.URL; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; import java.security.Security; import java.util.ArrayList; +import java.util.Date; import com.amazon.corretto.crypto.provider.AmazonCorrettoCryptoProvider; import com.amazon.corretto.crypto.provider.test.TestResultLogger; @@ -81,7 +85,24 @@ public void testHTTPSConnectivity(boolean useBouncyCastle, String urlStr) throws // http://bouncy-castle.1462172.n4.nabble.com/BC-1-54-quot-breaks-quot-SunJSSE-PKCS12-keystore-PBE-td4658064.html // As such we'll set up our trust manager (and load the CA certs) before we install BC - CustomTrustManager customTrustManager = new CustomTrustManager(); + final CustomTrustManager customTrustManager = new CustomTrustManager() { + @Override + public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { + try { + super.checkServerTrusted(chain, authType); + } catch (final CertificateException e) { + final Date now = new Date(); + for (X509Certificate cert: chain) { + final Date notAfter = cert.getNotAfter(); + final String subjectName = cert.getSubjectX500Principal().getName(X500Principal.RFC2253); + if (notAfter.before(now) && (subjectName.contains(".badssl.com,") || subjectName.endsWith(".badssl.com"))) { + return; + } + } + throw e; + } + } + }; Security.insertProviderAt(AmazonCorrettoCryptoProvider.INSTANCE, 1); if (useBouncyCastle) {