blob: f5d024ae0e9fe8af3fc6138f1481e57755697e83 [file] [log] [blame]
/*
* Copyright 2015 The Android Open Source Project
*
* 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 android.keystore.cts;
import com.android.cts.keystore.R;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Provider.Service;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;
public class SignatureTest extends AndroidTestCase {
private static final String EXPECTED_PROVIDER_NAME = "AndroidKeyStoreBCWorkaround";
private static final String[] EXPECTED_SIGNATURE_ALGORITHMS = {
"NONEwithRSA",
"MD5withRSA",
"SHA1withRSA",
"SHA224withRSA",
"SHA256withRSA",
"SHA384withRSA",
"SHA512withRSA",
"SHA1withRSA/PSS",
"SHA224withRSA/PSS",
"SHA256withRSA/PSS",
"SHA384withRSA/PSS",
"SHA512withRSA/PSS",
"NONEwithECDSA",
"SHA1withECDSA",
"SHA224withECDSA",
"SHA256withECDSA",
"SHA384withECDSA",
"SHA512withECDSA"
};
private static final Map<String, String> SIG_ALG_TO_CANONICAL_NAME_CASE_INSENSITIVE =
new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
static {
// For an unknown legacy reason, libcore's ProviderTest#test_Provider_getServices insists
// that a Service with algorithm "ECDSA" be exposed, despite the RI not exposing any such
// services. Thus, our provider has to expose the "ECDSA" service whose actual proper
// name is SHA1withECDSA.
SIG_ALG_TO_CANONICAL_NAME_CASE_INSENSITIVE.put("ECDSA", "SHA1withECDSA");
}
private static final byte[] SHORT_MSG_KAT_MESSAGE =
HexEncoding.decode("ec174729c4f5c570ba0de4c424cdcbf0362a7718039464");
private static final byte[] LONG_MSG_KAT_SEED = SHORT_MSG_KAT_MESSAGE;
private static final int LONG_MSG_KAT_SIZE_BYTES = 3 * 1024 * 1024 + 123;
private static final Map<String, byte[]> SHORT_MSG_KAT_SIGNATURES =
new TreeMap<String, byte[]>(String.CASE_INSENSITIVE_ORDER);
static {
// From RI
SHORT_MSG_KAT_SIGNATURES.put("NONEwithECDSA", HexEncoding.decode(
"304402201ea57c2fb571991639d103bfec658ee7f359b60664e400a5834cfc20d28b588902202433f5"
+ "eb07d2b03bf8d238ea256ea399d0913a6cfcae2c3b00e2efd50aebc967"));
SHORT_MSG_KAT_SIGNATURES.put("SHA1withECDSA", HexEncoding.decode(
"30440220742d71a013564ab196789322b9231ac5ff26460c2d6b1ab8ccb45eec254cc8ba0220780a86"
+ "5ddc2334fae23d563e3142b04660c2ab1b875c4ff8c557a1d1accc43e1"));
SHORT_MSG_KAT_SIGNATURES.put("SHA224withECDSA", HexEncoding.decode(
"304502200f74966078b34317daa69e487c3163dbb4e0391cd74191cc3e95b33fc60966e3022100ebdc"
+ "be19c516d550609f73fb37557a406e397bc1725a1baba50cdfc3537bd377"));
SHORT_MSG_KAT_SIGNATURES.put("SHA256withECDSA", HexEncoding.decode(
"304402204443b560d888beeae729155b0d9410fef2ec78607d9166af6144346fba8ce45d02205b0727"
+ "bfa630050f1395c8bcf46c614c14eb15f2a6abbd3bc7c0e83b41819281"));
SHORT_MSG_KAT_SIGNATURES.put("SHA384withECDSA", HexEncoding.decode(
"3045022025ade03446ce95aa525a51aedd16baf12a2b8b9c1f4c87224c38e48c84cbbbf8022100ad21"
+ "8424c3671bc1513e1da7e7186dbc6bf67bec434c95863a48e79f53684971"));
SHORT_MSG_KAT_SIGNATURES.put("SHA512withECDSA", HexEncoding.decode(
"3045022100969e8fed2dc4ddcdf341368e057efe4e3a00eda66bbb127dec31bb0144c5334602201087"
+ "2b7f9ab9c06a07053e0641e6adc18a87a1d7807550a19e872e78e5c7f0dd"));
// From RI
SHORT_MSG_KAT_SIGNATURES.put("NONEwithRSA", HexEncoding.decode(
"257d0704e514ead29a5c45576adb2d5a7d7738e6a83b5d6463a5306788015d14580fee340e78e00d39"
+ "f56ae616083ac929e5daf9eeab40b908eb05d0cd1036d9e92799587df0d4c5304c9b27f913e1c891"
+ "919eff0df3b5d9c0d8cc4cd843795840799cc0353192c3868b3f8cad96d04bb566ca53e1146aa2a3"
+ "4b890ce917680bbdea1dee417a89630224d2ee79d66d38c7c77e50b45e1dd1b8b63eb98ecd60426b"
+ "c9fb30917e78ae4dd7cbfa9475f9be53bf45e7032add52681553679252f4f74de77831c95ea69f30"
+ "2f0fd28867de058728455e3537680c86a001236e70c7680c78b4dc98942d233b968635a0debccb41"
+ "fbc17ece7172631f5ab6d578d70eb0"));
SHORT_MSG_KAT_SIGNATURES.put("MD5withRSA", HexEncoding.decode(
"1e1edefc9a6a4e61fcef0d4b202cc2b53ab9043b1a0b21117d122d4c5399182998ec66608e1ab13513"
+ "08fbb23f92d5d970f7fb1a0691f8d1e682ff4f5e394ef2dfcbdc2de5c2c33372aec9a0c7fba982c5"
+ "c0f1a5f65677d9294d54a2e613cc0e5feb919135e883827da0e1c222bf31336afa63a837c57c0b70"
+ "70ceb8a24492a42afa6750cc9fe3a9586aa15507a65410db973a4b26734624d323dc700928717789"
+ "fb1f970d57326eef49e012fcebcfbbfd18fb4d6feff03587d0f2b0a556fe467437a44a2283ea5027"
+ "6efda4fd427365687960e0c289c5853a7b300ff081511a2f52899e6f6a569b30e07adfd52e9d9d7e"
+ "ab33999da0da1dd16d4e88bd980fcd"));
SHORT_MSG_KAT_SIGNATURES.put("SHA1withRSA", HexEncoding.decode(
"280977b4ee18cbe27d3e452c9b90174f5d81dd518018ce52ff1cd1e8d4d0626afca85be14a43fa3b76"
+ "a80e818b4bc10abc62180fa15619d78be98ccd8fa642ea05355aa84f2924e041c2b594b1cf31d42b"
+ "f11c78dd3cbb6cc2cbfe151792985e6e5cf73c2e600a38f31e26e84c3e4a434f67a625fefe712d17"
+ "b34125ea91d333cfe1c4ac914b6c411b08e64700885325e07510c4f49ef3648252736f17d3ae7705"
+ "0054ceb07ab04b5ecaa5fc4556328bad4f97eba37f9bf079506e0eb136c9eadf9e466ccb18d65b4d"
+ "ef2ba3ca5c2f33354a2040dfb646423f96ba43d1d8f3dcf91c0c2c14e7958159d2ac3ebc0b6b87e6"
+ "6efbdda046ce8a766fecc3f905d6ff"));
SHORT_MSG_KAT_SIGNATURES.put("SHA224withRSA", HexEncoding.decode(
"490af9e685ef44da9528d9271d00e09a3e688012bf3f63fd924a06cb4db747a28cdf924c2d51620165"
+ "33985abf4b91d64c17ff7e2b4f0de5a28375dddf556cd9e5dcebd112f766f07cb867e8d5710ce79a"
+ "1c3d5244cbd16618b0fedc2b9015d51a98d453747fb320b97995ea9579adbc8bf6042b2f4252cef1"
+ "787207fefaf4b9c7212fe0ff8b22ae12ffc888f0a1e6923455577e82b58608dabc2acba05be693ff"
+ "ae7da263d6c83cb13d59a083f177578d11030f8974bdb301f6135ecd5ec18dd68dc453c5963e94b6"
+ "2d89bcda0ff63ac7394030f79b59139e1d51573f0d4a1e85d22502c9b0d29412f7eb277fb42fa4db"
+ "18875baffa7719b700e4830edbcd6f"));
SHORT_MSG_KAT_SIGNATURES.put("SHA256withRSA", HexEncoding.decode(
"1f1adcd6edbf4c50777c932db6e99a577853fbc9c71c692e921291c5aa73eb0155e30c8d4f3aff828f"
+ "2040c84e10b1ba729ccc23899650451022fcd3574df5454b01112adec5f01565b578bbc7c32810c9"
+ "407106054ad8f4f640b589ddef264d028ad906536d61c8053ef0dba8e10ca2e30a9dd6ccc9a9ec3e"
+ "76c10d36029820865b2d01095987af4a29369ffc6f70fa7e1de2b8e28f41894df4225cf966454096"
+ "7fb7ecff443948c8a0ee6a1be51e0f8e8887ff512dbdc4fc81636e69ae698000ce3899c2ec999b68"
+ "691adfb53092380264b27d91bd64561fee9d2e622919cf6b472fd764dc2065ae6a67df2c7b5ec855"
+ "099bdb6bb104ee41fa129c9da99745"));
SHORT_MSG_KAT_SIGNATURES.put("SHA384withRSA", HexEncoding.decode(
"3b5e8baa62803569642fa8c3255249709c9f1d69bd31f7b531d5071c07cd9bac29273097666d96b2e3"
+ "2db13529b6414be5aee0c8a90c3f3b2a5c815f37fac16a3527fa45903f847416ed218eee2fef5b87"
+ "5f0c97576f58b3467e83497c1cdeea44d0ea151e9c4d27b85eef75d612b1fc16731859738e95bdf1"
+ "2f2098ebd501d8493c66585e8545fb13d736f7dbbb530fb06f6f157cd10c332ca498b379336efdaf"
+ "a8f940552da2dbb047c33e87f699068eaadd6d47c92a299f35483ba3ae09f7e52a205f202c1af997"
+ "c9bdc40f423b3767292c7fcea3eaf338a76f9db07a53d7f8d084bf1ceac38ec0509e442b1283cd8f"
+ "013c4c7a9189fe4ef9ab00c80cb470"));
SHORT_MSG_KAT_SIGNATURES.put("SHA512withRSA", HexEncoding.decode(
"23eb7577d2ffefed10780c2a26f79f64abe08203e900db2333413f30bbc81f800857857c8b0e02c3e8"
+ "8fe3cf5514130d6216ef7c4a86b1012594c7cb07a293159b92bf40291224386a84e607e0a8389c8a"
+ "a0c45cc553037517c52f61fe0ea51dba184e890db7d9517760724c038018330c0a9450c280430e6f"
+ "9e4cdd4545c3f6684485cd6e27203735ff4be76420071920b18c54d98c0e3eb7ae7d1f01f5171ace"
+ "87885c6185f66d947d51a441a756bc953458f7d3a1714226899562478ebf91ab18d8e7556a966661"
+ "31de37bc2e399b366877f53c1d88f93c989aeb64a43f0f6cbc2a29587230f7e3e90ea18868d79584"
+ "3e62b49f5df78e355b437ec2f882f9"));
// From Bouncy Castle
SHORT_MSG_KAT_SIGNATURES.put("SHA1withRSA/PSS", HexEncoding.decode(
"17e483781695067a25bc7cb204429a8754af36032038460e1938c28cd058025b14d2cffe5d3da39e76"
+ "6542014e5419f1d4c4d7d8e3ebcd2221dde04d24bbbad657f6782b7a0fada3c3ea595bc21054b0ab"
+ "d1eb1ada86276ed31dbcce58be7407cbbb924d595fbf44f2bb6e3eab92296076e291439107e67912"
+ "b4fac3a27ff84af7cd2db1385a8340b2e49c7c2ec96a6b657a1641da80799cb88734cca35a2b3a2c"
+ "4af832a34ac8d3134ccc8b61150dc1b64391888a3a84bdb5184b48e8509e8ba726ba8847e4ca0640"
+ "ce615e3adf5248ce08adb6484f6f29caf6c65308ec6351d97369ae005a7c762f76f0ddc0becc3e45"
+ "529aa9c8391473e392c9a60c2d0834"));
SHORT_MSG_KAT_SIGNATURES.put("SHA224withRSA/PSS", HexEncoding.decode(
"3b7641a49e7766ed879f2b0e33ceb3d935678a7deffbd855a97abf00a65c981814ac54a71150b2ffea"
+ "d5db83aa96d0939267b3c5e2fcf958e9c6fdf7d90908e6139f7f330a16dc625d8268ffd324659f6c"
+ "e36798ef3b71a92f1d2237e3ce1e281aacc1d5370ae63c9b75e7134ad15cca1410746bf259ac4519"
+ "c407877503900ec8f3b71edce727e9d0275c9cd48385f89ce76ed17a2bf246578f183bb6577d6942"
+ "2056c7d9145528fc8ca036926a9fafe819f37c1a4a0a69b17f3d4b0a116106f94a5d2a5f8ce6981c"
+ "d6e5c2f858dcb0823e725fffe6be14ca882c81fa993bebda549fcf983eb7f8a87eccd545951dcdc9"
+ "d8055ae4f4067de997cfd89952c905"));
SHORT_MSG_KAT_SIGNATURES.put("SHA256withRSA/PSS", HexEncoding.decode(
"6f0f744fa8e813b4c7caa0c395c1aa8aee0d61e621b4daae305c759b5b5972311ad691f8867821efba"
+ "d57995cc8ff38f33393293e94e1c484e94de4816b0fd986f5710a02d80e62461cc6f87f1f3742268"
+ "c28a54870f290d136aa629cbe00a1bf243fab1674c04cd5910a786b2ac5e71d9c6f4c41daa4c584d"
+ "46ba7ee768d2d2559be587a7b2009f3b7497d556a0da8a8ae80ce91152c81ffba62720d36b699d1f"
+ "157137ff7ee7239fc4baf611d01582346e201900f7a4f2617cdf574653e124fb895c6cb76d4ed5a8"
+ "aca97d1e408e8011eba649d5617bae8b27c1b946dcff7b29151d8632ad128f22907e8b83b9149e16"
+ "fbb9e9b87600a2f90c1fd6dc164c52"));
SHORT_MSG_KAT_SIGNATURES.put("SHA384withRSA/PSS", HexEncoding.decode(
"8e57992362ad4b0487a707b2f8811d953f5aaf800978859981e7dcddad6f9f411fb162859115577c53"
+ "7a3524e26bf069508185848d6e29e7da1f9660a49771533e43853e02232314afd2928a1ff1824345"
+ "a5a90309a59d213ff6a4d04520f95a976342e6ac529ec6a6821157f4fee3bdae30d836d3ab44386d"
+ "3914e6aacd6a6a63e1d63b4d9bfb93b343b6c1f28d60042ffbe1e46fb692a381456e84b3328dbcae"
+ "ed6fc577cb1c5f86a38c5c34d439eeee7e798edc9f2bcd4fc217b1630e45b8df67def2c2cdb9fea0"
+ "5d67aa6cce6e9a72e9a114e2e620a54c05755e32685ffc7e50487c3cd00888c09492fad8c461c338"
+ "e7d099b275deaf184b7d6689385f7c"));
SHORT_MSG_KAT_SIGNATURES.put("SHA512withRSA/PSS", HexEncoding.decode(
"7a40f9f2797beda0702df0520c7138269295a0f0328aab4eba123ebf178ea4abc745ed42d3b175dc70"
+ "c8dcc98f46f2234b392dbb3e939f30888715c4fbb47fbb5bb7c0557c140c579f48226710e5b3da0d"
+ "9511337cde5626df586b4004100dd45490e5f8ae23307b5d1054c97e9ef58f9c385ca55b6db4f58d"
+ "2e19bc8ca9d8c2b4922fb3325b6fb61fc40a359e9196aa9388845b136d2790d71410e20371dcf0a7"
+ "0425ee1854c5c3d7de976b28de0ee9b1048ed99b2a957edc97466cc4c87e36224fd323605228f61a"
+ "1aad30253b0698f9a358491138027d325d46bdfdf72171c57a2dab0a9cddaad8e170b8275c172e42"
+ "33b29ed81c0f4de9fe9f0670106aad"));
}
private static final Map<String, byte[]> LONG_MSG_KAT_SIGNATURES =
new TreeMap<String, byte[]>(String.CASE_INSENSITIVE_ORDER);
static {
// From RI
LONG_MSG_KAT_SIGNATURES.put("SHA1withECDSA", HexEncoding.decode(
"3044022075f09bb5c87d883c088ca2ad263bbe1754ab614f727465bc43695d3521eaccf80220460e4e"
+ "32421e6f4398cd9b7fbb31a1d1f2961f26b9783620f6413f0e6f7efb84"));
LONG_MSG_KAT_SIGNATURES.put("SHA224withECDSA", HexEncoding.decode(
"3045022100d6b24250b7d3cbd329913705f4990cfd1000f338f7332a44f07d7731bd8e1ff602200565"
+ "0951e14d0d21c4344a449843ef65ac3a3f831dc7f304c0fa068c996f7d34"));
LONG_MSG_KAT_SIGNATURES.put("SHA256withECDSA", HexEncoding.decode(
"30440220501946a2c373e8da19b36e3c7718e3f2f2f16395d5026ac4fbbc7b2d53f9f21a0220347d7a"
+ "46685282f308bacd5fb25ae92b351228ea39082784789696580f27eed1"));
LONG_MSG_KAT_SIGNATURES.put("SHA384withECDSA", HexEncoding.decode(
"30450220576836de4ab94a869e867b2360a71dc5a0b3351ea1c896b163206db7c3507dc2022100c1a6"
+ "719052a175e023bca7f3b9bb7a379fc6b51864cb28a195076d2f3c79ed2e"));
LONG_MSG_KAT_SIGNATURES.put("SHA512withECDSA", HexEncoding.decode(
"304402204ca46bac4e43e8694d1af38854c96024a4e9bcc55c6904c1f8fea0d1927f69f7022054662e"
+ "84b4d16b9f7e8164f4896212dec3c7c1e7fd108f69b0dff5bc15399eeb"));
// From RI
LONG_MSG_KAT_SIGNATURES.put("MD5withRSA", HexEncoding.decode(
"7040f3e0d95f4d22719d26e5e684dbcd5ed52ab4a7c5aa51b938b2c060c79eb600f9c9771c2fcda7e3"
+ "55e7c7b5e2ba9fe9a2a3621881c0fe51702781ffcde6ce7013218c04bb05988346c2bed99afb97a8"
+ "113fb50697adf93791c9129e938040f91178e35d6f323cfa515ea6d2112e8cce1302201b51333794"
+ "4a5c425cecc8181842ace89163d84784599ea688060ad0d61ac92b673feabe01ae5e6b85d8b5e8f0"
+ "519aea3c29781e82df9153404d027d75df8370658898ed348acf4e13fd8f79c8a545881fbbf585e1"
+ "c666be3805e808819e2cc730379f35a207f9e0e646c7ab6d598c75b1901f0c5ca7099e34f7f01579"
+ "3b57dfb5c2a32e8423bfed6215f9d0"));
LONG_MSG_KAT_SIGNATURES.put("SHA1withRSA", HexEncoding.decode(
"187d7689206c9dd03861009c6cb62c7752fd2bbc354f0bea4e76059fe582744c80027175112a3df4b6"
+ "3b4a5626ed3051192e3c9b6d906497472f6df81171064b59114ff5d7c60f66943549634461cfadd8"
+ "a033cba2b8781fb7936ea1ca0043da119856a21e533afa999f095cf87604bb33a14e8f82fab01998"
+ "9ef3133e8069708670645ddd5cdc86bbe19fbf672b409fb6d7cae2f913814cd3dc8d5ae8e4037ccf"
+ "4a3ef97db8c8a08516716258c4b767607c51dfb289d90af014d3cfc64dbadb2135ed59728b78fda0"
+ "823fe7e68e84280c283d21ab660364a9bf035afa9a7262bade87057a63aa1d7e2c09bb9dd037bcbd"
+ "7b98356793bc32be81623833c6ab62"));
LONG_MSG_KAT_SIGNATURES.put("SHA224withRSA", HexEncoding.decode(
"31ff68ddfafcf3ff6e651c93649bf9cc06f4138493317606d8676a8676f9d9f3a1d5e418358f79d143"
+ "a922a3cfc5e1ad6765dc829b556c9019a6d9389144cc6a7571011c024c0514891970508dac5f0d26"
+ "f26b536cf3e4511b5e72cd9f60590b387d8a351a9f28839a1c5be5272cb75a9062aa313f3d095074"
+ "6d0a21d4f8c9a94d3bb4715c3ef0207cf1335653161a8f78972329f3ec2fa5cfe05318221cb1f535"
+ "8151dde5410f6c36f32287a2d5e76bf36134d7103fc6810a1bb8627de37d6b9efde347242d08b0b6"
+ "2b1d73bacd243ccc8546536080b42a82b7162afeb4151315746a14b64e45226c9f5b35cf1577fc6b"
+ "f5c882b71deb7f0e375db5c0196446"));
LONG_MSG_KAT_SIGNATURES.put("SHA256withRSA", HexEncoding.decode(
"529c70877dedf3eb1abda98a2f2b0fc899e1edece70da79f8f8bbceb98de5c85263bef2ef8a7322624"
+ "5ed2767045ea6965f35cb53e6ac1d6c62e8007a79962507d3e01c77d4e96674344438519adae67d9"
+ "6357da5c4527969c939fd86f3b8685338c2be4bf6f1f85527b11fcaa4708f925e8bb9b877bda179b"
+ "d1b45153ef22834cf593ecc5b6eca3deddbe5d05894e4e5707d71bc35ea879ccb6e8ffc32e0cdc5e"
+ "88a30eef7a608d9ea80b5cefec2aa493a3b1354ad20e88ab1f8bfda3bd9961e10f0736d1bc090d57"
+ "b93fbce3e6e2fc99e67c7b466188d1615b4150c206472e48a9253b7549cebf6c7cbb558b54e10b73"
+ "c8b1747c18d1890a24d0a835ee710a"));
LONG_MSG_KAT_SIGNATURES.put("SHA384withRSA", HexEncoding.decode(
"5dd3553bc594c541937dac9a8ac119407712da7564816bcdc0ca4e14bc6059b9f9bd72e99be8a3df3e"
+ "0a3c4e8ed643db9ed528b43a396dba470ad3307815bd7c75fa5b08775a378cc4203341379087dcb3"
+ "62a5e9f5c979744e4498a6aafd1b1a8069caf4ef437f2743754861fcc96d67a0f1dd3397bb65ede3"
+ "18d2b3628eb2c3ec5db8a3e21fbbe2629f1030641e420963abc4da99e24dd497337c8149a52d97da"
+ "7176c0767d72e18f8c9a49e6808509837f719fd16ba27b19a2b32bd19b9b14818e0b9be81062be77"
+ "4fb1b3105a1528170822391915a5cd12b8e79aaab7943c34094da4c4f8d56f52177db953d3bf7846"
+ "f0e5f22f2311054a1daba4fec6b589"));
LONG_MSG_KAT_SIGNATURES.put("SHA512withRSA", HexEncoding.decode(
"971d6350337866fbcb48c49446c50cac1995b822cfec8f2a3e2c8206158a2ddfc8fc7b38f5174b3288"
+ "91489e7b379829bac5e48cd41e9713ea7e2dc9c61cf90d255387d31818d2f161ec5c3a977b4ce121"
+ "62fb11ede30d1e63c0fbba8a4094e6ad39e64176a033e7130bbed71a67ff1713b45f0bedeb1ee532"
+ "15690f169452c061cd7d15e71cc754a2f233f5647af8373d2b583e98e4242c0a0581e0ce2b22e15a"
+ "443b0ff23d516ed39664f8b8ab5ca98a44af500407941fae97f37cb1becbcbff453608cb94a176d9"
+ "e702947fff80bc8d1e9bcdef2b7bbe681e15327cee50a72649aed0d730df7b3c9c31b165416d9c9f"
+ "1fcb04edbf96514f5758b9e90ebc0e"));
// From Bouncy Castle
LONG_MSG_KAT_SIGNATURES.put("SHA1withRSA/PSS", HexEncoding.decode(
"54a2050b22f6182b65d790da80ea16bfbc34b0c7e564d1a3ce4450e9b7785d9eaa14814dee8699977a"
+ "e8da5cfb3c55c9a623ca55abcc0ef4b3b515ce31d49a78db442f9db270d35a179baf71057fe8d6d2"
+ "d3f7e4fd5f5c80e11dc059c72a1a0373f527d88089c230525f895ee19e45f5547572083418c9e542"
+ "5ff44e407500d1c49159484f38e4b00523c2fa45b7b9b38a2c1ad676b36f02a06db6fca52bd79ba1"
+ "94d5062f5035a12a1f026ac216789844a5da0caa4d481386a12ca635c06b877515ce3782d9189d87"
+ "d1ff5ec6ec7c39437071c8db7d1c2702205da4cfb01805ca7fec5595dba2234602ca5347d30538cb"
+ "4b5286c151609afcca890a6276d5e8"));
LONG_MSG_KAT_SIGNATURES.put("SHA224withRSA/PSS", HexEncoding.decode(
"7e95c1e4f700ceaf9ce72fd3f9f245ba80f2e1341341c49521779c8a79004f9c534297441330b9df36"
+ "bb23467eb560e5e5538612cecc27953336a0d4d8044d5a80f6bcef5299830215258c574d271ea6cd"
+ "7117c2723189385435b0f06951ff3d6a700b23bc7ed3298cfb6aa65e8f540717d57f8b55290a4862"
+ "034d9f73e8d9cb6ae7fa55a8b4c127535b5690122d6405cb0c9a313808327cfd4fb763eae875acd1"
+ "b60e1920ecf1116102cc5f7d776ed88e666962f759258d6f5454c29cb99b8f9ccad07d209671b607"
+ "014d19009e392bfb08247acf7f354458dc51196d84b492798dd829b7300c7591d42c58f21bd2c3d1"
+ "e5ce0a0d3e0aa8aa4b090b6a619fc6"));
LONG_MSG_KAT_SIGNATURES.put("SHA256withRSA/PSS", HexEncoding.decode(
"5a8c0ae593a6714207b3ad83398b38b93da18cfda73139ea9f02c88a989368ae6901357194a873dd2e"
+ "8cd1bb86d1f81fbc8bf725538dc2ad60759f8caab6a98a6baa6014874a92d4b92ed72e73f2721ba2"
+ "86e545924860d27210b53f9308c4fec622fdfca7dd6d51a5b092184114e9dcf57636cdabaca17b49"
+ "70cd5e93ce12c30af6d09d6964c5ad173095ea000529620d94a25b4cc977deefd25cc810a7b11cd5"
+ "e5b71c9276b0bd33c53db01304b359a4a88f3fe8bc3335669f7609b0f6da17e49ad87f38468fa2c6"
+ "8134ba6df407207559355b6e486a745009931796ab0567c9bd61788073aa00113b324fa25bd32b4d"
+ "3521e98e0b4905c6dce30d70387a2e"));
LONG_MSG_KAT_SIGNATURES.put("SHA384withRSA/PSS", HexEncoding.decode(
"7913f13dc399adb07cd96c1bb484f999d047efcd96501c92477d2234a1da94db9c6fd65a8031bd3040"
+ "82d90ef4a4f388e670795d144ef72a160583d4a2c805415542fa16ffd8760d2f28bdc82f63db0900"
+ "a3554bc9175dafa1899249abd49591216ba2965a4862d0f59d8b8c8d1042ed7ac43a3a15650d578a"
+ "2ea53696e462f757b326b7f0f7610fb9934aee7d954a45ca03ef66464a5611433e1224d05f783cd1"
+ "935eff90015140cb35e15f2bbf491a0d6342ccef57e453f3462412c5ff4dfdc44527ea76c6b05b1d"
+ "1330869aec1b2f41e7d975eba6b056e7c2f75dd73b1eff6d853b9507f410279b02f9244b656a1aca"
+ "befcc5e1167df3a49c4a7d8479c30f"));
LONG_MSG_KAT_SIGNATURES.put("SHA512withRSA/PSS", HexEncoding.decode(
"43ffefe9c96014312679a2e3803eb7c58a2a4ab8bb659c12fec7fb574c82aed673e21ed86ac309cf6c"
+ "e567e47b7c6c83dcd72e3ee946067c2004689420528174d028e3d32b2b306bcbcb6a9c8e8b83918f"
+ "7415d792f9d6417769def3316ed61898443d3ffa4dc160e5b5ecf4a11a9dfed6b4a7aa65f0f2c653"
+ "4f7e514aed73be441609ffca29207b4ced249058543fd6e13a02ef42babe2cdf4aaba66b42e9d47f"
+ "c79b4ed54fbc28d9d732f2e468d43f0ca1de6fd5312fad2c4e3eaf3e9586bca6a8bab24b4dfab8b3"
+ "9a4057c8ed27024b61b425036bea5e23689cd9db2450be47ec2c30bb6707740c70a53b3e7a1c7ecf"
+ "f04e3de1460e60e9be7a42b1ddff0c"));
}
private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
public void testAlgorithmList() {
// Assert that Android Keystore Provider exposes exactly the expected signature algorithms.
// We don't care whether the algorithms are exposed via aliases, as long as the canonical
// names of algorithms are accepted.
// If the Provider exposes extraneous algorithms, it'll be caught because it'll have to
// expose at least one Service for such an algorithm, and this Service's algorithm will
// not be in the expected set.
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
Set<Service> services = provider.getServices();
Set<String> actualSigAlgsLowerCase = new HashSet<String>();
Set<String> expectedSigAlgsLowerCase = new HashSet<String>(
Arrays.asList(TestUtils.toLowerCase(EXPECTED_SIGNATURE_ALGORITHMS)));
for (Service service : services) {
if ("Signature".equalsIgnoreCase(service.getType())) {
String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
if (!expectedSigAlgsLowerCase.contains(algLowerCase)) {
// Unexpected algorithm -- check whether it's an alias for an expected one
String canonicalAlgorithm =
SIG_ALG_TO_CANONICAL_NAME_CASE_INSENSITIVE.get(algLowerCase);
if (canonicalAlgorithm != null) {
// Use the canonical name instead
algLowerCase = canonicalAlgorithm.toLowerCase();
}
}
actualSigAlgsLowerCase.add(algLowerCase);
}
}
TestUtils.assertContentsInAnyOrder(actualSigAlgsLowerCase,
expectedSigAlgsLowerCase.toArray(new String[0]));
}
public void testGeneratedSignatureVerifies() throws Exception {
Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
for (String sigAlgorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
try {
KeyPair keyPair = getKeyPairForSignatureAlgorithm(sigAlgorithm, keyPairs);
// Generate a signature
Signature signature = Signature.getInstance(sigAlgorithm);
signature.initSign(keyPair.getPrivate());
assertSame(provider, signature.getProvider());
byte[] message = "This is a test".getBytes("UTF-8");
signature.update(message);
byte[] sigBytes = signature.sign();
// Assert that it verifies using our own Provider
assertSignatureVerifiesOneShot(
sigAlgorithm, provider, keyPair.getPublic(), message, sigBytes);
// Assert that it verifies using whatever Provider is chosen by JCA by
// default for this signature algorithm and public key.
assertSignatureVerifiesOneShot(
sigAlgorithm, keyPair.getPublic(), message, sigBytes);
} catch (Exception e) {
throw new RuntimeException(sigAlgorithm + " failed", e);
}
}
}
public void testSmallMsgKat() throws Exception {
Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
byte[] message = SHORT_MSG_KAT_MESSAGE;
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
try {
byte[] goodSigBytes = SHORT_MSG_KAT_SIGNATURES.get(algorithm);
assertNotNull(goodSigBytes);
KeyPair keyPair = getKeyPairForSignatureAlgorithm(algorithm, keyPairs);
// Assert that AndroidKeyStore provider can verify the known good signature.
assertSignatureVerifiesOneShot(
algorithm, provider, keyPair.getPublic(), message, goodSigBytes);
assertSignatureVerifiesFedOneByteAtATime(
algorithm, provider, keyPair.getPublic(), message, goodSigBytes);
assertSignatureVerifiesFedUsingFixedSizeChunks(
algorithm, provider, keyPair.getPublic(), message, goodSigBytes, 3);
byte[] messageWithBitFlip = message.clone();
messageWithBitFlip[messageWithBitFlip.length / 2] ^= 1;
assertSignatureDoesNotVerifyOneShot(
algorithm, provider, keyPair.getPublic(), messageWithBitFlip, goodSigBytes);
byte[] goodSigWithBitFlip = goodSigBytes.clone();
goodSigWithBitFlip[goodSigWithBitFlip.length / 2] ^= 1;
assertSignatureDoesNotVerifyOneShot(
algorithm, provider, keyPair.getPublic(), message, goodSigWithBitFlip);
// Sign the message in one go
Signature signature = Signature.getInstance(algorithm);
signature.initSign(keyPair.getPrivate());
assertSame(provider, signature.getProvider());
signature.update(message);
byte[] generatedSigBytes = signature.sign();
boolean deterministicSignatureScheme =
algorithm.toLowerCase().endsWith("withrsa");
if (deterministicSignatureScheme) {
MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
} else {
if (Math.abs(goodSigBytes.length - generatedSigBytes.length) > 2) {
fail("Generated signature expected to be between "
+ (goodSigBytes.length - 2) + " and "
+ (goodSigBytes.length + 2) + " bytes long, but was: "
+ generatedSigBytes.length + " bytes: "
+ HexEncoding.encode(generatedSigBytes));
}
// Assert that the signature verifies using our own Provider
assertSignatureVerifiesOneShot(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
assertSignatureVerifiesFedOneByteAtATime(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
assertSignatureVerifiesFedUsingFixedSizeChunks(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes,
3);
// Assert that the signature verifies using whatever Provider is chosen by JCA
// by default for this signature algorithm and public key.
assertSignatureVerifiesOneShot(
algorithm, keyPair.getPublic(), message, generatedSigBytes);
}
// Sign the message by feeding it into the Signature one byte at a time
signature = Signature.getInstance(signature.getAlgorithm());
signature.initSign(keyPair.getPrivate());
for (int i = 0; i < message.length; i++) {
signature.update(message[i]);
}
generatedSigBytes = signature.sign();
if (deterministicSignatureScheme) {
MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
} else {
if (Math.abs(goodSigBytes.length - generatedSigBytes.length) > 2) {
fail("Generated signature expected to be between "
+ (goodSigBytes.length - 2) + " and "
+ (goodSigBytes.length + 2) + " bytes long, but was: "
+ generatedSigBytes.length + " bytes: "
+ HexEncoding.encode(generatedSigBytes));
}
// Assert that the signature verifies using our own Provider
assertSignatureVerifiesOneShot(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
assertSignatureVerifiesFedOneByteAtATime(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
assertSignatureVerifiesFedUsingFixedSizeChunks(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes,
3);
// Assert that the signature verifies using whatever Provider is chosen by JCA
// by default for this signature algorithm and public key.
assertSignatureVerifiesOneShot(
algorithm, keyPair.getPublic(), message, generatedSigBytes);
}
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
private static byte[] generateLargeKatMsg(byte[] seed, int msgSizeBytes) throws Exception {
byte[] result = new byte[msgSizeBytes];
MessageDigest digest = MessageDigest.getInstance("SHA-512");
int resultOffset = 0;
int resultRemaining = msgSizeBytes;
while (resultRemaining > 0) {
seed = digest.digest(seed);
int chunkSize = Math.min(seed.length, resultRemaining);
System.arraycopy(seed, 0, result, resultOffset, chunkSize);
resultOffset += chunkSize;
resultRemaining -= chunkSize;
}
return result;
}
public void testLongMsgKat() throws Exception {
Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
byte[] message = generateLargeKatMsg(LONG_MSG_KAT_SEED, LONG_MSG_KAT_SIZE_BYTES);
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
KeyPair keyPair = getKeyPairForSignatureAlgorithm(algorithm, keyPairs);
try {
if (algorithm.toLowerCase(Locale.US).startsWith("nonewith")) {
// This algorithm cannot accept large messages
Signature signature = Signature.getInstance(algorithm);
signature.initSign(keyPair.getPrivate());
assertSame(provider, signature.getProvider());
try {
signature.update(message);
signature.sign();
fail();
} catch (SignatureException expected) {}
// Bogus signature generated using SHA-256 digest -- shouldn't because the
// message is too long (and should not be digested/hashed) and because the
// signature uses the wrong digest/hash.
byte[] sigBytes = SHORT_MSG_KAT_SIGNATURES.get(
"SHA256" + algorithm.substring("NONE".length()));
assertNotNull(sigBytes);
signature = Signature.getInstance(algorithm);
signature.initVerify(keyPair.getPublic());
try {
signature.update(message);
signature.verify(sigBytes);
fail();
} catch (SignatureException expected) {}
continue;
}
byte[] goodSigBytes = LONG_MSG_KAT_SIGNATURES.get(algorithm);
assertNotNull(goodSigBytes);
// Assert that AndroidKeyStore provider can verify the known good signature.
assertSignatureVerifiesOneShot(
algorithm, provider, keyPair.getPublic(), message, goodSigBytes);
assertSignatureVerifiesFedUsingFixedSizeChunks(
algorithm, provider, keyPair.getPublic(), message, goodSigBytes, 718871);
// Sign the message in one go
Signature signature = Signature.getInstance(algorithm);
signature.initSign(keyPair.getPrivate());
assertSame(provider, signature.getProvider());
signature.update(message);
byte[] generatedSigBytes = signature.sign();
boolean deterministicSignatureScheme =
algorithm.toLowerCase().endsWith("withrsa");
if (deterministicSignatureScheme) {
MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
} else {
if (Math.abs(goodSigBytes.length - generatedSigBytes.length) > 2) {
fail("Generated signature expected to be between "
+ (goodSigBytes.length - 2) + " and "
+ (goodSigBytes.length + 2) + " bytes long, but was: "
+ generatedSigBytes.length + " bytes: "
+ HexEncoding.encode(generatedSigBytes));
}
// Assert that the signature verifies using our own Provider
assertSignatureVerifiesOneShot(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
assertSignatureVerifiesFedUsingFixedSizeChunks(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes,
718871);
// Assert that the signature verifies using whatever Provider is chosen by JCA
// by default for this signature algorithm and public key.
assertSignatureVerifiesOneShot(
algorithm, keyPair.getPublic(), message, generatedSigBytes);
}
// Sign the message by feeding it into the Signature one byte at a time
signature = Signature.getInstance(signature.getAlgorithm());
generatedSigBytes = generateSignatureFedUsingFixedSizeChunks(
algorithm, provider, keyPair.getPrivate(), message, 444307);
signature.initSign(keyPair.getPrivate());
if (deterministicSignatureScheme) {
MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
} else {
if (Math.abs(goodSigBytes.length - generatedSigBytes.length) > 2) {
fail("Generated signature expected to be between "
+ (goodSigBytes.length - 2) + " and "
+ (goodSigBytes.length + 2) + " bytes long, but was: "
+ generatedSigBytes.length + " bytes: "
+ HexEncoding.encode(generatedSigBytes));
}
// Assert that the signature verifies using our own Provider
assertSignatureVerifiesOneShot(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes);
assertSignatureVerifiesFedUsingFixedSizeChunks(
algorithm, provider, keyPair.getPublic(), message, generatedSigBytes,
718871);
// Assert that the signature verifies using whatever Provider is chosen by JCA
// by default for this signature algorithm and public key.
assertSignatureVerifiesOneShot(
algorithm, keyPair.getPublic(), message, generatedSigBytes);
}
} catch (Throwable e) {
throw new RuntimeException("Failed for " + algorithm, e);
}
}
}
public void testInitVerifySucceedsDespiteMissingAuthorizations() throws Exception {
KeyProtection spec = new KeyProtection.Builder(0).build();
assertInitVerifySucceeds("SHA256withECDSA", spec);
assertInitVerifySucceeds("SHA256withRSA", spec);
assertInitVerifySucceeds("SHA256withRSA/PSS", spec);
}
public void testInitSignFailsWhenNotAuthorizedToSign() throws Exception {
KeyProtection.Builder good = new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_NONE);
int badPurposes = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
| KeyProperties.PURPOSE_VERIFY;
assertInitSignSucceeds("SHA256withECDSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA256withECDSA",
TestUtils.buildUpon(good, badPurposes).build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
assertInitSignSucceeds("SHA256withRSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA256withRSA",
TestUtils.buildUpon(good, badPurposes).build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
assertInitSignSucceeds("SHA256withRSA/PSS", good.build());
assertInitSignThrowsInvalidKeyException("SHA256withRSA/PSS",
TestUtils.buildUpon(good, badPurposes).build());
}
public void testInitSignFailsWhenDigestNotAuthorized() throws Exception {
KeyProtection.Builder good = new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA384);
String badDigest = KeyProperties.DIGEST_SHA256;
assertInitSignSucceeds("SHA384withECDSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA384withECDSA",
TestUtils.buildUpon(good).setDigests(badDigest).build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
assertInitSignSucceeds("SHA384withRSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA384withRSA",
TestUtils.buildUpon(good).setDigests(badDigest).build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
assertInitSignSucceeds("SHA384withRSA/PSS", good.build());
assertInitSignThrowsInvalidKeyException("SHA384withRSA/PSS",
TestUtils.buildUpon(good).setDigests(badDigest).build());
}
public void testInitSignFailsWhenPaddingNotAuthorized() throws Exception {
KeyProtection.Builder good = new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA512);
// Does not apply to ECDSA because it doesn't any signature padding schemes
// assertInitSignThrowsInvalidKeyException("SHA256withECDSA", builder.build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
assertInitSignSucceeds("SHA512withRSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA512withRSA",
TestUtils.buildUpon(good)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
.build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
assertInitSignSucceeds("SHA512withRSA/PSS", good.build());
assertInitSignThrowsInvalidKeyException("SHA512withRSA/PSS",
TestUtils.buildUpon(good)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.build());
}
public void testInitSignFailsWhenKeyNotYetValid() throws Exception {
KeyProtection.Builder good = new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA224);
Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
assertInitSignSucceeds("SHA224withECDSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA224withECDSA",
TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
assertInitSignThrowsInvalidKeyException("SHA224withRSA", good.build());
assertInitSignSucceeds("SHA224withRSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA224withRSA",
TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS", good.build());
assertInitSignSucceeds("SHA224withRSA/PSS", good.build());
assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS",
TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
}
public void testInitSignFailsWhenKeyNoLongerValid() throws Exception {
KeyProtection.Builder good = new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_SHA224);
Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
assertInitSignSucceeds("SHA224withECDSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA224withECDSA",
TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
assertInitSignThrowsInvalidKeyException("SHA224withRSA", good.build());
assertInitSignSucceeds("SHA224withRSA", good.build());
assertInitSignThrowsInvalidKeyException("SHA224withRSA",
TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS", good.build());
assertInitSignSucceeds("SHA224withRSA/PSS", good.build());
assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS",
TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
}
private void assertInitVerifySucceeds(
String signatureAlgorithm,
KeyProtection keyProtection) throws Exception {
int[] resIds = getDefaultKeyAndCertResIds(signatureAlgorithm);
assertInitVerifySucceeds(
signatureAlgorithm,
resIds[0],
resIds[1],
keyProtection);
}
private void assertInitVerifySucceeds(
String signatureAlgorithm,
int privateKeyResId,
int certResId,
KeyProtection keyProtection) throws Exception {
PublicKey publicKey = TestUtils.importIntoAndroidKeyStore(
"test1",
TestUtils.getRawResPrivateKey(getContext(), privateKeyResId),
TestUtils.getRawResX509Certificate(getContext(), certResId),
keyProtection)
.getPublic();
Signature signature = Signature.getInstance(signatureAlgorithm);
signature.initVerify(publicKey);
}
private void assertInitSignSucceeds(
String signatureAlgorithm,
KeyProtection keyProtection) throws Exception {
int[] resIds = getDefaultKeyAndCertResIds(signatureAlgorithm);
assertInitSignSucceeds(
signatureAlgorithm,
resIds[0],
resIds[1],
keyProtection);
}
private void assertInitSignSucceeds(
String signatureAlgorithm,
int privateKeyResId,
int certResId,
KeyProtection keyProtection) throws Exception {
PrivateKey privateKey = TestUtils.importIntoAndroidKeyStore(
"test1",
TestUtils.getRawResPrivateKey(getContext(), privateKeyResId),
TestUtils.getRawResX509Certificate(getContext(), certResId),
keyProtection)
.getPrivate();
Signature signature = Signature.getInstance(signatureAlgorithm);
signature.initSign(privateKey);
}
private void assertInitSignThrowsInvalidKeyException(
String signatureAlgorithm,
KeyProtection keyProtection) throws Exception {
assertInitSignThrowsInvalidKeyException(null, signatureAlgorithm, keyProtection);
}
private void assertInitSignThrowsInvalidKeyException(
String message,
String signatureAlgorithm,
KeyProtection keyProtection) throws Exception {
int[] resIds = getDefaultKeyAndCertResIds(signatureAlgorithm);
assertInitSignThrowsInvalidKeyException(
message,
signatureAlgorithm,
resIds[0],
resIds[1],
keyProtection);
}
private void assertInitSignThrowsInvalidKeyException(
String message,
String signatureAlgorithm,
int privateKeyResId,
int certResId,
KeyProtection keyProtection) throws Exception {
PrivateKey privateKey = TestUtils.importIntoAndroidKeyStore(
"test1",
TestUtils.getRawResPrivateKey(getContext(), privateKeyResId),
TestUtils.getRawResX509Certificate(getContext(), certResId),
keyProtection)
.getPrivate();
Signature signature = Signature.getInstance(signatureAlgorithm);
try {
signature.initSign(privateKey);
fail(message);
} catch (InvalidKeyException expected) {}
}
static int[] getDefaultKeyAndCertResIds(String signatureAlgorithm) {
String sigAlgLowerCase = signatureAlgorithm.toLowerCase();
if (sigAlgLowerCase.contains("ecdsa")) {
return new int[] {R.raw.ec_key1_pkcs8, R.raw.ec_key1_cert};
} else if (sigAlgLowerCase.contains("rsa")) {
return new int[] {R.raw.rsa_key1_pkcs8, R.raw.rsa_key1_cert};
} else {
throw new IllegalArgumentException(
"Unknown signature algorithm: " + signatureAlgorithm);
}
}
private static String getKeyAlgorithmForSignatureAlgorithm(String signatureAlgorithm) {
String algLowerCase = signatureAlgorithm.toLowerCase();
if (algLowerCase.contains("withecdsa")) {
return KeyProperties.KEY_ALGORITHM_EC;
} else if (algLowerCase.contains("withrsa")) {
return KeyProperties.KEY_ALGORITHM_RSA;
} else {
throw new IllegalArgumentException(
"Unsupported signature algorithm: " + signatureAlgorithm);
}
}
private static KeyPair getKeyPairForSignatureAlgorithm(String signatureAlgorithm,
Iterable<KeyPair> keyPairs) {
return TestUtils.getKeyPairForKeyAlgorithm(
getKeyAlgorithmForSignatureAlgorithm(signatureAlgorithm), keyPairs);
}
private Collection<KeyPair> importDefaultKatKeyPairs() throws Exception {
return Arrays.asList(
TestUtils.importIntoAndroidKeyStore(
"testRsa",
TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key1_pkcs8),
TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key1_cert),
new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_NONE)
.setSignaturePaddings(
KeyProperties.SIGNATURE_PADDING_RSA_PKCS1,
KeyProperties.SIGNATURE_PADDING_RSA_PSS)
.build()),
TestUtils.importIntoAndroidKeyStore(
"testEc",
TestUtils.getRawResPrivateKey(getContext(), R.raw.ec_key1_pkcs8),
TestUtils.getRawResX509Certificate(getContext(), R.raw.ec_key1_cert),
new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
.setDigests(KeyProperties.DIGEST_NONE)
.build())
);
}
private void assertSignatureVerifiesOneShot(
String algorithm,
PublicKey publicKey,
byte[] message,
byte[] signature) throws Exception {
assertSignatureVerifiesOneShot(algorithm, null, publicKey, message, signature);
}
private void assertSignatureVerifiesOneShot(
String algorithm,
Provider provider,
PublicKey publicKey,
byte[] message,
byte[] signature) throws Exception {
Signature sig = (provider != null)
? Signature.getInstance(algorithm, provider) : Signature.getInstance(algorithm);
sig.initVerify(publicKey);
sig.update(message);
if (!sig.verify(signature)) {
fail("Signature did not verify. algorithm: " + algorithm
+ ", provider: " + sig.getProvider().getName()
+ ", signature (" + signature.length + " bytes): "
+ HexEncoding.encode(signature));
}
}
private void assertSignatureDoesNotVerifyOneShot(
String algorithm,
Provider provider,
PublicKey publicKey,
byte[] message,
byte[] signature) throws Exception {
Signature sig = (provider != null)
? Signature.getInstance(algorithm, provider) : Signature.getInstance(algorithm);
sig.initVerify(publicKey);
sig.update(message);
if (sig.verify(signature)) {
fail("Signature verified unexpectedly. algorithm: " + algorithm
+ ", provider: " + sig.getProvider().getName()
+ ", signature (" + signature.length + " bytes): "
+ HexEncoding.encode(signature));
}
}
private void assertSignatureVerifiesFedOneByteAtATime(
String algorithm,
Provider provider,
PublicKey publicKey,
byte[] message,
byte[] signature) throws Exception {
Signature sig = (provider != null)
? Signature.getInstance(algorithm, provider) : Signature.getInstance(algorithm);
sig.initVerify(publicKey);
for (int i = 0; i < message.length; i++) {
sig.update(message[i]);
}
if (!sig.verify(signature)) {
fail("Signature did not verify. algorithm: " + algorithm
+ ", provider: " + sig.getProvider().getName()
+ ", signature (" + signature.length + " bytes): "
+ HexEncoding.encode(signature));
}
}
private byte[] generateSignatureFedUsingFixedSizeChunks(
String algorithm,
Provider expectedProvider,
PrivateKey privateKey,
byte[] message,
int chunkSizeBytes) throws Exception {
Signature signature = Signature.getInstance(algorithm);
signature.initSign(privateKey);
assertSame(expectedProvider, signature.getProvider());
int messageRemaining = message.length;
int messageOffset = 0;
while (messageRemaining > 0) {
int actualChunkSizeBytes = Math.min(chunkSizeBytes, messageRemaining);
signature.update(message, messageOffset, actualChunkSizeBytes);
messageOffset += actualChunkSizeBytes;
messageRemaining -= actualChunkSizeBytes;
}
return signature.sign();
}
private void assertSignatureVerifiesFedUsingFixedSizeChunks(
String algorithm,
Provider provider,
PublicKey publicKey,
byte[] message,
byte[] signature,
int chunkSizeBytes) throws Exception {
Signature sig = (provider != null)
? Signature.getInstance(algorithm, provider) : Signature.getInstance(algorithm);
sig.initVerify(publicKey);
int messageRemaining = message.length;
int messageOffset = 0;
while (messageRemaining > 0) {
int actualChunkSizeBytes = Math.min(chunkSizeBytes, messageRemaining);
sig.update(message, messageOffset, actualChunkSizeBytes);
messageOffset += actualChunkSizeBytes;
messageRemaining -= actualChunkSizeBytes;
}
if (!sig.verify(signature)) {
fail("Signature did not verify. algorithm: " + algorithm
+ ", provider: " + sig.getProvider().getName()
+ ", signature (" + signature.length + " bytes): "
+ HexEncoding.encode(signature));
}
}
}