From 17c26f91c9d2a8128c58cfe2e64cc8f5391d9b07 Mon Sep 17 00:00:00 2001 From: Hamid Reza Sharifi Date: Sun, 19 Feb 2023 17:53:38 +0330 Subject: [PATCH] Bael 5190: Verify Digital Signatures in Java article (#13439) * #BAEL-5190: add keystore and certificate files * #BAEL-5190: add keystore address * #BAEL-5190: update hashing * #BAEL-5190: delete main classes * #BAEL-5190: move keystore files to test directory * #BAEL-5190: rename to DigitalSignatureUtils * #BAEL-5190: main source code * #BAEL-5190: main test source * #BAEL-5190: update for testing * #BAEL-5190: update keystore type * #BAEL-5190: remove p12 keystores * #BAEL-5190: add jks keystores --------- Co-authored-by: h_sharifi --- .../DigitalSignatureUtils.java | 86 ++++++++++++++++++ .../com/baeldung/digitalsignature/Utils.java | 33 ------- ...tureWithMessageDigestAndCipherSigning.java | 28 ------ ...reWithMessageDigestAndCipherVerifying.java | 33 ------- .../DigitalSignatureWithSignatureSigning.java | 27 ------ ...igitalSignatureWithSignatureVerifying.java | 28 ------ .../DigitalSignatureUnitTest.java | 76 ++++++++++++++++ .../digitalsignature/receiver_keystore.jks | Bin 0 -> 785 bytes .../digitalsignature/sender_certificate.cer | 17 ++++ .../digitalsignature/sender_keystore.jks | Bin 0 -> 2072 bytes 10 files changed, 179 insertions(+), 149 deletions(-) create mode 100644 libraries-security/src/main/java/com/baeldung/digitalsignature/DigitalSignatureUtils.java delete mode 100644 libraries-security/src/main/java/com/baeldung/digitalsignature/Utils.java delete mode 100644 libraries-security/src/main/java/com/baeldung/digitalsignature/level1/DigitalSignatureWithMessageDigestAndCipherSigning.java delete mode 100644 libraries-security/src/main/java/com/baeldung/digitalsignature/level1/DigitalSignatureWithMessageDigestAndCipherVerifying.java delete mode 100644 libraries-security/src/main/java/com/baeldung/digitalsignature/level2/DigitalSignatureWithSignatureSigning.java delete mode 100644 libraries-security/src/main/java/com/baeldung/digitalsignature/level2/DigitalSignatureWithSignatureVerifying.java create mode 100644 libraries-security/src/test/java/com/baeldung/digitalsignature/DigitalSignatureUnitTest.java create mode 100644 libraries-security/src/test/resources/digitalsignature/receiver_keystore.jks create mode 100644 libraries-security/src/test/resources/digitalsignature/sender_certificate.cer create mode 100644 libraries-security/src/test/resources/digitalsignature/sender_keystore.jks diff --git a/libraries-security/src/main/java/com/baeldung/digitalsignature/DigitalSignatureUtils.java b/libraries-security/src/main/java/com/baeldung/digitalsignature/DigitalSignatureUtils.java new file mode 100644 index 0000000000..cc0da187b9 --- /dev/null +++ b/libraries-security/src/main/java/com/baeldung/digitalsignature/DigitalSignatureUtils.java @@ -0,0 +1,86 @@ +package com.baeldung.digitalsignature; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.DigestInfo; +import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; +import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder; + +import javax.crypto.Cipher; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.*; +import java.security.cert.Certificate; +import java.util.Arrays; + +public class DigitalSignatureUtils { + + public static PrivateKey getPrivateKey(String file, char[] password, String storeType, String alias) throws Exception { + KeyStore keyStore = KeyStore.getInstance(storeType); + keyStore.load(new FileInputStream(file), password); + return (PrivateKey) keyStore.getKey(alias, password); + } + + public static PublicKey getPublicKey(String file, char[] password, String storeType, String alias) throws Exception { + KeyStore keyStore = KeyStore.getInstance(storeType); + keyStore.load(new FileInputStream(file), password); + Certificate certificate = keyStore.getCertificate(alias); + return certificate.getPublicKey(); + } + + public static byte[] sign(byte[] message, String signingAlgorithm, PrivateKey signingKey) throws SecurityException { + try { + Signature signature = Signature.getInstance(signingAlgorithm); + signature.initSign(signingKey); + signature.update(message); + return signature.sign(); + } catch (GeneralSecurityException exp) { + throw new SecurityException("Error during signature generation", exp); + } + } + + public static boolean verify(byte[] messageBytes, String signingAlgorithm, PublicKey publicKey, byte[] signedData) { + try { + Signature signature = Signature.getInstance(signingAlgorithm); + signature.initVerify(publicKey); + signature.update(messageBytes); + return signature.verify(signedData); + } catch (GeneralSecurityException exp) { + throw new SecurityException("Error during verifying", exp); + } + } + + public static byte[] signWithMessageDigestAndCipher(byte[] messageBytes, String hashingAlgorithm, PrivateKey privateKey) { + try { + MessageDigest md = MessageDigest.getInstance(hashingAlgorithm); + byte[] messageHash = md.digest(messageBytes); + DigestAlgorithmIdentifierFinder hashAlgorithmFinder = new DefaultDigestAlgorithmIdentifierFinder(); + AlgorithmIdentifier hashingAlgorithmIdentifier = hashAlgorithmFinder.find(hashingAlgorithm); + DigestInfo digestInfo = new DigestInfo(hashingAlgorithmIdentifier, messageHash); + byte[] hashToEncrypt = digestInfo.getEncoded(); + + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + return cipher.doFinal(hashToEncrypt); + } catch (GeneralSecurityException | IOException exp) { + throw new SecurityException("Error during signature generation", exp); + } + } + + public static boolean verifyWithMessageDigestAndCipher(byte[] messageBytes, String hashingAlgorithm, PublicKey publicKey, byte[] encryptedMessageHash) { + try { + MessageDigest md = MessageDigest.getInstance(hashingAlgorithm); + byte[] newMessageHash = md.digest(messageBytes); + DigestAlgorithmIdentifierFinder hashAlgorithmFinder = new DefaultDigestAlgorithmIdentifierFinder(); + AlgorithmIdentifier hashingAlgorithmIdentifier = hashAlgorithmFinder.find(hashingAlgorithm); + DigestInfo digestInfo = new DigestInfo(hashingAlgorithmIdentifier, newMessageHash); + byte[] hashToEncrypt = digestInfo.getEncoded(); + + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + byte[] decryptedMessageHash = cipher.doFinal(encryptedMessageHash); + return Arrays.equals(decryptedMessageHash, hashToEncrypt); + } catch (GeneralSecurityException | IOException exp) { + throw new SecurityException("Error during verifying", exp); + } + } +} diff --git a/libraries-security/src/main/java/com/baeldung/digitalsignature/Utils.java b/libraries-security/src/main/java/com/baeldung/digitalsignature/Utils.java deleted file mode 100644 index 9f1e5808c3..0000000000 --- a/libraries-security/src/main/java/com/baeldung/digitalsignature/Utils.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.baeldung.digitalsignature; - -import java.io.FileInputStream; -import java.security.KeyStore; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.cert.Certificate; - -public class Utils { - - private static final String STORE_TYPE = "PKCS12"; - private static final char[] PASSWORD = "changeit".toCharArray(); - private static final String SENDER_KEYSTORE = "sender_keystore.p12"; - private static final String SENDER_ALIAS = "senderKeyPair"; - - public static final String SIGNING_ALGORITHM = "SHA256withRSA"; - - private static final String RECEIVER_KEYSTORE = "receiver_keystore.p12"; - private static final String RECEIVER_ALIAS = "receiverKeyPair"; - - public static PrivateKey getPrivateKey() throws Exception { - KeyStore keyStore = KeyStore.getInstance(STORE_TYPE); - keyStore.load(new FileInputStream(SENDER_KEYSTORE), PASSWORD); - return (PrivateKey) keyStore.getKey(SENDER_ALIAS, PASSWORD); - } - - public static PublicKey getPublicKey() throws Exception { - KeyStore keyStore = KeyStore.getInstance(STORE_TYPE); - keyStore.load(new FileInputStream(RECEIVER_KEYSTORE), PASSWORD); - Certificate certificate = keyStore.getCertificate(RECEIVER_ALIAS); - return certificate.getPublicKey(); - } -} diff --git a/libraries-security/src/main/java/com/baeldung/digitalsignature/level1/DigitalSignatureWithMessageDigestAndCipherSigning.java b/libraries-security/src/main/java/com/baeldung/digitalsignature/level1/DigitalSignatureWithMessageDigestAndCipherSigning.java deleted file mode 100644 index 78d40dd365..0000000000 --- a/libraries-security/src/main/java/com/baeldung/digitalsignature/level1/DigitalSignatureWithMessageDigestAndCipherSigning.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.baeldung.digitalsignature.level1; - -import com.baeldung.digitalsignature.Utils; - -import javax.crypto.Cipher; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.MessageDigest; -import java.security.PrivateKey; - -public class DigitalSignatureWithMessageDigestAndCipherSigning { - - public static void main(String[] args) throws Exception { - - PrivateKey privateKey = Utils.getPrivateKey(); - - byte[] messageBytes = Files.readAllBytes(Paths.get("src/test/resources/digitalsignature/message.txt")); - - MessageDigest md = MessageDigest.getInstance("SHA-256"); - byte[] messageHash = md.digest(messageBytes); - - Cipher cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.ENCRYPT_MODE, privateKey); - byte[] digitalSignature = cipher.doFinal(messageHash); - - Files.write(Paths.get("target/digital_signature_1"), digitalSignature); - } -} diff --git a/libraries-security/src/main/java/com/baeldung/digitalsignature/level1/DigitalSignatureWithMessageDigestAndCipherVerifying.java b/libraries-security/src/main/java/com/baeldung/digitalsignature/level1/DigitalSignatureWithMessageDigestAndCipherVerifying.java deleted file mode 100644 index 0b242a44fb..0000000000 --- a/libraries-security/src/main/java/com/baeldung/digitalsignature/level1/DigitalSignatureWithMessageDigestAndCipherVerifying.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.baeldung.digitalsignature.level1; - -import com.baeldung.digitalsignature.Utils; - -import javax.crypto.Cipher; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.MessageDigest; -import java.security.PublicKey; -import java.util.Arrays; - -public class DigitalSignatureWithMessageDigestAndCipherVerifying { - - public static void main(String[] args) throws Exception { - - PublicKey publicKey = Utils.getPublicKey(); - - byte[] messageBytes = Files.readAllBytes(Paths.get("src/test/resources/digitalsignature/message.txt")); - - MessageDigest md = MessageDigest.getInstance("SHA-256"); - byte[] newMessageHash = md.digest(messageBytes); - - byte[] encryptedMessageHash = Files.readAllBytes(Paths.get("target/digital_signature_1")); - - Cipher cipher = Cipher.getInstance("RSA"); - cipher.init(Cipher.DECRYPT_MODE, publicKey); - byte[] decryptedMessageHash = cipher.doFinal(encryptedMessageHash); - - boolean isCorrect = Arrays.equals(decryptedMessageHash, newMessageHash); - System.out.println("Signature " + (isCorrect ? "correct" : "incorrect")); - } - -} diff --git a/libraries-security/src/main/java/com/baeldung/digitalsignature/level2/DigitalSignatureWithSignatureSigning.java b/libraries-security/src/main/java/com/baeldung/digitalsignature/level2/DigitalSignatureWithSignatureSigning.java deleted file mode 100644 index afc8fd1045..0000000000 --- a/libraries-security/src/main/java/com/baeldung/digitalsignature/level2/DigitalSignatureWithSignatureSigning.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.baeldung.digitalsignature.level2; - -import com.baeldung.digitalsignature.Utils; - -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.PrivateKey; -import java.security.Signature; - -public class DigitalSignatureWithSignatureSigning { - - public static void main(String[] args) throws Exception { - - PrivateKey privateKey = Utils.getPrivateKey(); - - Signature signature = Signature.getInstance(Utils.SIGNING_ALGORITHM); - signature.initSign(privateKey); - - byte[] messageBytes = Files.readAllBytes(Paths.get("src/test/resources/digitalsignature/message.txt")); - - signature.update(messageBytes); - byte[] digitalSignature = signature.sign(); - - Files.write(Paths.get("target/digital_signature_2"), digitalSignature); - } - -} diff --git a/libraries-security/src/main/java/com/baeldung/digitalsignature/level2/DigitalSignatureWithSignatureVerifying.java b/libraries-security/src/main/java/com/baeldung/digitalsignature/level2/DigitalSignatureWithSignatureVerifying.java deleted file mode 100644 index ee34be989c..0000000000 --- a/libraries-security/src/main/java/com/baeldung/digitalsignature/level2/DigitalSignatureWithSignatureVerifying.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.baeldung.digitalsignature.level2; - -import com.baeldung.digitalsignature.Utils; - -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.PublicKey; -import java.security.Signature; - -public class DigitalSignatureWithSignatureVerifying { - - public static void main(String[] args) throws Exception { - - PublicKey publicKey = Utils.getPublicKey(); - - byte[] sig = Files.readAllBytes(Paths.get("target/digital_signature_2")); - - Signature signature = Signature.getInstance(Utils.SIGNING_ALGORITHM); - signature.initVerify(publicKey); - - byte[] messageBytes = Files.readAllBytes(Paths.get("src/test/resources/digitalsignature/message.txt")); - - signature.update(messageBytes); - - boolean isCorrect = signature.verify(sig); - System.out.println("Signature " + (isCorrect ? "correct" : "incorrect")); - } -} diff --git a/libraries-security/src/test/java/com/baeldung/digitalsignature/DigitalSignatureUnitTest.java b/libraries-security/src/test/java/com/baeldung/digitalsignature/DigitalSignatureUnitTest.java new file mode 100644 index 0000000000..65058dca52 --- /dev/null +++ b/libraries-security/src/test/java/com/baeldung/digitalsignature/DigitalSignatureUnitTest.java @@ -0,0 +1,76 @@ +package com.baeldung.digitalsignature; + +import org.junit.Test; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.PrivateKey; +import java.security.PublicKey; + +import static org.junit.Assert.assertTrue; + +public class DigitalSignatureUnitTest { + + String messagePath = "src/test/resources/digitalsignature/message.txt"; + String senderKeyStore = "src/test/resources/digitalsignature/sender_keystore.jks"; + String receiverKeyStore = "src/test/resources/digitalsignature/receiver_keystore.jks"; + String storeType = "JKS"; + String senderAlias = "senderKeyPair"; + String receiverAlias = "receiverKeyPair"; + char[] password = "changeit".toCharArray(); + String signingAlgorithm = "SHA256withRSA"; + String hashingAlgorithm = "SHA-256"; + + @Test + public void givenMessageData_whenSignWithSignatureSigning_thenVerify() throws Exception { + PrivateKey privateKey = DigitalSignatureUtils.getPrivateKey(senderKeyStore, password, storeType, senderAlias); + byte[] messageBytes = Files.readAllBytes(Paths.get(messagePath)); + + byte[] digitalSignature = DigitalSignatureUtils.sign(messageBytes, signingAlgorithm, privateKey); + + PublicKey publicKey = DigitalSignatureUtils.getPublicKey(receiverKeyStore, password, storeType, receiverAlias); + boolean isCorrect = DigitalSignatureUtils.verify(messageBytes, signingAlgorithm, publicKey, digitalSignature); + + assertTrue(isCorrect); + } + + @Test + public void givenMessageData_whenSignWithMessageDigestAndCipher_thenVerify() throws Exception { + PrivateKey privateKey = DigitalSignatureUtils.getPrivateKey(senderKeyStore, password, storeType, senderAlias); + byte[] messageBytes = Files.readAllBytes(Paths.get(messagePath)); + + byte[] encryptedMessageHash = DigitalSignatureUtils.signWithMessageDigestAndCipher(messageBytes, hashingAlgorithm, privateKey); + + PublicKey publicKey = DigitalSignatureUtils.getPublicKey(receiverKeyStore, password, storeType, receiverAlias); + boolean isCorrect = DigitalSignatureUtils.verifyWithMessageDigestAndCipher(messageBytes, hashingAlgorithm, publicKey, encryptedMessageHash); + + assertTrue(isCorrect); + } + + @Test + public void givenMessageData_whenSignWithSignatureSigning_thenVerifyWithMessageDigestAndCipher() throws Exception { + PrivateKey privateKey = DigitalSignatureUtils.getPrivateKey(senderKeyStore, password, storeType, senderAlias); + byte[] messageBytes = Files.readAllBytes(Paths.get(messagePath)); + + byte[] digitalSignature = DigitalSignatureUtils.sign(messageBytes, signingAlgorithm, privateKey); + + PublicKey publicKey = DigitalSignatureUtils.getPublicKey(receiverKeyStore, password, storeType, receiverAlias); + boolean isCorrect = DigitalSignatureUtils.verifyWithMessageDigestAndCipher(messageBytes, hashingAlgorithm, publicKey, digitalSignature); + + assertTrue(isCorrect); + } + + @Test + public void givenMessageData_whenSignWithMessageDigestAndCipher_thenVerifyWithSignature() throws Exception { + PrivateKey privateKey = DigitalSignatureUtils.getPrivateKey(senderKeyStore, password, storeType, senderAlias); + byte[] messageBytes = Files.readAllBytes(Paths.get(messagePath)); + + byte[] encryptedMessageHash = DigitalSignatureUtils.signWithMessageDigestAndCipher(messageBytes, hashingAlgorithm, privateKey); + + PublicKey publicKey = DigitalSignatureUtils.getPublicKey(receiverKeyStore, password, storeType, receiverAlias); + boolean isCorrect = DigitalSignatureUtils.verify(messageBytes, signingAlgorithm, publicKey, encryptedMessageHash); + + assertTrue(isCorrect); + } + +} diff --git a/libraries-security/src/test/resources/digitalsignature/receiver_keystore.jks b/libraries-security/src/test/resources/digitalsignature/receiver_keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..184b5b8600adc83b8e40ccd9f91ba17045004a9b GIT binary patch literal 785 zcmezO_TO6u1_mY|W(3pxMXAZDnPsU(*{PKUiJ3(}@wN;f#ySSp2t88+OQ6z|22D&y z4VoC&E?{P2WMX2;3ftCXz{|#|)#lOmotKf3o0Y*p*ig`bpN%<`g;|)xDKRxCr8FLhAedPAsq7C0_d7+T*5}p^f>wqx1e}7~baHz@YW>|L>kD zbInrlHW4XSpI&&l6Zw+imPuO4SC#9F9zK+{?Fn;SXRdP0%C5>&`Fdr<>QB|laR;l#zCC zu54#wW@KPotY{!_APWp{Sw0pq77@p&4e1M1&4TY$IxjnX-eLE-<(F?D2O=VfiIljs!1(Pjg(an_ZuG_h*drj+Ae&J|2FTWM39vZtI)guF`nSx-7y_?_P$8 z_mc`IzlGbA40L9%pIx~=hV}csbjIxhoF%)r@8Rg1*uB>3$qS3k?$*g=o(l7nUY?5% zTYPW%;l+0vLjrgV&%duMPP+c}_~~|;;~(x$Jez%N!G!FY$#IU}U17nDA)>$Lf0T(z znDjS{&z)E8!2JBOtGn0fuNxQ11W$1pzRE z1pP1$1_~<%0R#am0uccL1pows1nH#M%}(`SxrW@K;Iqy^j*%zT*uBod0)-sit*PE# z{c3x!vMP18A?!HPeoD~c6&pfBp?jTnIMf1wP*qa#3%qJW8zNY5sw6Z$s^PwS^^^$Bui1y)!SMtGrQ=*u z1R^)5VPi>Gh30`MHVj%i4~d6EZARIgGITFF_b@v4tCfF(xIiQP50TnQxYL=0rVxjZ znJi!ht=&e;SG6@Q!+;0KcB`FXWy4Xc$Vii>Xye%>Kx(41D!XYQpq)Ab`Bg8F z)Y)wL=6m;ZNL)c3L&WL0J`~3g8_=!4NjA%(eR)iHp#GeD{=>E_C`uT}tb;0k&r|tW z@ZBL`LYG`ntkG~ShR(=`IC2)L8RsBte zlx$rdI~`f@E!;`^n@06nqf@JzpRB%KE^y$W?Ib~<1|?zoGk?G;i>nzMcF{T?@zz#m z$F!hO_dW-em9nN^+BAoitdSn-Q%C>Oud|UOya#E_#X6^Q=X@cp1hHR!F7J2Yai*m% zw>nNW(;?jSlRu1LnTz9#tGX?B|Hta+_Ws#1kI-jgPFyastr!XbeKe?WF`ZLHPVh|S zlIl_#mcBKku$is}nd&?D!6RqG55dm~Tw+LizO!0kob#yIxr3K(I<) z`C7KZKC0ilO%jGwG`S7xL)rdI&ijYfUz!V}@$%Kq`=#?&5fbsX)^QJ_Z{KZnu-))w zFk)v&LNQUHXL zinl@8Hnf`s*J+uC!rJg=^t`2aW_kfsGIjy?WJ&kP_=$+(y>TVO2*n}J7)7q(LMJLQ z_O%~yRJ1$@rm0smSjbAgQ(yrtbAZ%*j0Ws1T}Q!#9mpt>}25+}b- z$ftR_7j^eLNZA%KJU8yep8seu+YPV)D*6BWjFg?ukMGQPn@2^|#2r{as}orf`kqs8 z+8?x$Gauh@q+cM$)s1Sf6iPCKqHA3ni)}&ehklfrG-h=5B-f(h;T=7URza>eB*r>E zdPyVKd04CRdt+X~d(Nb`sqtiYb_M#Mrqs6r0|5X5qaiRKFdYU1RUHll76cSQTCiuJ zB{oyuc|)ed&p^A*r_hZ(wVn23YcRfvKha`ckZYM^UO`KWR#O2~75bm?99m$K{Z!Z+2th8BO6D2nI_^z*cl6 zA=~p>#*LO!R)3)ruCd&BC*>-`u6o;JI5t!QsG0Sl9ra@a7;^1r4|vSuSRf^!+Ktl3 z-^JUk2X-;%`V2wYo;eQpbgUCD2zsz2Lh;J_A9GPOm_iK;!OR@NDoZSZt9V-idlWB6 C3dLFg literal 0 HcmV?d00001