Keystore operation failed with RSA sign and verify

筅森魡賤 提交于 2020-01-14 03:10:00

问题


I have difficulties to use RSA for signing and verifying. Below is part of my RSA class that I use. The methods init(), privateKey() and publicKey() work fine with encrypt() and decrypt() methods (last two not listed here), so I assume I made some mistake inside of my sign() method. Method verify() wasn't tested yet, because signing was unsuccessful, so I wasn't able to move further. Please take a look...


My RSA class is:

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);
    }
}

My Hex class is:

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);
    }
}

My test code is:

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());
}

And the output is:

ERROR: java.security.InvalidKeyException: Keystore operation failed

Debugging shows that the error occur inside of my RSA.sign() method, when it calls initSign().

What is wrong in my code?


回答1:


If you are signing with an RSA key you must set a signature padding mode in the KeyGenParameterSpec. You haven't done so. For example, in your init() method you might add the following line right after .setEncryptionPaddings():

.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)

e.g.

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());

The other supported signature padding mode is SIGNATURE_PADDING_RSA_PSS. Note that PSS signatures and OAEP encryption are the more modern versions of RSA block formatting for signing and encrypting. They provide better security guarantees and should always be preferred over the other modes unless they cannot be used because of interoperability issues with existing code bases.

The signature algorithm names that end in "PSS", e.g. SHA224WithRSA_PSS are obviously only for PSS padding, whereas the others are only for PKCS1 padding.



来源:https://stackoverflow.com/questions/49410575/keystore-operation-failed-with-rsa-sign-and-verify

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!