JavaRush /وبلاگ جاوا /Random-FA /معماری رمزنگاری جاوا: مقدمه اول
Viacheslav
مرحله

معماری رمزنگاری جاوا: مقدمه اول

در گروه منتشر شد
امنیت تبادل داده یکی از مهمترین ویژگی های برنامه های کاربردی مدرن است. از زمان های قدیم، مردم با روش های حیله گرانه ای روبرو شده اند که با پیشرفت بشر به کل علم رمزنگاری تبدیل شد. به طور طبیعی، جاوا کنار نرفت و به توسعه دهندگان معماری رمزنگاری جاوا (JCA) را پیشنهاد داد. این بررسی باید اولین ایده از نحوه کارکرد آن را ارائه دهد.

پیشگفتار

من پیشنهاد می کنم به گذشته سفر کنیم. پیش روی ما روم باستان است. و پیش از ما گایوس ژولیوس سزار است که برای فرماندهان خود پیامی می فرستد. بیایید ببینیم در این پیام چیست:
معماری رمزنگاری جاوا: مقدمه اول - 2
این چه معنی می تواند داشته باشد "ЕСКЕУГЬГМХИФЯ Е УЛП":؟ بیایید Java Online Compiler را باز کنیم، برای مثال: repl.it
class Main {
  public static void main(String[] args) {
    String code = "ЕСКЕУГЬГМХИФЯ Е УЛП";
    for (char symbol : code.toCharArray()) {
      if (symbol != ' ') {
        symbol = (char) (symbol - 3);
      }
      System.out.print(symbol);
    }
  }
}
پیش روی ما ساده ترین پیاده سازی رمز سزار است. با توجه به کار سوتونیوس مورخ رومی باستان با عنوان "زندگی دوازده سزار"، دقیقاً اینگونه بود که سزار پیام ها را برای ژنرال های خود رمزگذاری می کرد. و این یکی از قدیمی ترین ارجاعات به استفاده از چیزی به عنوان رمزنگاری است . کلمه رمزنگاری از کلمات یونانی باستان "پنهان" و "نوشتن" آمده است. این علم تکنیک های حفظ حریم خصوصی است. جاوا از رمزنگاری پشتیبانی خاص خود را دارد و به آن معماری رمز نگاری جاوا (JCA) می گویند. توضیحات را می توان در اسناد رسمی Oracle یافت - " معماری رمز نگاری جاوا (JCA) ". من به شما پیشنهاد می کنم به فرصت هایی که به لطف JCA به دست می آوریم نگاه کنید.
معماری رمزنگاری جاوا: مقدمه اول - 3

J.C.A.

