【发布时间】:2021-05-20 03:21:17
【问题描述】:
我正在开发一个在服务器和 Android 应用之间实现 RSA threshold signature 的应用程序。
服务器和应用程序各有一份 RSA 私钥。在 Android/Java 中,有两个类用于保存 RSA 私钥:
后者持有额外的信息(如模数分解、CRT coefficients等)以提高签名效率。但是,我无法存储额外的信息,因为它们会泄露主私钥。
我尝试在 Android KeyStore 中存储前一种类型的对象 (RSAPrivateKey),但似乎密钥库只接受后一种类型 (RSAPrivateCrtKey)。
这是有效的代码摘录(DummyCert 只是 java.security.cert.X509Certificate 的一个实现,其所有方法都返回默认值):
BigInteger n = new BigInteger("...");
BigInteger d = new BigInteger("...");
BigInteger e = new BigInteger("...");
BigInteger primeP = new BigInteger("...");
BigInteger primeQ = new BigInteger("...");
BigInteger primeExponentP = new BigInteger("...");
BigInteger primeExponentQ = new BigInteger("...");
BigInteger crt = new BigInteger("...");
KeySpec keySpec = new RSAPrivateCrtKeySpec(n, e, d, primeP, primeQ, primeExponentP, primeExponentQ, crt);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey sk = kf.generatePrivate(keySpec);
Certificate[] dummy = new Certificate[]{new DummyCert()};
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyStore.setKeyEntry(KEY_ALIAS, sk, null, dummy);
但是,如果我们将keySpec 的定义更改为:
KeySpec keySpec = new RSAPrivateKeySpec(n, d);
抛出以下异常:
java.lang.RuntimeException: error:04000090:RSA routines:OPENSSL_internal:VALUE_MISSING
at com.android.org.conscrypt.NativeCrypto.EVP_marshal_private_key(Native Method)
at com.android.org.conscrypt.OpenSSLRSAPrivateKey.getEncoded(OpenSSLRSAPrivateKey.java:207)
at android.security.keystore.AndroidKeyStoreSpi.setPrivateKeyEntry(AndroidKeyStoreSpi.java:480)
at android.security.keystore.AndroidKeyStoreSpi.engineSetKeyEntry(AndroidKeyStoreSpi.java:294)
at java.security.KeyStore.setKeyEntry(KeyStore.java:1179)
at com.example.keystoretester.MainActivity.createKeyStoreEntry(MainActivity.java:68)
at com.example.keystoretester.MainActivity.onCreate(MainActivity.java:31)
at android.app.Activity.performCreate(Activity.java:8000)
at android.app.Activity.performCreate(Activity.java:7984)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
PS:
我逐行跟踪setKeyEntry 的代码。似乎AndroidKeyStoreSpi 的这一部分抛出了异常,可能调用了 native 函数:
int errorCode = mKeyStore.importKey(
Credentials.USER_PRIVATE_KEY + alias,
importArgs,
KeymasterDefs.KM_KEY_FORMAT_PKCS8,
pkcs8EncodedPrivateKeyBytes,
mUid,
flags,
resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
throw new KeyStoreException("Failed to store private key",
KeyStore.getKeyStoreException(errorCode));
}
可以通过直接访问密钥存储服务来规避上述检查。 here 解释了一个这样的例子。但代码适用于 Android 4.x,它可能太旧了。我只是想在进一步挖掘之前获得专家意见。
【问题讨论】:
-
您基本上是想对所有内容进行分解。
previousPrime()或previousProbablePrime()是您需要的一种方法。我只能这么说。 -
...但是,我无法存储额外的信息,因为它们会泄露主私钥...。我不知道这到底是什么意思,但给定只需私有指数(以及公共指数和模数),您就可以快速导出私钥的 CRT 形式中存在的所有剩余参数。因此,实际上,
RSAPrivateCrtKey中泄露的任何内容也会在RSAPrivateKey中泄露。 -
@PresidentJamesK.Polk:这正是我不将公共指数与私钥一起存储并使用 DummyCert 类的原因。准确地说,设 (e,n) 为公钥,(d,n) 为私钥。提取两个私钥共享 (d1, n) 和 (d2, n),其中 d1*d2 = d (mod phi(n))。该应用仅存储 (d1, n),但不存储其“对应的”公钥 (e1, n),其中 e1 = (d1)^-1 mod phi(n)。
标签: java android cryptography rsa keystore