ממשק API של Java KeyStore

ג'אווה טופ

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

1. סקירה כללית

במדריך זה אנו בוחנים ניהול מפתחות ותעודות הצפנה ב- Java באמצעות ה- KeyStore ממשק API.

2. Keystores

אם עלינו לנהל מפתחות ואישורים בג'אווה, אנו זקוקים ל- חנות מפתחות, שהוא פשוט אוסף מאובטח של alias ערכים של מפתחות ותעודות.

אנו בדרך כלל שומרים את חנויות המפתח במערכת קבצים ויכולים להגן עליה באמצעות סיסמה.

כברירת מחדל, ל- Java יש קובץ חנות מפתחות הממוקם ב JAVA_HOME /jre/ lib / אבטחה / cacerts. אנו יכולים לגשת למאגר המפתחות באמצעות סיסמת ברירת המחדל של מאגר המפתחות שנה את זה.

עכשיו, עם קצת הרקע הזה, בוא נגיע ליצור את הראשון שלנו.

3. יצירת Keystore

3.1. בְּנִיָה

אנו יכולים ליצור חנות מפתחות בקלות באמצעות keytool, או שנוכל לעשות זאת באופן תכנותי באמצעות ה- KeyStore ממשק API:

KeyStore ks = KeyStore.getInstance (KeyStore.getDefaultType ());

כאן אנו משתמשים בסוג ברירת המחדל, אם כי ישנם כמה סוגים של חנות מפתחות זמינים כמו jceks אוֹ 12.

אנו יכולים לעקוף את סוג ברירת המחדל של "JKS" (פרוטוקול חנות מפתחות של Oracle) באמצעות a -Dkeystore.type פָּרָמֶטֶר:

-Dkeystore.type = pkcs12

לחלופין, אנו יכולים כמובן לרשום את אחד הפורמטים הנתמכים ב- getInstance:

KeyStore ks = KeyStore.getInstance ("pcks12"); 

3.2. אִתחוּל

בהתחלה, אנחנו צריכים לִטעוֹן חנות המפתחות:

char [] pwdArray = "סיסמה" .toCharArray (); ks.load (null, pwdArray); 

אנו משתמשים לִטעוֹן בין אם אנו יוצרים חנות מפתחות חדשה ובין אם נפתח מפתח קיים.

ואנחנו אומרים KeyStore ליצור חדש על ידי מעבר ריק כפרמטר הראשון.

אנו מספקים גם סיסמה שתשמש לגישה למאגר המפתחות בעתיד. אנחנו יכולים גם להגדיר את זה ל ריקאף על פי שהסודות שלנו ייפתחו.

3.3. אִחסוּן

לבסוף, אנו שומרים את מאגר המפתחות החדש שלנו במערכת הקבצים:

נסה (FileOutputStream fos = חדש FileOutputStream ("newKeyStoreFileName.jks")) {ks.store (fos, pwdArray); } 

שים לב שלא מוצגים לעיל הם כמה יוצאים מן הכלל שנבדקו getInstance, לִטעוֹן, ו חנות כל זריקה.

4. טעינת Keystore

כדי לטעון חנות מפתחות, ראשית עלינו ליצור KeyStore למשל, כמו קודם.

הפעם, עם זאת, בואו נציין את הפורמט מכיוון שאנחנו טוענים פורמט קיים:

KeyStore ks = KeyStore.getInstance ("JKS"); ks.load (FileInputStream חדש ("newKeyStoreFileName.jks"), pwdArray);

אם ה- JVM שלנו לא תומך בסוג מאגר המפתחות שעברנו, או אם הוא לא תואם את סוג המפתח במערכת הקבצים שאנו פותחים, נקבל KeyStoreException:

java.security.KeyStoreException: KEYSTORE_TYPE לא נמצא

כמו כן, אם הסיסמה שגויה, נקבל חריג מפתח בלתי ניתן לשחזור:

java.security.UnrecoverableKeyException: אימות הסיסמה נכשל

5. אחסון ערכים

בחנות המפתחות אנו יכולים לאחסן שלושה סוגים שונים של ערכים, כל ערך תחת הכינוי שלו:

  • מקשים סימטריים (המכונים מפתחות סודיים ב- JCE),
  • מפתחות אסימטריים (המכונים מפתחות ציבוריים ופרטיים ב- JCE), וכן
  • אישורים מהימנים

בואו נסתכל על כל אחד.

5.1. שמירת מפתח סימטרי

