public void DoTest13() { BigInteger modulus = new BigInteger(1, Hex.Decode("CDCBDABBF93BE8E8294E32B055256BBD0397735189BF75816341BB0D488D05D627991221DF7D59835C76A4BB4808ADEEB779E7794504E956ADC2A661B46904CDC71337DD29DDDD454124EF79CFDD7BC2C21952573CEFBA485CC38C6BD2428809B5A31A898A6B5648CAA4ED678D9743B589134B7187478996300EDBA16271A861")); BigInteger pubExp = new BigInteger(1, Hex.Decode("010001")); BigInteger privExp = new BigInteger(1, Hex.Decode("4BA6432AD42C74AA5AFCB6DF60FD57846CBC909489994ABD9C59FE439CC6D23D6DE2F3EA65B8335E796FD7904CA37C248367997257AFBD82B26F1A30525C447A236C65E6ADE43ECAAF7283584B2570FA07B340D9C9380D88EAACFFAEEFE7F472DBC9735C3FF3A3211E8A6BBFD94456B6A33C17A2C4EC18CE6335150548ED126D")); RsaKeyParameters pubParams = new RsaKeyParameters(false, modulus, pubExp); RsaKeyParameters privParams = new RsaKeyParameters(true, modulus, privExp); IAsymmetricBlockCipher rsaEngine = new RsaBlindedEngine(); IDigest digest = new Sha256Digest(); // set challenge to all zero's for verification byte[] challenge = new byte[8]; // DOES NOT USE FINAL BOOLEAN TO INDICATE RECOVERY Iso9796d2Signer signer = new Iso9796d2Signer(rsaEngine, digest, false); // sign signer.Init(true, privParams); signer.BlockUpdate(challenge, 0, challenge.Length); byte[] sig = signer.GenerateSignature(); // verify signer.Init(false, pubParams); signer.BlockUpdate(challenge, 0, challenge.Length); if (!signer.VerifySignature(sig)) { Fail("basic verification failed"); } // === LETS ACTUALLY DO SOME RECOVERY, USING INPUT FROM INTERNAL AUTHENTICATE === signer.Reset(); string args0 = "482E20D1EDDED34359C38F5E7C01203F9D6B2641CDCA5C404D49ADAEDE034C7481D781D043722587761C90468DE69C6585A1E8B9C322F90E1B580EEDAB3F6007D0C366CF92B4DB8B41C8314929DCE2BE889C0129123484D2FD3D12763D2EBFD12AC8E51D7061AFCA1A53DEDEC7B9A617472A78C952CCC72467AE008E5F132994"; digest = new Sha1Digest(); signer = new Iso9796d2Signer(rsaEngine, digest, true); signer.Init(false, pubParams); byte[] signature = Hex.Decode(args0); signer.UpdateWithRecoveredMessage(signature); signer.BlockUpdate(challenge, 0, challenge.Length); if (!signer.VerifySignature(signature)) { Fail("recovered + challenge signature failed"); } // === FINALLY, USING SHA-256 === signer.Reset(); digest = new Sha256Digest(); // NOTE setting implicit to false does not actually do anything for verification !!! signer = new Iso9796d2Signer(rsaEngine, digest, false); signer.Init(true, privParams); // generate NONCE of correct length using some inner knowledge int nonceLength = modulus.BitLength / 8 - 1 - digest.GetDigestSize() - 2; byte[] nonce = new byte[nonceLength]; SecureRandom rnd = new SecureRandom(); rnd.NextBytes(nonce); signer.BlockUpdate(nonce, 0, nonce.Length); signer.BlockUpdate(challenge, 0, challenge.Length); byte[] sig3 = signer.GenerateSignature(); signer.Init(false, pubParams); signer.UpdateWithRecoveredMessage(sig3); signer.BlockUpdate(challenge, 0, challenge.Length); if (signer.VerifySignature(sig3)) { if (signer.HasFullMessage()) { Fail("signer indicates full message"); } byte[] recoverableMessage = signer.GetRecoveredMessage(); // sanity check, normally the nonce is ignored in eMRTD specs (PKI Technical Report) if (!Arrays.AreEqual(nonce, recoverableMessage)) { Fail("Nonce compare with recoverable part of message failed"); } } else { Fail("recoverable + nonce failed."); } }