【问题标题】:IllegalArgumentException (Unparseable date) during AndroidKeyStore key generationAndroidKeyStore 密钥生成期间的 IllegalArgumentException(无法解析的日期)
【发布时间】:2016-03-14 12:24:57
【问题描述】:

在使用AndroidKeyStore 生成 RSA 密钥期间,我在我的应用程序中遇到了以下问题,但我了解到它可以很容易地在 Android SDK 的 BasicAndroidKeyStore 示例应用程序中重现。因此,如果您有 Locale.getDefault() == Locale.US,则此示例运行良好,但如果您将语言环境更改为,例如,"ar_EG",它将崩溃并出现异常:

java.lang.IllegalArgumentException:无效的日期字符串:无法解析 日期:“af`cadaaedcaGMT+00:00”(偏移量 0) 在 com.android.org.bouncycastle.asn1.DERUTCTime.(DERUTCTime.java:98) 在 com.android.org.bouncycastle.asn1.x509.Time.(Time.java:62) 在 com.android.org.bouncycastle.x509.X509V3CertificateGenerator.setNotBefore(X509V3CertificateGenerator.java:112) 在 android.security.AndroidKeyPairGenerator.generateKeyPair(AndroidKeyPairGenerator.java:127) 在 java.security.KeyPairGenerator$KeyPairGeneratorImpl.generateKeyPair(KeyPairGenerator.java:276) 在 com.example.android.basicandroidkeystore.BasicAndroidKeyStoreFragment.createKeys(BasicAndroidKeyStoreFragment.java:237)

因此,问题在于密钥有效时间转换为相对于默认区域设置的字符串。 这是来自ASN1UTCTime 类的代码sn-p,它在KeyPairGenerator.generateKeyPair() 的底层使用以下方法调用:

public ASN1UTCTime(
    String time)
{
    this.time = Strings.toByteArray(time);
    try
    {
        this.getDate();
    }
    catch (ParseException e)
    {
        throw new IllegalArgumentException("invalid date string: " + e.getMessage());
    }
}

在调用此方法之前,将 Date 对象传递给以下 Time 构造函数,该构造函数使用默认系统语言环境:

public Time(
        Date    time)
    {
        SimpleTimeZone      tz = new SimpleTimeZone(0, "Z");
        SimpleDateFormat    dateF = new SimpleDateFormat("yyyyMMddHHmmss");
        dateF.setTimeZone(tz);
        String  d = dateF.format(time) + "Z";
        int     year = Integer.parseInt(d.substring(0, 4));
        if (year < 1950 || year > 2049)
        {
            this.time = new DERGeneralizedTime(d);
        }
        else
        {
            this.time = new DERUTCTime(d.substring(2));
        }
    } 

这很奇怪,因为ASN1UTCTime类还有一个构造函数,似乎更适合国际化工作:

/**
     * Base constructor from a java.util.date and Locale - you may need to use this if the default locale
     * doesn't use a Gregorian calender so that the GeneralizedTime produced is compatible with other ASN.1 implementations.
     *
     * @param time a date object representing the time of interest.
     * @param locale an appropriate Locale for producing an ASN.1 UTCTime value.
     */
    public ASN1UTCTime(
        Date time,
        Locale locale)
    {
        SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'", locale);
        dateF.setTimeZone(new SimpleTimeZone(0,"Z"));
        this.time = Strings.toByteArray(dateF.format(time));
    } 

那么,什么是正确的解决方法或建议如何解决这个问题?

【问题讨论】:

  • 1.您看到此内容的 Android 设备的 Android 版本、制造商编号和型号是什么? 2. 如果在生成密钥对和自签名证书之前调用Locale.setDefault(Locale.US),问题会消失吗?
  • 1.我在 Lenovo A536 (4.4.2) 和 LG Nexus 4 (5.1.1) 上对此进行了测试。
  • 2.是的,在我第一次发现这个问题之后,我试图从代码中更改语言环境。这个修复解决了这个问题,但它看起来很脏,因为我们应用程序的其他部分可能会调用 Locale.getDefault() 并获得错误的值。

标签: java android security


【解决方案1】:

这是known issue with AndroidKeyStore

Android KeyStore 未正确获取语言环境,并导致语言从右到左的设备语言环境失败。示例堆栈跟踪:

Caused by: java.lang.IllegalArgumentException: invalid date string: Unparseable date: "aga``eaeeb`eGMT+00:00" (at offset 0)
    at com.android.org.bouncycastle.asn1.DERUTCTime.<init>(DERUTCTime.java:98)
    at com.android.org.bouncycastle.asn1.x509.Time.<init>(Time.java:62)
    at com.android.org.bouncycastle.x509.X509V3CertificateGenerator.setNotBefore(X509V3CertificateGenerator.java:112)
    at android.security.AndroidKeyPairGenerator.generateKeyPair(AndroidKeyPairGenerator.java:128)
    at java.security.KeyPairGenerator$KeyPairGeneratorImpl.generateKeyPair(KeyPairGenerator.java:275)

一种解决方法是在生成密钥对之前设置英语语言环境,然后将其更改回来:

/**
 * Generates RSA keys.
 */
private void generateRsaKeys(Context context, String rsaAlias) {
    try {
        // Set English locale as default (workaround)
        Locale initialLocale = Locale.getDefault();
        setLocale(Locale.ENGLISH);
        // Generate the RSA key pairs
        Calendar start = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        end.add(Calendar.YEAR, 30); // 30 years
        KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
                .setAlias(rsaAlias)
                .setSubject(new X500Principal("CN=" + rsaAlias + ", O=Organization"))
                .setSerialNumber(BigInteger.TEN)
                .setStartDate(start.getTime())
                .setEndDate(end.getTime())
                .build();
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(RSA, ANDROID_KEY_STORE);
        kpg.initialize(spec);
        kpg.generateKeyPair();
        // Reset default locale
        setLocale(initialLocale);
    } catch (NoSuchAlgorithmException | NoSuchProviderException | InvalidAlgorithmParameterException e) {
        Log.e(e, "generateRsaKeys: ");
    }
}

/**
 * Sets default locale.
 */
private void setLocale(Locale locale) {
    Locale.setDefault(locale);
    Resources resources = context.getResources();
    Configuration config = resources.getConfiguration();
    config.locale = locale;
    resources.updateConfiguration(config, resources.getDisplayMetrics());
}

【讨论】:

  • 你为我节省了很多时间。谢谢。
  • 我想知道他们在哪个 Android 版本上修复了这个问题?至少在 API 21 上它仍然是可重现的,但在 Android 9 上却不是。
【解决方案2】:

我正在使用以下代码在 Android M 中生成 RSA 密钥对。可能会对您有所帮助。

KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA",
                    "AndroidKeyStore");
            gen.initialize(new KeyGenParameterSpec.Builder(KEY_ALIAS,
                    KeyProperties.PURPOSE_ENCRYPT
                            | KeyProperties.PURPOSE_DECRYPT)
                                    .setDigests(KeyProperties.DIGEST_SHA256,
                                            KeyProperties.DIGEST_SHA512)
                                    .setSignaturePaddings(
                                            KeyProperties.SIGNATURE_PADDING_RSA_PSS)
                                    .setEncryptionPaddings(
                                            KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
                                    .build());

            KeyPair keyPair = gen.generateKeyPair();

【讨论】:

    【解决方案3】:

    1- 将计算机的日期和时间设置为默认设置
    2- 为您所在地区设置正确的时区
    3- 重启安卓工作室

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-01
      • 2015-06-17
      • 2018-01-30
      相关资源
      最近更新 更多