我们相信加密! 教程
許多人認為加密是一個復雜的主題,這很難理解。 雖然可以實現它的某些方面,但是每個人都可以理解它在更高層次上的工作方式。
這就是我要處理的這篇文章。 用簡單的術語解釋它是如何工作的,然后使用一些代碼。
是的,我們信任加密。 信任是什么意思? 我們相信,我們的消息僅由授權方讀取(機密性),在傳輸過程中不會更改(完整性),并且確實由我們認為已發送(驗證)的人發送。
維基百科為加密提供了一個很好的定義:“是以一種僅授權方可以訪問的方式對消息或信息進行編碼的過程”。
因此,加密通過使用密鑰(密碼)將我們的信息轉換為難以理解的密鑰(密文),該密鑰只??能從授權方轉回原始信息。
加密方案有兩種類型, 對稱和非對稱密鑰加密。
在對稱加密中,相同的密鑰用于加密和解密消息。 我們希望訪問該消息的用戶必須具有密鑰,但沒有其他密鑰,否則我們的消息將受到威脅。
我對非對稱密鑰加密很感興趣。 非對稱密鑰方案使用兩個密鑰:私有密鑰和公共密鑰。 這些鍵對很特殊。 它們之所以特別是因為它們是使用稱為非對稱算法的一類算法生成的。 實際的算法不在此討論范圍之內,但是在本教程的后面,我們將使用RSA。
您現在需要知道的是,這些鍵具有以下屬性。 使用以下內容加密的消息:
看起來足夠簡單吧? 那么它在實踐中如何使用? 讓我們考慮兩個朋友,愛麗絲和鮑勃。 他們有自己的一對公鑰和私鑰,并且希望在聊天中保持私密性。 他們每個人都公開提供他們的公鑰,但是要小心地隱藏他們的私鑰。
當愛麗絲(Alice)要發送僅從鮑勃(Bob)讀取的消息時,她使用鮑勃(Bob)的公共密鑰對消息進行加密。 然后Bob和只有他才能使用他的私鑰解密消息。 而已。
這就解釋了第一個屬性的使用,但是第二個呢? 似乎沒有理由使用我們的私鑰進行加密。 好吧,有。 我們怎么知道愛麗絲是那個發信息的人? 如果我們可以使用Alice的公鑰解密消息,則可以確保使用Alice的私鑰進行加密,因此確實是從Alice發送的。 簡單的說:
使用公鑰,以便人們只能將內容發送給您,而私鑰則用于證明您的身份。
因此,我們可以使用公鑰來確保機密性 ,并使用私鑰來保證真實性 。 那么誠信呢? 為了實現這一點,我們使用加密哈希。 良好的加密散列可接收輸入消息并生成具有以下屬性的消息摘要:
如果我們想確保接收到的消息在過渡期間沒有受到破壞,則將哈希值與加密消息一起發送。 在接收端,我們使用相同的算法對解密后的消息進行哈希處理,然后進行比較以確保哈希值完全匹配。 如果它們是,那么我們可以確信消息沒有被更改。
這些散列或消息摘要也具有其他用途。 您會看到,有時Bob做出承諾,然后否認自己曾經做過。 我們想讓他檢查。 用奇特的術語來說,這是不可否認的 ,它可以防止各方拒絕發送消息。 數字簽名是眾所周知的應用。
在繼續學習并享受代碼樂趣之前,讓我再說幾件事。
現在,讓我們使用一個教程來幫助所有這些問題。我們將允許三個人愛麗絲,鮑勃和保羅與機密性,完整性和身份驗證進行通信(進一步將它們稱為CIA)。 完整的代碼可在github上找到 。
該項目有幾個依賴項,如下所示:
我們將從EncryptedMessage類開始,該類將提供我們確保CIA所需的所有信息。 該消息將包含用于機密性的實際加密消息,該消息的哈希值將用于確保發送者的完整性和身份驗證,原始消息和加密消息則用于身份驗證。 我們還提供了一種破壞消息有效負載的方法,因此我們可以針對摘要測試驗證(稍后會進行介紹)。
package com.tasosmartidis.tutorial.encryption.domain;import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; import lombok.Getter;@AllArgsConstructor @Getter @EqualsAndHashCode public class EncryptedMessage {private String encryptedMessagePayload;private String senderId;private String encryptedSenderId;private String messageDigest;// FOR DEMO PURPOSES ONLY!public void compromiseEncryptedMessagePayload(String message) {this.encryptedMessagePayload = message;}@Overridepublic String toString() {return encryptedMessagePayload;} }現在讓我們進入加密部分。 我們將創建一個獨立于實際非對稱算法和密鑰長度的基本加密器類。 它將創建密鑰和密碼,具有用于加密和解密文本以及提供對密鑰的訪問的方法。 看起來像這樣:
package com.tasosmartidis.tutorial.encryption.encryptor;import com.tasosmartidis.tutorial.encryption.domain.EncryptorProperties; import com.tasosmartidis.tutorial.encryption.exception.DecryptionException; import com.tasosmartidis.tutorial.encryption.exception.EncryptionException; import com.tasosmartidis.tutorial.encryption.exception.EncryptorInitializationException; import com.tasosmartidis.tutorial.encryption.exception.UnauthorizedForDecryptionException; import org.apache.commons.codec.binary.Base64;import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.nio.charset.StandardCharsets; import java.security.*;public class BaseAsymmetricEncryptor {private final KeyPairGenerator keyPairGenerator;private final KeyPair keyPair;private final Cipher cipher;private final EncryptorProperties encryptorProperties;protected BaseAsymmetricEncryptor(EncryptorProperties encryptorProperties) {this.encryptorProperties = encryptorProperties;this.keyPairGenerator = generateKeyPair();this.keyPairGenerator.initialize(this.encryptorProperties.getKeyLength());this.keyPair = this.keyPairGenerator.generateKeyPair();this.cipher = createCipher(encryptorProperties);}protected PrivateKey getPrivateKey() {return this.keyPair.getPrivate();}public PublicKey getPublicKey() {return this.keyPair.getPublic();}protected String encryptText(String textToEncrypt, Key key) {try {this.cipher.init(Cipher.ENCRYPT_MODE, key);return Base64.encodeBase64String(cipher.doFinal(textToEncrypt.getBytes(StandardCharsets.UTF_8)));} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException ex) {throw new EncryptionException("Encryption of message failed", ex);}}protected String decryptText(String textToDecrypt, Key key) {try {this.cipher.init(Cipher.DECRYPT_MODE, key);return new String(cipher.doFinal(Base64.decodeBase64(textToDecrypt)), StandardCharsets.UTF_8);}catch (InvalidKeyException | BadPaddingException ex){throw new UnauthorizedForDecryptionException("Not authorized to decrypt message", ex);} catch (IllegalBlockSizeException ex) {throw new DecryptionException("Decryption of message failed", ex);}}private Cipher createCipher(EncryptorProperties encryptorProperties) {try {return Cipher.getInstance(encryptorProperties.getAsymmetricAlgorithm());} catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {throw new EncryptorInitializationException("Creation of cipher failed", ex);}}private KeyPairGenerator generateKeyPair() {try {return KeyPairGenerator.getInstance(this.encryptorProperties.getAsymmetricAlgorithm());} catch (NoSuchAlgorithmException ex) {throw new EncryptorInitializationException("Creation of encryption keypair failed", ex);}}}為了實現我們的功能,我們需要處理許多異常,但是由于萬一發生它們,我們將不對其進行任何處理,因此我們將它們包裝在語義上有意義的運行時異常中。 我不會在這里展示異常類,因為它們只有一個構造函數。 但是您可以在github.com.tasosmartidis.tutorial.encryption.exception包下的項目中檢出它們。
您將在代碼的不同部分看到它們的實際用法。 BaseAsymmetricEncryptor的構造BaseAsymmetricEncryptor將EncryptorProperites實例作為參數。
package com.tasosmartidis.tutorial.encryption.domain;import lombok.AllArgsConstructor;@AllArgsConstructor public class EncryptorProperties {private final AsymmetricAlgorithm asymmetricAlgorithm;private final int keyLength;public String getAsymmetricAlgorithm() {return asymmetricAlgorithm.toString();}public int getKeyLength() {return keyLength;} }我們將創建一個基于RSA的加密器實現。 該代碼應該說明一切:
package com.tasosmartidis.tutorial.encryption.encryptor;import com.tasosmartidis.tutorial.encryption.domain.AsymmetricAlgorithm; import com.tasosmartidis.tutorial.encryption.domain.EncryptedMessage; import com.tasosmartidis.tutorial.encryption.domain.EncryptorProperties; import org.bouncycastle.jcajce.provider.digest.SHA3; import org.bouncycastle.util.encoders.Hex;import java.security.PublicKey;public class RsaEncryptor extends BaseAsymmetricEncryptor {private static final int KEY_LENGTH = 2048;public RsaEncryptor() {super(new EncryptorProperties(AsymmetricAlgorithm.RSA, KEY_LENGTH));}public String encryptMessageForPublicKeyOwner(String message, PublicKey key) {return super.encryptText(message, key);}public String encryptMessageWithPrivateKey(String message) {return super.encryptText(message, super.getPrivateKey());}public String decryptReceivedMessage(EncryptedMessage message) {return super.decryptText(message.getEncryptedMessagePayload(), super.getPrivateKey());}public String decryptMessageFromOwnerOfPublicKey(String message, PublicKey publicKey) {return super.decryptText(message, publicKey);}public String hashMessage(String message) {SHA3.DigestSHA3 digestSHA3 = new SHA3.Digest512();byte[] messageDigest = digestSHA3.digest(message.getBytes());return Hex.toHexString(messageDigest);} }對于我們的演示,我們將需要演員,彼此交流信息的人。 每個人都將具有唯一的身份,姓名以及與之通信的受信任聯系人的列表。
package com.tasosmartidis.tutorial.encryption.demo;import com.tasosmartidis.tutorial.encryption.domain.EncryptedMessage; import com.tasosmartidis.tutorial.encryption.message.RsaMessenger; import lombok.EqualsAndHashCode;import java.security.PublicKey; import java.util.HashSet; import java.util.Set; import java.util.UUID;@EqualsAndHashCode public class Person {private final String id;private final String name;private final Set<Person> trustedContacts;private final RsaMessenger rsaMessenger;public Person(String name) {this.id = UUID.randomUUID().toString();this.name = name;this.trustedContacts = new HashSet<>();this.rsaMessenger = new RsaMessenger(this.trustedContacts, this.id);}public PublicKey getPublicKey() {return this.rsaMessenger.getPublicKey();}public String getName() {return name;}public String getId() {return id;}public void addTrustedContact(Person newContact) {if(trustedContacts.contains(newContact)) {return;}trustedContacts.add(newContact);}public EncryptedMessage sendEncryptedMessageToPerson(String message, Person person) {return this.rsaMessenger.encryptMessageForPerson(message, person);}public void readEncryptedMessage(EncryptedMessage encryptedMessage) {this.rsaMessenger.readEncryptedMessage(encryptedMessage);}}接下來,讓我們創建一個RsaMessanger類,該類將允許人們使用RsaEncryptor發送加密的消息。 發送加密的消息時,我們將提供所有必要的信息,以確保機密性,完整性和身份驗證。 閱讀時,我們將解密消息,我們將嘗試驗證消息是由受信任的聯系人發送的,并確保消息沒有受到破壞或更改。
package com.tasosmartidis.tutorial.encryption.message;import com.tasosmartidis.tutorial.encryption.demo.Person; import com.tasosmartidis.tutorial.encryption.domain.EncryptedMessage; import com.tasosmartidis.tutorial.encryption.encryptor.RsaEncryptor; import com.tasosmartidis.tutorial.encryption.exception.PayloadAndDigestMismatchException;import java.security.PublicKey; import java.util.Optional; import java.util.Set;public class RsaMessenger {private final RsaEncryptor encryptionHandler;private final Set<Person> trustedContacts;private final String personId;public RsaMessenger(Set<Person> trustedContacts, String personId) {this.encryptionHandler = new RsaEncryptor();this.trustedContacts = trustedContacts;this.personId = personId;}public PublicKey getPublicKey() {return this.encryptionHandler.getPublicKey();}public EncryptedMessage encryptMessageForPerson(String message, Person person) {String encryptedMessage = this.encryptionHandler.encryptMessageForPublicKeyOwner(message, person.getPublicKey());String myEncryptedId = this.encryptionHandler.encryptMessageWithPrivateKey(this.personId);String hashedMessage = this.encryptionHandler.hashMessage(message);return new EncryptedMessage(encryptedMessage, this.personId, myEncryptedId, hashedMessage);}public void readEncryptedMessage(EncryptedMessage message) {String decryptedMessage = this.encryptionHandler.decryptReceivedMessage(message);Optional<Person> sender = tryIdentifyMessageSender(message.getSenderId());if(!decryptedMessageHashIsValid(decryptedMessage, message.getMessageDigest())) {throw new PayloadAndDigestMismatchException("Message digest sent does not match the one generated from the received message");}if(sender.isPresent() && senderSignatureIsValid(sender.get(), message.getEncryptedSenderId())) {System.out.println(sender.get().getName() +" send message: " + decryptedMessage);}else {System.out.println("Unknown source send message: " + decryptedMessage);}}private boolean senderSignatureIsValid(Person sender, String encryptedSenderId) {if(rawSenderIdMatchesDecryptedSenderId(sender, encryptedSenderId)) {return true;}return false;}private boolean rawSenderIdMatchesDecryptedSenderId(Person sender, String encryptedSenderId) {return sender.getId().equals(this.encryptionHandler.decryptMessageFromOwnerOfPublicKey(encryptedSenderId, sender.getPublicKey()));}private Optional<Person> tryIdentifyMessageSender(String id) {return this.trustedContacts.stream().filter(contact -> contact.getId().equals(id)).findFirst();}private boolean decryptedMessageHashIsValid(String decryptedMessage, String hashedMessage) {String decryptedMessageHashed = this.encryptionHandler.hashMessage(decryptedMessage);if(decryptedMessageHashed.equals(hashedMessage)) {return true;}return false;} }好的! 現在是演示時間!
我們將創建一些測試以確保一切正常。 我們要測試的方案是:
當愛麗絲(鮑勃的受信任聯系人)向他發送加密的消息時,鮑勃可以解密該消息,并且知道來自鮑勃。 還要確保有效載荷沒有被更改。
從Alice到Bob的相同消息不可用于Paul解密,并且將引發UnauthorizedForDecryptionException 。 當保羅(鮑勃不知道)發送加密消息時,鮑勃將能夠讀取該消息,但不知道是誰發送了該消息。 最后,當我們危及加密消息的有效負載時,帶有消息摘要的驗證將識別出該消息并引發異常。
運行測試將產生以下結果:
就是這樣! 再次感謝您的閱讀,您可以在github上找到代碼。
翻譯自: https://www.javacodegeeks.com/2017/11/encryption-trust-tutorial.html
總結
以上是生活随笔為你收集整理的我们相信加密! 教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 威马汽车“借壳”
- 下一篇: AOC 爱攻发布 16G3 便携显示器: