Не удалось выполнить операцию с хранилищем ключей со знаком RSA и проверить
У меня есть трудности с использованием RSA для подписи и проверки. Ниже часть моего RSA
класс, который я использую. Методы init()
, privateKey()
а также publicKey()
хорошо работать с encrypt()
а также decrypt()
методы (последние два не перечислены здесь), поэтому я предполагаю, что я сделал некоторую ошибку внутри моего sign()
метод. метод verify()
еще не проверялось, потому что подписывание было неудачным, поэтому я не мог двигаться дальше. Пожалуйста, взгляните...
мой RSA
класс это:
class RSA
{
private final static String ANDROID_KEY_STORE = "AndroidKeyStore";
public final static int ANY_PURPOSE = KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_SIGN |
KeyProperties.PURPOSE_VERIFY;
public final static long CENTURY = (100 * 365) + 24;
public enum KeySize
{
BIT_512 ( 512),
BIT_768 ( 768),
BIT_1024 (1024),
BIT_2048 (2048),
BIT_3072 (3072),
BIT_4096 (4096);
private int value;
KeySize(int value)
{
this.value = value;
}
public int value()
{
return this.value;
}
}
public enum SignatureAlgorithm
{
MD5WithRSA ("MD5WithRSA"),
SHA1WithRSA ("SHA1WithRSA"),
SHA1WithRSA_PSS ("SHA1WithRSA/PSS"),
SHA224WithRSA ("SHA224WithRSA"),
SHA224WithRSA_PSS ("SHA224WithRSA/PSS"),
SHA256WithRSA ("SHA256WithRSA"),
SHA256WithRSA_PSS ("SHA256WithRSA/PSS"),
SHA384WithRSA ("SHA384WithRSA"),
SHA384WithRSA_PSS ("SHA384WithRSA/PSS"),
SHA512WithRSA ("SHA512WithRSA"),
SHA512WithRSA_PSS ("SHA512WithRSA/PSS");
private String value;
SignatureAlgorithm(String value)
{
this.value = value;
}
public String value()
{
return this.value;
}
}
public static KeyPair init(final String alias, final RSA.KeySize keySize,
final long validityDays, final Boolean reset)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
IOException, NoSuchProviderException, InvalidAlgorithmParameterException
{
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
if (reset || (!keyStore.containsAlias(alias)))
{
final long now = java.lang.System.currentTimeMillis();
KeyPairGenerator keyPairGenerator = KeyPairGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE);
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(alias, RSA.ANY_PURPOSE)
.setRandomizedEncryptionRequired(false)
.setDigests(
KeyProperties.DIGEST_NONE, KeyProperties.DIGEST_MD5,
KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA224,
KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
.setKeySize(keySize.value())
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_NONE,
KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setCertificateSubject(new X500Principal(
"CN=Android, O=Android Authority"))
.setCertificateSerialNumber(new BigInteger(256, new Random()))
.setCertificateNotBefore(new Date (now - (now % 1000L)))
.setCertificateNotAfter(new Date(((new Date(now - (now % 1000L)))
.getTime()) + (validityDays * 86400000L)))
.build());
return keyPairGenerator.generateKeyPair();
}
else return null;
}
public static PublicKey publicKey(final String alias)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
IOException, UnrecoverableEntryException
{
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
return keyStore.getCertificate(alias).getPublicKey();
}
public static PrivateKey privateKey(final String alias)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
IOException, UnrecoverableEntryException
{
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
return (PrivateKey) keyStore.getKey(alias, null);
}
public static byte[] sign(final String message, final String alias,
final SignatureAlgorithm algorithm)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
IOException, UnrecoverableEntryException,
InvalidKeyException, SignatureException
{
Signature instance = Signature.getInstance(algorithm.value());
instance.initSign(privateKey(alias), new SecureRandom());
instance.update(message.getBytes("UTF-8"));
return instance.sign();
}
public static Boolean verify(final String message, final byte[] signature,
final String alias, final SignatureAlgorithm algorithm)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
IOException, UnrecoverableEntryException,
InvalidKeyException, SignatureException
{
Signature instance = Signature.getInstance(algorithm.value());
instance.initVerify(publicKey(alias));
instance.update(message.getBytes("UTF-8"));
return instance.verify(signature);
}
}
мой Hex
класс это:
class Hex
{
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHexString(final byte[] bytes)
{
char[] hexChars = new char[2 * bytes.length];
for (int i = 0; i < bytes.length; i++)
{
int b = bytes[i] & 0xFF;
hexChars[2 * i] = hexArray[b >>> 4];
hexChars[2 * i + 1] = hexArray[b & 0x0F];
}
return new String(hexChars);
}
}
Мой тестовый код:
try
{
RSA.init("RSA", RSA.KeySize.BIT_512, RSA.CENTURY, true);
RSA.SignatureAlgorithm signatureAlgorithm = RSA.SignatureAlgorithm.SHA1WithRSA;
byte[] x = RSA.sign("My message", "RSA", signatureAlgorithm);
Log.d("RSA Test", "x: " + Hex.bytesToHexString(x));
Boolean y = RSA.verify("My message", x, "RSA", signatureAlgorithm);
Log.d("RSA Test", "y: " + (y? "True" : "False"));
}
catch (Exception e)
{
Log.d("RSA Test", "ERROR: " + e.toString());
}
И вывод:
ОШИБКА: java.security.InvalidKeyException: сбой операции хранилища ключей
Отладка показывает, что ошибка происходит внутри моего RSA.sign()
метод, когда он вызывает initSign()
,
Что не так в моем коде?
1 ответ
Если вы подписываете ключ RSA, вы должны установить режим заполнения подписи в KeyGenParameterSpec
, Вы этого не сделали. Например, в вашем init()
метод вы могли бы добавить следующую строку сразу после .setEncryptionPaddings()
:
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
например
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(alias, RSA.ANY_PURPOSE)
.setRandomizedEncryptionRequired(false)
.setDigests(
KeyProperties.DIGEST_NONE, KeyProperties.DIGEST_MD5,
KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA224,
KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
.setKeySize(keySize.value())
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_NONE,
KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setCertificateSubject(new X500Principal(
"CN=Android, O=Android Authority"))
.setCertificateSerialNumber(new BigInteger(256, new Random()))
.setCertificateNotBefore(new Date(now - (now % 1000L)))
.setCertificateNotAfter(new Date(((new Date(now - (now % 1000L)))
.getTime()) + (validityDays * 86400000L)))
.build());
Другой поддерживаемый режим заполнения подписи SIGNATURE_PADDING_RSA_PSS
, Обратите внимание, что подписи PSS и шифрование OAEP являются более современными версиями блочного форматирования RSA для подписи и шифрования. Они обеспечивают лучшие гарантии безопасности и всегда должны быть предпочтительнее других режимов, если они не могут быть использованы из-за проблем совместимости с существующими базами кода.
Названия алгоритма подписи, оканчивающиеся на "PSS", например SHA224WithRSA_PSS
очевидно, только для заполнения PSS, тогда как другие только для заполнения PKCS1.