همانطور که قبلاً آموختیم، جاوا معماری رمزنگاری جاوا (JCA) را برای کار با رمزنگاری ارائه می دهد. این معماری شامل یک API (یعنی مجموعه خاصی از اینترفیس ها) و ارائه دهندگان (که آنها را پیاده سازی می کنند) است:
معماری رمزنگاری جاوا: مقدمه اول - 4
همانطور که مستندات می گوید، " پلتفرم جاوا شامل تعدادی ارائه دهنده داخلی است ". یعنی پلتفرم جاوا مجموعه ای از ارائه دهندگان داخلی را فراهم می کند که در صورت لزوم می توان آنها را گسترش داد. این را خودتان می توانید ببینید:
import java.security.Provider;
import java.security.Security;
class Main {
  public static void main(String[] args) {
    Provider[] providers = Security.getProviders();
    for (Provider p : providers) {
      System.out.println(p.getName());
    }
  }
}
ثبت یک ارائه دهنده شخص ثالث بسیار آسان است. به عنوان مثال: Security.addProvider(new BouncyCastleProvider()); این مثال یکی از معروف ترین ارائه دهندگان - BouncyCastle را به هم متصل می کند . اما در این بررسی ما فقط از ابزارهای اولیه و بدون کتابخانه های شخص ثالث استفاده خواهیم کرد. سند اصلی ما: " معماری رمزنگاری جاوا (JCA) ". درک اینکه چگونه JCA کار می کند به شما کمک می کند تا فناوری هایی را که در آنها از همین JCA به طور فعال استفاده می شود درک کنید. برای مثال: HTTPS (به « از HTTP به HTTPS » مراجعه کنید.
معماری رمزنگاری جاوا: مقدمه اول - 5

MessageDigest

اولین چیزی که در مستندات JCA ذکر شده است MessageDigest است. به طور کلی، Digest در روسی یکسان خواهد بود - خلاصه به معنای "خلاصه" است. اما در رمزنگاری، خلاصه یک مجموع هش است. شما همچنین می توانید به راحتی به یاد داشته باشید که در انگلیسی Digest می تواند به عنوان digest نیز ترجمه شود. جزئیات بیشتر را می توان در مستندات JCA در بخش " MessageDigest " یافت. همانطور که مستندات می گوید، MessageDigest یک نتیجه با اندازه ثابت به نام digest یا هش ایجاد می کند. هش کردن یک تابع یک طرفه است، یعنی. اگر چیزی را هش کنیم، از نتیجه (یعنی از هش) نمی توانیم منبع اصلی را بدست آوریم. اما اگر اشیاء یکسان هش شوند (مثلاً رشته‌هایی از کاراکترهای یکسان)، پس هش آنها باید مطابقت داشته باشد. همانطور که در اسناد ذکر شده است، گاهی اوقات به چنین هش "جمع چک" یا "اثر انگشت دیجیتال" داده نیز گفته می شود. هش کردن را می توان با استفاده از الگوریتم های مختلف انجام داد. الگوریتم های موجود را می توان در سند " مستندات نام الگوریتم استاندارد معماری رمزنگاری جاوا برای JDK 8 " مشاهده کرد. بیایید هش را انجام دهیم و هش را در کنسول چاپ کنیم:
import javax.xml.bind.DatatypeConverter;
import java.security.*;
public class Main {
  public static void main(String[] args) {
    try {
      MessageDigest digester = MessageDigest.getInstance("SHA-512");
      byte[] input = "Secret string".getBytes();
      byte[] digest = digester.digest(input);
      System.out.println(DatatypeConverter.printHexBinary(digest));
    } catch (NoSuchAlgorithmException e) {
      throw new IllegalStateException(e);
    }
  }
}
هش کردن می تواند مفید باشد، برای مثال، هنگام ذخیره رمزهای عبور. از آنجایی که هش رمز عبور وارد شده را می توان با هش ذخیره شده قبلی بررسی کرد. اگر هش ها مطابقت داشته باشند، پسورد نیز مطابقت دارد. برای هش حتی ایمن تر، از مفهومی به نام نمک استفاده می شود. Salt را می توان با استفاده از کلاس SecureRandom پیاده سازی کرد . قبل از اجرای روش هضم، اجازه دهید اضافه کردن "نمک" را شرح دهیم:
byte[] salt = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(salt);
digester.update(salt);
اما هش یک تابع یک طرفه است. اما اگر بخواهید بتوانید رمزگذاری و رمزگشایی کنید چه؟
معماری رمزنگاری جاوا: مقدمه اول - 6

رمزنگاری کلید متقارن

رمزگذاری متقارن رمزگذاری است که از یک کلید برای رمزگذاری و رمزگشایی استفاده می کند. برای استفاده از رمزگذاری متقارن به یک کلید نیاز داریم. برای دریافت آن از KeyGenerator استفاده می کنیم . علاوه بر این، ما به کلاسی نیاز خواهیم داشت که نشان دهنده یک رمز ( Cipher ) باشد. همانطور که در مستندات JCA در بخش " ایجاد یک شی رمز " بیان شده است، برای ایجاد یک رمز باید نه فقط یک الگوریتم، بلکه یک "تبدیل" را در خط مشخص کنید. شرح تبدیل به این صورت است: "algorithm/mode/padding":
  • الگوریتم : در اینجا به نام های استاندارد « الگوریتم های رمزگذاری (رمزگذاری) » نگاه می کنیم. توصیه می شود از AES استفاده کنید.
  • حالت : حالت رمزگذاری. به عنوان مثال: ECB یا CBC (در این مورد کمی بعد صحبت خواهیم کرد)
  • تورفتگی / تقسیم : هر بلوک داده به طور جداگانه رمزگذاری می شود. این پارامتر تعیین می کند که چه مقدار داده به عنوان 1 بلوک محاسبه می شود.
به عنوان مثال، تبدیل زیر را در نظر بگیرید: "AES/ECB/PKCS5Padding". یعنی الگوریتم رمزگذاری AES است، حالت رمزگذاری ECB (مخفف Electronic Codebook)، اندازه بلوک PKCS5Padding است. PKCS5Padding می گوید که اندازه یک بلوک 2 بایت (16 بیت) است. حالت رمزگذاری کتاب کد الکترونیکی شامل رمزگذاری متوالی هر بلوک است:
معماری رمزنگاری جاوا: مقدمه اول - 7
ممکن است در کد شبیه به این باشد:
import javax.xml.bind.DatatypeConverter;
import javax.crypto.*;
import java.security.Key;
public class Main {
  public static void main(String[] args) throws Exception {
    String text = "secret!!secret!!secret!!secret!!";
    // Generate new key
    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    keygen.init(256);
    Key key = keygen.generateKey();
    // Encrypt with key
    String transformation = "AES/ECB/PKCS5Padding";
    Cipher cipher = Cipher.getInstance(transformation);
    cipher.init(Cipher.ENCRYPT_MODE, key);
    byte[] encrypted = cipher.doFinal(text.getBytes());
    System.out.println(DatatypeConverter.printHexBinary(encrypted));
    // Decrypt with key
    cipher.init(Cipher.DECRYPT_MODE, key);
    String result = new String(cipher.doFinal(encrypted));
    System.out.println(result);
  }
}
اگر اجرا کنیم، انتظار تکرار آن را خواهیم داشت، زیرا ما 32 کاراکتر مشخص کردیم. این کاراکترها 2 بلوک 16 بیتی را تشکیل می دهند:
معماری رمزنگاری جاوا: مقدمه اول - 8
برای جلوگیری از پخش مجدد در این مورد، باید از حالت دیگری استفاده کنید - زنجیره بلوک رمز (CBC). این حالت مفهوم Initialization Vector را معرفی می کند (که توسط کلاس IvParameterSpec ارائه می شود). و همچنین به لطف این حالت، از نتیجه تولید آخرین بلوک برای تولید بلوک بعدی استفاده می شود:
معماری رمزنگاری جاوا: مقدمه اول - 9
حالا بیایید این را با کد بنویسیم:
import javax.xml.bind.DatatypeConverter;
import javax.crypto.*;
import java.security.*;
import javax.crypto.spec.IvParameterSpec;
public class Main {
  public static void main(String[] args) throws Exception {
    // Initialization Vector
    SecureRandom random = SecureRandom.getInstanceStrong();
    byte[] rnd = new byte[16];
    random.nextBytes(rnd);
    IvParameterSpec ivSpec = new IvParameterSpec(rnd);
    // Prepare key
    KeyGenerator keygen = KeyGenerator.getInstance("AES");
    keygen.init(256);
    Key key = keygen.generateKey();
    // CBC
    String text = "secret!!secret!!secret!!secret!!";
    String transformation = "AES/CBC/PKCS5Padding";
    Cipher cipher = Cipher.getInstance(transformation);
    cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
    byte[] enc = cipher.doFinal(text.getBytes());
    System.out.println(DatatypeConverter.printHexBinary(enc));
    // Decrypt
    cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
    String result = new String(cipher.doFinal(enc));
    System.out.println(result);
  }
}
همانطور که می بینیم، در نتیجه بلوک های رمز تکراری را نمی بینیم. به همین دلیل، حالت ECB توصیه نمی شود، زیرا دیدن تکرارها و استفاده از این دانش برای رمزگشایی را ممکن می کند. برای اطلاعات بیشتر در مورد ECB و CBC، به شما توصیه می کنم مطالب را بخوانید: " حالت کتاب کد الکترونیکی ". اما رمزگذاری متقارن یک مشکل آشکار دارد - شما باید به نحوی کلید را از کسی که رمزگذاری می کند به کسی که رمزگذاری می کند منتقل کنید. و در این مسیر می توان این کلید را رهگیری کرد و سپس امکان رهگیری داده ها فراهم خواهد شد. و رمزگذاری نامتقارن برای حل این مشکل طراحی شده است.
معماری رمزنگاری جاوا: مقدمه اول - 10

رمزگذاری نامتقارن

رمزگذاری نامتقارن یا رمزنگاری با کلید عمومی یک روش رمزگذاری است که از یک جفت کلید استفاده می کند: یک کلید خصوصی (که از همه مخفی نگه داشته می شود) و یک کلید عمومی (در دسترس عموم). این جداسازی به منظور تبادل امن کلید عمومی بین طرفین مبادله اطلاعات و در عین حال ایمن نگه داشتن کلید مخفی ضروری است. هنگام ایجاد یک جفت کلید، KeyGenerator دیگر برای ما کافی نیست، ما به KeyPairGenerator نیاز داریم . بیایید به یک مثال نگاه کنیم:
import javax.crypto.*;
import java.security.*;
public class Main {
  public static void main(String[] args) throws Exception {
    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    generator.initialize(1024);
    KeyPair keyPair = generator.generateKeyPair();
    // Encrypt with PRIVATE KEY
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
    byte[] data = cipher.doFinal("Hello!".getBytes());
    // Decrypt with PUBLIC KEY
    cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
    byte[] result = cipher.doFinal(data);
    System.out.println(new String(result));
  }
}
درک این نکته مهم است که هنگام استفاده از رمزگذاری نامتقارن، ما همیشه از KeyPair برای استفاده از یک کلید برای رمزگذاری و دیگری برای رمزگشایی استفاده می کنیم. اما چون نکته رمزگذاری این است که فقط گیرنده می تواند آن را رمزگشایی کند؛ با یک کلید عمومی رمزگذاری می شود و فقط با یک کلید خصوصی رمزگشایی می شود.
معماری رمزنگاری جاوا: اولین آشنایی - 11

امضای دیجیتالی

همانطور که در بالا دیدیم، با دانستن کلید عمومی، می توانید داده ها را ارسال کنید تا فقط صاحب کلید خصوصی بتواند آن را رمزگشایی کند. یعنی ماهیت رمزگذاری نامتقارن این است که هر کسی رمزگذاری می کند، اما فقط ما می خوانیم. یک رویه معکوس نیز وجود دارد - یک امضای دیجیتال که توسط کلاس Signature نشان داده می شود . یک امضای دیجیتال می تواند از الگوریتم های زیر استفاده کند: " الگوریتم های امضا ". مستندات JCA پیشنهاد می کند که به این دو نگاه دقیق تری بیندازید: DSAwithMD5 و RSAwithMD5 چه چیزی بهتر از DSA یا RSA است و تفاوت آنها چیست می توانید در اینجا بخوانید: " کدامیک برای انتقال فایل های رمزگذاری شده بهترین کار می کند - RSA یا DSA؟ ". یا بحث های اینجا را بخوانید: " RSA در مقابل DSA برای کلیدهای احراز هویت SSH ". بنابراین، امضای دیجیتال. مانند قبل به یک KeyPair و یک کلاس Signature جدید نیاز داریم. اگر تاکنون در کامپایلرهای آنلاین تست کرده اید، ممکن است مثال زیر برای آنها تا حدودی دشوار باشد. مثال من فقط در اینجا اجرا شد: rextester.com . ما کلاس های مورد نیاز خود را وارد می کنیم:
import javax.crypto.*;
import java.security.*;
ما همچنین روش اصلی را بازنویسی می کنیم:
public static void main(String[] args) throws Exception {
    // Generate keys
    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
    SecureRandom random = SecureRandom.getInstanceStrong();
    generator.initialize(2048, random);
    KeyPair keyPair = generator.generateKeyPair();
    // Digital Signature
    Signature dsa = Signature.getInstance("SHA256withRSA");
    dsa.initSign(keyPair.getPrivate());
    // Update and sign the data
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
    byte[] data = cipher.doFinal("Hello!".getBytes());
    dsa.update(data);
    byte[] signature = dsa.sign();
    // Verify signature
    dsa.initVerify(keyPair.getPublic());
    dsa.update(data);
    boolean verifies = dsa.verify(signature);
    System.out.println("Signature is ok: " + verifies);
    // Decrypt if signature is correct
    if (verifies) {
      cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
      byte[] result = cipher.doFinal(data);
      System.out.println(new String(result));
    }
}
امضای دیجیتال اینگونه عمل می کند. امضای دیجیتال موضوع جالبی است. به شما توصیه می کنم به گزارش مربوط به این موضوع نگاه کنید:
معماری رمزنگاری جاوا: اولین آشنایی - 12
در بالا دیدیم که طرفین چگونه داده ها را مبادله می کنند. آیا رابط استانداردی برای این تعامل در JCA وجود ندارد؟ معلوم می شود که وجود دارد. بیایید به آن نگاه کنیم.
معماری رمزنگاری جاوا: اولین آشنایی - 13

توافقنامه کلیدی

معماری رمزنگاری جاوا یک ابزار مهم را معرفی می کند - توافق کلید یک پروتکل است. توسط کلاس KeyAgreement نمایش داده می شود . همانطور که در مستندات JCA بیان شد، این پروتکل به چندین طرف اجازه می دهد تا کلید رمزنگاری یکسانی را بدون به اشتراک گذاشتن هیچ گونه اطلاعات مخفی بین طرفین تنظیم کنند. عجیب به نظر میرسد؟ سپس به یک مثال نگاه می کنیم:
// 1. Одна из сторон (Алиса) генерирует пару ключей. Encoded публичный ключ отдаёт.
KeyPairGenerator generator = KeyPairGenerator.getInstance("DH");
KeyPair aliceKeyPair = generator.generateKeyPair();
byte[] alicePubKeyEncoded = aliceKeyPair.getPublic().getEncoded();

// 2. Другая сторона (например, Боб) получает открытый ключ Алисы
KeyFactory bobKeyFactory = KeyFactory.getInstance("DH");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(alicePubKeyEncoded);
PublicKey alicePubKey = bobKeyFactory.generatePublic(x509KeySpec);
// Параметры, которые использовала Алиса при генерации ключей
DHParameterSpec dhParamFromAlicePubKey = ((DHPublicKey)alicePubKey).getParams();
// Создаёт свою пару ключей. Отдаёт свой Encoded открытый ключ
KeyPairGenerator bobKpairGen = KeyPairGenerator.getInstance("DH");
bobKpairGen.initialize(dhParamFromAlicePubKey);
KeyPair bobKeyPair = bobKpairGen.generateKeyPair();
byte[] bobPubKeyEncoded = bobKeyPair.getPublic().getEncoded();

Теперь, у Алисы есть открытый ключ Боба, а у Боба есть открытый ключ Алисы. What дальше?
Как сказано в documentации JCA, у нас есть инструмент KeyAgreement, https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/CryptoSpec.html#KeyAgreement который позволяет установить одинаковые ключи шифрования без необходимости обмениваться секретной информацией (т.е. без обмена private key). Соглашение выглядит следующим образом:
// 3. Соглашение по протоколу Диффи-Хеллмана (Diffie–Hellman, DH)
KeyAgreement aliceKeyAgree = KeyAgreement.getInstance("DH");
aliceKeyAgree.init(aliceKeyPair.getPrivate());
// Алиса на основе ключа боба и своего private key создаёт общий shared ключ
KeyFactory aliceKeyFactory = KeyFactory.getInstance("DH");
x509KeySpec = new X509EncodedKeySpec(bobPubKeyEncoded);
PublicKey bobPubKey = aliceKeyFactory.generatePublic(x509KeySpec);
aliceKeyAgree.doPhase(bobPubKey, true);
byte[] aliceSharedSecret = aliceKeyAgree.generateSecret();
SecretKeySpec aliceAesKey = new SecretKeySpec(aliceSharedSecret, 0, 16, "AES");
// Боб на основе ключа Алисы и своего private key создаёт общий shared ключ
KeyAgreement bobKeyAgree = KeyAgreement.getInstance("DH");
bobKeyAgree.init(bobKeyPair.getPrivate());
bobKeyAgree.doPhase(alicePubKey, true);
byte[] bobSharedSecret = bobKeyAgree.generateSecret();
SecretKeySpec bobAesKey = new SecretKeySpec(bobSharedSecret, 0, 16, "AES");
// Общий ключ у Алисы и Боба одинаков
System.out.println("Shared keys are equals: " + Arrays.equals(aliceSharedSecret, bobSharedSecret));

Далее Боб и Алиса, используя общий ключ, про который больше никто не знает, обмениваются зашифрованными данными:
// 4. Боб шифрует сообщение для Алисы
Cipher bobCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
bobCipher.init(Cipher.ENCRYPT_MODE, bobAesKey);
byte[] ciphertext = bobCipher.doFinal("Hello, Alice!".getBytes());
// Передаёт Алисе параметры, с которыми выполнялась шифровка
byte[] encodedParamsFromBob = bobCipher.getParameters().getEncoded();

// 5. Алиса принимает сообщение и расшифровывает его
AlgorithmParameters aesParams = AlgorithmParameters.getInstance("AES");
aesParams.init(encodedParamsFromBob);
Cipher aliceCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
aliceCipher.init(Cipher.DECRYPT_MODE, aliceAesKey, aesParams);
byte[] recovered = aliceCipher.doFinal(ciphertext);
System.out.println(new String(recovered));
این مثال از مثال مستندات JCA گرفته شده است: " تبادل کلید دیفی-هلمن بین دو طرف ". این تقریباً همان چیزی است که رمزگذاری نامتقارن در معماری رمزنگاری جاوا با استفاده از پروتکل توافق کلید به نظر می رسد. برای اطلاعات بیشتر درباره رمزگذاری نامتقارن، ویدیوهای توصیه شده:
معماری رمزنگاری جاوا: اولین آشنایی - 14

گواهینامه ها

خوب ، برای دسر ما هنوز چیزی کم اهمیت نداریم - گواهی ها. به طور معمول، گواهی ها با استفاده از ابزار keytool همراه با jdk تولید می شوند. شما می توانید جزئیات بیشتر را بخوانید، به عنوان مثال، در اینجا: " تولید یک گواهی SSL خود امضا شده با استفاده از دستور کلید ابزار جاوا ". همچنین می توانید کتابچه های راهنمای Oracle را مطالعه کنید. به عنوان مثال، در اینجا: " استفاده از ابزار کلید برای ایجاد یک گواهی سرور ". به عنوان مثال، بیایید از Tutorialspoint Java Online Compiler استفاده کنیم :
import sun.security.tools.keytool.CertAndKeyGen;
import sun.security.x509.*;
import java.security.cert.*;
import java.security.*;
// Compiler args: -XDignore.symbol.file
public class Main {
  public static void main(String[] args) throws Exception {
    CertAndKeyGen certGen = new CertAndKeyGen("RSA", "SHA256WithRSA", null);
    // generate it with 2048 bits
    certGen.generate(2048);
    PrivateKey privateKey = certGen.getPrivateKey();
    X509Key publicKey = certGen.getPublicKey();
    // prepare the validity of the certificate
    long validSecs = (long) 365 * 24 * 60 * 60; // valid for one year
    // enter your details according to your application
    X500Name principal = new X500Name("CN=My Application,O=My Organisation,L=My City,C=DE");
    // add the certificate information, currently only valid for one year.
    X509Certificate cert = certGen.getSelfCertificate(principal, validSecs);
    // Public Key from Cert equals Public Key from generator
    PublicKey publicKeyFromCert = cert.getPublicKey();
    System.out.println(publicKeyFromCert.equals(publicKey));
  }
}
همانطور که می بینیم، یک گواهی توانایی ارائه یک کلید عمومی را فراهم می کند. این روش یک ایراد دارد - ما از آن استفاده می کنیم sun.securityکه خطرناک تلقی می شود، زیرا ... این بسته بخشی از API عمومی جاوا نیست. به همین دلیل است که در طول کامپایل لازم است که پارامتر - را مشخص کنید XDignore.symbol.file. راه دیگری وجود دارد - ایجاد یک گواهی به صورت دستی. نکته منفی این است که از یک API داخلی استفاده می کند که مستند نشده است. با این حال، دانستن در مورد آن مفید است. حداقل، زیرا به وضوح قابل مشاهده است که چگونه از مشخصات RFC-2459 استفاده می شود: " Internet X.509 Public Key Infrastructure ". در اینجا یک مثال است:
// 1. Генерируем пару ключей
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(4096);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// 2. Определяем данные сертификата
// Определяем срок действия сертификата
Date from = new Date();
Date to = new Date(from.getTime() + 365 * 1000L * 24L * 60L * 60L);
CertificateValidity interval = new CertificateValidity(from, to);
// Определяем subject name, т.е. Name того, с чем ассоциирован публичный ключ
// CN = Common Name. Через точку с запятой могут быть указаны также другие атрибуты
// См. https://docs.oracle.com/cd/E24191_01/common/tutorials/authz_cert_attributes.html
X500Name owner = new X500Name("cn=Unknown");
// Уникальный в пределах CA, т.е. Certificate Authority (тот, кто выдаёт сертификат) номер
BigInteger number = new BigInteger(64, new SecureRandom());
CertificateSerialNumber serialNumber = new CertificateSerialNumber(number);
// Определяем алгоритм подписи сертификата
AlgorithmId algorithmId = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
CertificateAlgorithmId certificateAlgorithmId = new CertificateAlgorithmId(algorithmId);
// 3. По подготовленной информации создаём сертификат
X509CertInfo info = new X509CertInfo();
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, serialNumber);
info.set(X509CertInfo.SUBJECT, owner);
info.set(X509CertInfo.ISSUER, owner);
info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
info.set(X509CertInfo.ALGORITHM_ID, certificateAlgorithmId);
// 4. Подписываем сертификат
X509CertImpl certificate = new X509CertImpl(info);
certificate.sign(keyPair.getPrivate(), "SHA256withRSA");
// 5. Проверка сертификата
try {
	// В случае ошибки здесь будет брошено исключение. Например: java.security.SignatureException
	certificate.verify(keyPair.getPublic());
} catch (Exception e) {
	throw new IllegalStateException(e);
}
معماری رمزنگاری جاوا: مقدمه اول - 15

فروشگاه کلید (KeyStore)

آخرین چیزی که می خواهم در مورد آن صحبت کنم، فروشگاه کلید و گواهی است که KeyStore نام دارد. واضح است که تولید مداوم گواهینامه ها و کلیدها گران و بیهوده است. بنابراین، آنها باید به نحوی ایمن ذخیره شوند. یک ابزار برای این وجود دارد - KeyStore. ذخیره کلید در مستندات JCA در فصل " KeyManagement " توضیح داده شده است. API برای کار با آن بسیار واضح است. در اینجا یک مثال کوچک وجود دارد:
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
String alias = "EntityAlias";
java.security.cert.Certificate[] chain = {certificate};
keyStore.setKeyEntry(alias, keyPair.getPrivate(), "keyPassword".toCharArray(), chain);
// Загрузка содержимого (Private Key + Certificate)
Key key = keyStore.getKey(alias, "keyPassword".toCharArray());
Certificate[] certificateChain = keyStore.getCertificateChain(alias);
// Сохранение KeyStore на диск
File file = File.createTempFile("security_", ".ks");
System.out.println(file.getAbsolutePath());
try (FileOutputStream fos = new FileOutputStream(file)) {
	keyStore.store(fos, "keyStorePassword".toCharArray());
}
همانطور که از مثال می بینید، ابتدا loadبرای KeyStore اجرا می شود. اما در مورد ما، ویژگی اول را به عنوان null مشخص کردیم، i.e. هیچ منبعی برای KeyStore وجود ندارد. این بدان معناست که KeyStore خالی ایجاد می شود تا بیشتر ذخیره شود. پارامتر دوم نیز تهی است، زیرا ما در حال ایجاد یک KeyStore جدید هستیم. اگر می‌خواهیم KeyStore را از یک فایل بارگیری کنیم، باید یک رمز عبور در اینجا تعیین کنیم (مشابه روش KeyStore به نام store).

خط پایین

بنابراین ما با شما ابتدایی ترین و ابتدایی ترین اقدامات را در چارچوب معماری رمزنگاری جاوا (معروف به JCA) بررسی کرده ایم. دیدیم که رمزگذاری متقارن و نامتقارن چیست و چگونه در JCA پیاده سازی می شود. دیدیم که گواهی ها و امضاهای دیجیتال چگونه ایجاد می شوند و چگونه از آنها استفاده می شود. اینها همه فقط اصول اولیه هستند که در پشت آنها چیزهای پیچیده و جالب تری وجود دارد. امیدوارم این مطالب مروری مفید واقع شود و شما را برای مطالعه بیشتر در این زمینه مورد توجه قرار دهد.
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION