2016-03-14 43 views
3

在使用AndroidKeyStore生成RSA密鑰期間,我在我的應用程序中遇到了以下問題,而不是理解它可以輕鬆地在Android SDK的BasicAndroidKeyStore示例應用程序中再現。所以,如果你有Locale.getDefault() == Locale.US比這個樣品效果很好,但如果你改變區域設置,例如,"ar_EG",它將與異常崩潰:AndroidKeyStore密鑰生成期間的IllegalArgumentException(Unparseable date)

java.lang.IllegalArgumentException異常:無效的日期字符串:無法解析 日期: 「af`cadaaedcaGMT + 00:00」(在偏移0) 在 com.android.org.bouncycastle.asn1.DERUTCTime(DERUTCTime.java:98) 在com.android.org.bouncycastle.asn1.x509。時間。(Time.java:62) at 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類,它的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()); 
    } 
} 

在調用給被傳遞到下面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)); 
    } 

那麼,什麼是正確的修復或建議如何解決這個問題?

+0

1.什麼是Android版本,建設者號,和Android的機型你看到這個設備? 2.如果您在生成密鑰對和自簽名證書之前調用Locale.setDefault(Locale.US),問題是否消失? –

+0

1.我在聯想A536(4.4.2)和LG Nexus 4(5.1.1)上對此進行了測試。 – texnedo

+0

2.是的,我第一次發現這個問題後,試圖從代碼中改變語言環境。這個修復程序解決了這個問題,但它看起來很髒,因爲我們的應用程序的其他部分可能會調用Locale.getDefault()並獲取錯誤的值。 – texnedo

回答

0

我正在使用下面的代碼來生成Android M中的RSA密鑰對。Might可以幫助您。

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

它是否適用於阿拉伯語區域? – texnedo

3

這是一個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()); 
}