הדבר הפשוט ביותר שנוכל לאחסן בחנות מפתחות הוא מפתח סימטרי.

כדי לשמור מפתח סימטרי, נצטרך שלושה דברים:

  1. כינוי - זה פשוט השם שבו נשתמש בעתיד בכדי להתייחס לערך
  2. מפתח - שעטוף א KeyStore.SecretKeyEntry.
  3. סיסמה - שעטוף במה שמכונה a הגנה פאראם.
KeyStore.SecretKeyEntry secret = חדש KeyStore.SecretKeyEntry (secretKey); KeyStore.ProtectionParameter סיסמה = KeyStore.PasswordProtection חדש (pwdArray); ks.setEntry ("סוד db- הצפנה", סוד, סיסמה);

זכור כי הסיסמה לא יכולה להיות ריק, עם זאת, זה יכול להיות ריק חוּט.אם נשאיר את הסיסמה ריק לקבלת ערך, נקבל KeyStoreException:

java.security.KeyStoreException: נדרשת סיסמה שאינה null כדי ליצור SecretKeyEntry

זה אולי נראה קצת מוזר שאנחנו צריכים לעטוף את המפתח והסיסמה בשיעורי עטיפה.

אנחנו עוטפים את המפתח כי setEntry היא שיטה כללית שניתן להשתמש בה גם לסוגי הערך האחרים. סוג הכניסה מאפשר את KeyStore API לטיפול בזה אחרת.

אנו עוטפים את הסיסמה מכיוון ש- KeyStore API תומך בשיחות חוזרות ל- GUI ו- CLI כדי לאסוף את הסיסמה ממשתמש הקצה. בדוק את KeyStore.CallbackHandlerProtection Javadoc לפרטים נוספים.

אנו יכולים להשתמש בשיטה זו גם כדי לעדכן מפתח קיים. אנחנו רק צריכים להתקשר אליו שוב עם אותה כינוי וסיסמא והחדשה שלנו סוֹד.

5.2. שמירת מפתח פרטי

אחסון מפתחות אסימטריים הוא מעט מורכב יותר מכיוון שאנו צריכים להתמודד עם רשתות תעודות.

וגם ה KeyStore API נותן לנו שיטה ייעודית הנקראת setKeyEntry שהוא יותר נוח מהגנרי setEntry שיטה.

לכן, כדי לשמור מפתח אסימטרי, נצטרך ארבעה דברים:

  1. כינוי, כמו מקודם
  2. מפתח פרטי. מכיוון שאיננו משתמשים בשיטה הגנרית, המפתח לא ייעטף. כמו כן, לענייננו, זה צריך להיות מופע של מפתח פרטי
  3. סיסמה לגישה לערך. הפעם, הסיסמה היא חובה
  4. שרשרת תעודות המאשר את המפתח הציבורי המתאים
X509Certificate [] certificateChain = X509Certificate חדש [2]; שרשרת [0] = clientCert; שרשרת [1] = caCert; ks.setKeyEntry ("מפתח חתימת sso", privateKey, pwdArray, certificateChain);

עכשיו, המון יכול להשתבש כאן, כמובן, כאילו pwdArray הוא ריק:

java.security.KeyStoreException: הסיסמה לא יכולה להיות אפסית

אבל יש חריג ממש מוזר שצריך להיות מודע אליו, וזה אם pwdArray הוא מערך ריק:

java.security.UnrecoverableKeyException: נתון החסימה הסופית לא מרופדת כראוי

כדי לעדכן, אנו יכולים פשוט להתקשר לשיטה שוב עם אותה כינוי חדש מפתח פרטי ו תעודת שרשרת.

כמו כן, ייתכן שיהיה ערך לעשות רענון מהיר כיצד ליצור שרשרת אישורים.

5.3. שמירת תעודה מהימנה

אחסון אישורים מהימנים הוא די פשוט. זה דורש רק את הכינוי ואת האישוראת עצמה, שהוא מסוג תְעוּדָה:

ks.setCertificateEntry ("google.com", trustCertificate);

בדרך כלל האישור הוא שלא ייצרנו, אך הגיע מצד שלישי.

בגלל זה, חשוב לציין כאן את זה KeyStore לא ממש מאמת אישור זה. עלינו לאמת זאת בעצמנו לפני שנאחסן אותו.

כדי לעדכן, אנחנו יכולים פשוט להתקשר שוב לשיטה עם אותה כינוי חדש Trustcertificate.

6. קריאת ערכים

כעת לאחר שכתבנו כמה ערכים, בוודאי נרצה לקרוא אותם.

6.1. קריאת ערך יחיד

ראשית, אנו יכולים לשלוף מפתחות ותעודות לפי הכינוי שלהם:

מפתח ssoSigningKey = ks.getKey ("מפתח חתימת sso", pwdArray); תעודה google = ks.getCertificate ("google.com");

אם אין ערך בשם זה או שהוא מסוג אחר, אז להשיג מפתח פשוט חוזר ריק:

חלל ציבורי כאשרEntryIsMissingOrOfIncorrectType_thenReturnsNull () {// ... אתחל את חנות המפתח // ... הוסף ערך שנקרא "widget-api-secret" Assert.assertNull (ks.getKey ("קצת אחר-api-secret")); Assert.assertNotNull (ks.getKey ("יישומון-אפי-סודי")); Assert.assertNull (ks.getCertificate ("יישומון-אפי-סוד")); }

אבל אם הסיסמה למפתח שגויה, נקבל את אותה שגיאה מוזרה שדיברנו עליה קודם:

java.security.UnrecoverableKeyException: נתון החסימה הסופית לא מרופדת כראוי

6.2. בודק אם Keystore מכיל כינוי

מאז KeyStore רק מאחסן רשומות באמצעות מַפָּה, זה חושף את היכולת לבדוק קיום מבלי לאחזר את הערך:

חלל ציבורי כאשרAddingAlias_thenCanQueryWithoutSaving () {// ... אתחל את חנות המפתח // ... הוסף ערך שנקרא "widget-api-secret"
 assertTrue (ks.containsAlias ​​("יישומון-אפי-סוד")); assertFalse (ks.containsAlias ​​("איזה אחר-סודי-אפי")); }

6.3. בדיקת סוג הכניסה

אוֹ, KeyStore# entryInstanceOf הוא קצת יותר חזק.

זה כמו מכיל עליות, אלא שהוא בודק גם את סוג הערך:

חלל ציבורי כאשרAddingAlias_thenCanQueryByType () {// ... אתחל את חנות המפתח // ... הוסף ערך סודי בשם "יישומון-אפי-סוד"
 assertTrue (ks.containsAlias ​​("יישומון-אפי-סוד")); assertFalse (ks.entryInstanceOf ("widget-api-secret", KeyType.PrivateKeyEntry.class)); }

7. מחיקת ערכים

KeyStore, כמובן,תומך במחיקת הערכים שהוספנו:

חלל ציבורי כאשר מחיקת AnAlias_thenIdempotent () {// ... אתחל חנות מפתחות // ... הוסף ערך שנקרא "יישומון-אפי-סוד"
 assertEquals (ks.size (), 1);
 ks.deleteEntry ("יישומון-אפי-סודי"); ks.deleteEntry ("איזה אחר-סודי-אפי");
 assertFalse (ks.size (), 0); }

לְמַרְבֶּה הַמַזָל, מחק רשומה הוא חסר יכולת, ולכן השיטה מגיבה אותו הדבר, בין אם הערך קיים ובין אם לאו.

8. מחיקת Keystore

אם ברצוננו למחוק את מאגר המפתחות שלנו, ה- API אינו עוזר לנו, אך אנו עדיין יכולים להשתמש ב- Java כדי לעשות זאת:

Files.delete (Paths.get (keystorePath));

לחלופין, אנו יכולים לשמור את חנות המפתחות בסביבה, ופשוט להסיר רשומות:

כינויים במניין = keyStore.aliases (); בעוד (aliases.hasMoreElements ()) {alias מחרוזת = aliases.nextElement (); keyStore.deleteEntry (כינוי); }

9. מסקנה

במאמר זה דיברנו על ניהול אישורים ומפתחות באמצעות ממשק API של KeyStore. דנו מהו חנות מפתחות, כיצד ליצור, לטעון ולמחוק אחד, כיצד לאחסן מפתח או אישור במאגר המפתחות וכיצד לטעון ולעדכן ערכים קיימים בערכים חדשים.

ניתן למצוא את היישום המלא של הדוגמה ב- Github.

תחתית Java

רק הכרזתי על החדש למד אביב קורס, המתמקד ביסודות האביב 5 ומגף האביב 2:

>> בדוק את הקורס

$config[zx-auto] not found$config[zx-overlay] not found