在AndroidKeyStore密钥生成期间出现IllegalArgumentException(Unparseable date)

我在使用AndroidKeyStore的 RSA密钥生成期间在我的应用程序中遇到了以下问题,而不是理解它可以在Android SDK的BasicAndroidKeyStore示例应用程序中轻松复制。 因此,如果您的Locale.getDefault() == Locale.US比此示例效果好,但如果您将语言环境更改为"ar_EG" ,则会因exception而崩溃:

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

因此,问题在于转换为String的密钥有效时间是针对默认语言环境进行的。 这是来自ASN1UTCTime类的代码片段,它在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  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)); } 

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

这是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.(DERUTCTime.java:98) at com.android.org.bouncycastle.asn1.x509.Time.(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 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();