/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.security.wycheproof; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.math.BigInteger; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.MessageDigest; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests PKCS #1 v 1.5 signatures */ // TODO(bleichen): // - document stuff // - Join other RSA tests @RunWith(JUnit4.class) public class RsaSignatureTest { static final RSAPublicKeySpec RSA_KEY1 = new RSAPublicKeySpec( new BigInteger( "ab9014dc47d44b6d260fc1fef9ab022042fd9566e9d7b60c54100cb6e1d4edc9" + "8590467d0502c17fce69d00ac5efb40b2cb167d8a44ab93d73c4d0f109fb5a26" + "c2f8823236ff517cf84412e173679cfae42e043b6fec81f9d984b562517e6feb" + "e1f72295dbc3fdfc19d3240aa75515563f31dad83563f3a315acf9a0b351a23f", 16), new BigInteger("65537")); static final String ALGORITHM_KEY1 = "SHA256WithRSA"; /** * Test signatures for RSA_KEY1 and MESSAGE = "Test". The first signature is valid. All other * signatures are invalid. The signatures were generated by modifying the PKCS #1 padding in * various ways. I.e. while the generation of the false signature did require the private RSA key, * failing the test often is a sign that signatures can be forged. Such forgeries are much too * frequent. The following list is just an incomplete selection of past vulnerabilities: * *
Currently we check for just one type of legacy signatures: i.e., a missing NULL parameter in * the ASN encoding of the hash. BouncyCastle and the SunJCE accept this signature, Conscrypt does * not. * *
Some references that support accepting this signature: * https://codereview.chromium.org/1690123002/ * https://groups.google.com/a/chromium.org/forum/#!topic/chromium-reviews/Jo5S7HtEABI claims that * 7% of the responses in the Online Certificate Status Protocol (OCSP) miss the NULL parameter */ @Test public void testLegacySignatures() throws Exception { RSAPublicKeySpec key = RSA_KEY1; String algorithm = ALGORITHM_KEY1; byte[] message = "Test".getBytes("UTF-8"); Signature verifier = Signature.getInstance(algorithm); KeyFactory kf = KeyFactory.getInstance("RSA"); PublicKey pub = kf.generatePublic(key); for (String signature : LEGACY_SIGNATURES_KEY1) { byte[] signatureBytes = TestUtil.hexToBytes(signature); verifier.initVerify(pub); verifier.update(message); boolean verified = false; try { verified = verifier.verify(signatureBytes); } catch (SignatureException ex) { verified = false; } if (verified) { System.out.println("Verfied legacy signature:" + signature); } else { System.out.println("Rejected legacy signature:" + signature); } } } /** * Faults during the generation of a signature can leak the information about the private key. * A. K. Lenstra showed in "Memo on RSA signature generation in the presence of faults", * (https://infoscience.epfl.ch/record/164524/files/nscan20.PDF) that PKCS #1 signatures are * especially susceptible to faults when the Chinese Remainder Theorem is used to compute the * signature: one single faulty signature is sufficient to leak the private key. * * One countermeasure that is often used in libraries is to blind the RSA computation and * verify the signature before returning it. Nowadays, libraries are expected to have at least * some countermeasures against faulty computations. In some cases (e.g. OpenSSL) the library * tries to fix a faulty computation by generating a correct signature without using Chinese * remaindering. * * The test here does not induce a fault. Instead it tries to sign with a faulty private key. * The expected outcome of the test is that underlying provider either detects that the fault * or generates a valid signature by ignoring the faulty CRT parameter. * * Since the test only simulates a fault, but does not actually induce a fault it is somewhat * incomplete. It does not detect all vulnerable implementations. The test should nonetheless * detect implementations that include no verification at all. */ @Test public void testFaultySigner() throws Exception { BigInteger e = new BigInteger("65537"); BigInteger d = new BigInteger( "1491581187972832788084570222215155297353839087630599492610691218" + "6098027383804966741416365668088258821394558334495197493887270311" + "7558637148793177374456685063919969705672268324029058661801838398" + "1099187046803818325657704350675941092582695993374867459573741707" + "2513551423973482044545986645893321692393572214394692273248819124" + "5866638922766330300631727125395012955305761836925591665625409882" + "5987442083465656021724458811783361811914866856391248003733867121" + "5531501554906114868306919889638573670925006068497222709802245970" + "0014474779292382225845722344584808716054088377124806520166137504" + "58797849822813881641713404303944154638273"); BigInteger q = new BigInteger( "1327930250247153291239240833779228146841620599139480980326615632" + "6868823273498280322301518048955331731683358443542450740927959439" + "3056349447047388914345605165927201322192706870545643991584573901" + "9099563807204264522234257863225478717589651408831271029849307682" + "13198832542217762257092135384802889866043941823057701"); BigInteger p = new BigInteger( "1546732137638443281784728718025150988901748595222448633054370906" + "7724307988669542799529278238746541544956234718616481585427107180" + "6134464028933334724614223213582911567222033332353858049787180486" + "8311341830570208335451999930773903649599388066890163502238099141" + "76306676019969635213034585825883528127235874684082417"); BigInteger n = p.multiply(q); BigInteger dp = d.mod(p.subtract(BigInteger.ONE)); BigInteger dq = d.mod(q.subtract(BigInteger.ONE)); BigInteger crt = q.modInverse(p); RSAPrivateCrtKeySpec validKey = new RSAPrivateCrtKeySpec(n, e, d, p, q, dp, dq, crt); RSAPrivateCrtKeySpec invalidKey = new RSAPrivateCrtKeySpec(n, e, d, p, q, dp.add(BigInteger.valueOf(2)), dq, crt); byte[] message = "Test".getBytes("UTF-8"); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey validPrivKey = kf.generatePrivate(validKey); Signature signer = Signature.getInstance("SHA256WithRSA"); signer.initSign(validPrivKey); signer.update(message); byte[] signature = signer.sign(); PrivateKey invalidPrivKey = null; try { invalidPrivKey = kf.generatePrivate(invalidKey); } catch (InvalidKeySpecException ex) { // The provider checks the private key and notices a mismatch. // This is a good sign, though of course in this case it means that we can't // check for faults. System.out.println("Provider catches invalid RSA key:" + ex); return; } byte[] invalidSignature = null; try { signer.initSign(invalidPrivKey); signer.update(message); invalidSignature = signer.sign(); } catch (Exception ex) { // We do not necessarily expect a checked exception here, since generating // an invalid signature typically indicates a programming error. // Though RuntimeExceptions are fine here. System.out.println("Generating PKCS#1 signature with faulty key throws:" + ex); return; } String signatureHex = TestUtil.bytesToHex(signature); String invalidSignatureHex = TestUtil.bytesToHex(invalidSignature); if (signatureHex.equals(invalidSignatureHex)) { // The provider generated a correct signature. This can for example happen if the provider // does not use the CRT parameters. System.out.println("Signature generation did not use faulty parameter"); return; } fail("Generated faulty PKCS #1 signature with faulty parameters" + " valid signature:" + signatureHex + " invalid signature:" + invalidSignatureHex); } }