This commit is contained in:
petre.rosioru 2025-03-10 18:09:56 +02:00
parent 430a04837f
commit 8d227b26d3
4 changed files with 233 additions and 110 deletions

View file

@ -45,19 +45,20 @@ public class StartupValidator implements ApplicationContextInitializer<Configura
try { try {
final var message = properties.getProperty("index") + ":" + Instant.now() final var message = properties.getProperty("index") + ":" + Instant.now()
.toEpochMilli() * 1_000_000; .toEpochMilli() * 1_000_000;
final var encryptedData = KeyUtils.encryptDataWithAESGCM(message, KeyUtils.generateSharedSecret( final var encryptedData = KeyUtils.encryptDataWithAESGCM(message, KeyUtils.generateSharedSecret(
KeyUtils.stringToPrivateKey(env.getProperty("my-private-key")), KeyUtils.stringToPrivateKey(env.getProperty("my-private-key")),
KeyUtils.stringToPublicKey(env.getProperty("license-server-public-key")))); KeyUtils.stringToPublicKey(env.getProperty("license-server-public-key"))));
final var encrypted = Base64.getUrlEncoder() final var encrypted = Base64.getUrlEncoder()
.encodeToString(encryptedData); .encodeToString(encryptedData);
final var signature = KeyUtils.signMessage(message, final var signature = KeyUtils.signMessage(message,
KeyUtils.stringToPrivateKey(env.getProperty("my-private-key"))); KeyUtils.stringToPrivateKey(env.getProperty("my-private-key")));
responseCode = LicenseUtils.request(env.getProperty("license-server-endpoint"), resource.getInputStream(), responseCode = LicenseUtils.request(env.getProperty("license-server-endpoint"), resource.getInputStream(),
env.getProperty("license-server-trust-password"), env.getProperty("license-server-trust-password"),
env.getProperty("my-id") + "." + encrypted + "." + signature); Base64.getUrlEncoder()
.encodeToString(env.getProperty("my-id").getBytes()) + "." + encrypted + "." + signature);
} catch (IOException e) { } catch (IOException e) {
throw new IllegalStateException("IO exception for trust store!", e); throw new IllegalStateException("IO exception for trust store!", e);
} }
@ -86,12 +87,14 @@ public class StartupValidator implements ApplicationContextInitializer<Configura
properties.setProperty("index", ""); properties.setProperty("index", "");
saveProperties(properties, filePath); saveProperties(properties, filePath);
} }
private void saveProperties(Properties properties, private void saveProperties(Properties properties,
String filePath) throws IOException { String filePath) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filePath)) { try (FileOutputStream fos = new FileOutputStream(filePath)) {
properties.store(fos, "Updated properties file"); properties.store(fos, "Updated properties file");
} }
} }
private void loadProperties(Properties properties, private void loadProperties(Properties properties,
String filePath) throws IOException { String filePath) throws IOException {
try (FileInputStream fis = new FileInputStream(filePath)) { try (FileInputStream fis = new FileInputStream(filePath)) {

View file

@ -69,7 +69,7 @@ public final class KeyUtils {
// Print Results // Print Results
System.out.println("Original Message: " + originalMessage); System.out.println("Original Message: " + originalMessage);
System.out.println("Encrypted (Base64): " + Base64.getUrlEncoder() System.out.println("Encrypted (Base64): " + Base64.getUrlEncoder()
.encodeToString(encryptedData)); .encodeToString(encryptedData));
System.out.println("Decrypted Message: " + decryptedMessage); System.out.println("Decrypted Message: " + decryptedMessage);
String signature = signMessage(originalMessage, stringToPrivateKey(senderPrivateKeyToString)); String signature = signMessage(originalMessage, stringToPrivateKey(senderPrivateKeyToString));
@ -143,38 +143,53 @@ public final class KeyUtils {
} }
// Decrypt data using AES-GCM // Decrypt data using AES-GCM
private static String decryptDataWithAESGCM(byte[] encryptedDataWithIv, public static String decryptDataWithAESGCM(byte[] encryptedDataWithIv,
SecretKey sharedSecret) throws Exception { SecretKey sharedSecret) {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Extract IV try {
byte[] iv = new byte[12]; Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
System.arraycopy(encryptedDataWithIv, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[encryptedDataWithIv.length - iv.length];
System.arraycopy(encryptedDataWithIv, iv.length, encryptedData, 0, encryptedData.length);
// Decrypt // Extract IV
GCMParameterSpec spec = new GCMParameterSpec(128, iv); byte[] iv = new byte[12];
cipher.init(Cipher.DECRYPT_MODE, sharedSecret, spec); System.arraycopy(encryptedDataWithIv, 0, iv, 0, iv.length);
byte[] decryptedData = cipher.doFinal(encryptedData); byte[] encryptedData = new byte[encryptedDataWithIv.length - iv.length];
System.arraycopy(encryptedDataWithIv, iv.length, encryptedData, 0, encryptedData.length);
return new String(decryptedData); // Decrypt
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, sharedSecret, spec);
byte[] decryptedData = cipher.doFinal(encryptedData);
return new String(decryptedData);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException!", e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException("NoSuchPaddingException!", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("InvalidKeyException!", e);
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException("InvalidAlgorithmParameterException!", e);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException("IllegalBlockSizeException!", e);
} catch (BadPaddingException e) {
throw new RuntimeException("BadPaddingException!", e);
}
} }
private static String privateKeyToString(PrivateKey privateKey) { private static String privateKeyToString(PrivateKey privateKey) {
return Base64.getUrlEncoder() return Base64.getUrlEncoder()
.encodeToString(privateKey.getEncoded()); .encodeToString(privateKey.getEncoded());
} }
private static String publicKeyToString(PublicKey publicKey) { private static String publicKeyToString(PublicKey publicKey) {
return Base64.getUrlEncoder() return Base64.getUrlEncoder()
.encodeToString(publicKey.getEncoded()); .encodeToString(publicKey.getEncoded());
} }
public static PrivateKey stringToPrivateKey(String privateKeyStr) { public static PrivateKey stringToPrivateKey(String privateKeyStr) {
try { try {
byte[] keyBytes = Base64.getUrlDecoder() byte[] keyBytes = Base64.getUrlDecoder()
.decode(privateKeyStr); .decode(privateKeyStr);
KeyFactory keyFactory = KeyFactory.getInstance("EC"); KeyFactory keyFactory = KeyFactory.getInstance("EC");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
return keyFactory.generatePrivate(keySpec); return keyFactory.generatePrivate(keySpec);
@ -188,7 +203,7 @@ public final class KeyUtils {
public static PublicKey stringToPublicKey(String publicKeyStr) { public static PublicKey stringToPublicKey(String publicKeyStr) {
try { try {
byte[] keyBytes = Base64.getUrlDecoder() byte[] keyBytes = Base64.getUrlDecoder()
.decode(publicKeyStr); .decode(publicKeyStr);
KeyFactory keyFactory = KeyFactory.getInstance("EC"); KeyFactory keyFactory = KeyFactory.getInstance("EC");
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
return keyFactory.generatePublic(keySpec); return keyFactory.generatePublic(keySpec);
@ -207,7 +222,7 @@ public final class KeyUtils {
signature.update(message.getBytes()); signature.update(message.getBytes());
byte[] signedBytes = signature.sign(); byte[] signedBytes = signature.sign();
return Base64.getUrlEncoder() return Base64.getUrlEncoder()
.encodeToString(signedBytes); .encodeToString(signedBytes);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException!", e); throw new RuntimeException("NoSuchAlgorithmException!", e);
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
@ -218,14 +233,22 @@ public final class KeyUtils {
} }
// Verify the signature // Verify the signature
private static boolean verifySignature(String message, public static boolean verifySignature(String message,
String signatureStr, String signatureStr,
PublicKey publicKey) throws Exception { PublicKey publicKey) {
Signature signature = Signature.getInstance("SHA256withECDSA"); try {
signature.initVerify(publicKey); Signature signature = Signature.getInstance("SHA256withECDSA");
signature.update(message.getBytes()); signature.initVerify(publicKey);
byte[] signatureBytes = Base64.getUrlDecoder() signature.update(message.getBytes());
.decode(signatureStr); byte[] signatureBytes = Base64.getUrlDecoder()
return signature.verify(signatureBytes); .decode(signatureStr);
return signature.verify(signatureBytes);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException!", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("InvalidKeyException!", e);
} catch (SignatureException e) {
throw new RuntimeException("SignatureException!", e);
}
} }
} }

View file

@ -4,6 +4,8 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Properties; import java.util.Properties;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -14,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.example.license.utils.DeterministicHexSequenceWithTimestamp; import com.example.license.utils.DeterministicHexSequenceWithTimestamp;
import com.example.license.utils.KeyUtils;
@RestController @RestController
@RequestMapping("/license") @RequestMapping("/license")
@ -31,10 +34,11 @@ public class LicenseRest {
String[] split = payload.split("\\."); String[] split = payload.split("\\.");
File directory = new File(licenseDirectory); File directory = new File(licenseDirectory);
//TODO: extract ID, encrypted message and signature // TODO: extract ID, encrypted message and signature
final var id = split[0]; final var id = new String(Base64.getUrlDecoder().decode(split[0]), StandardCharsets.UTF_8);
final var encrypted = split[1]; final var encrypted = split[1];
final var signature = split[2]; final var signature = split[2];
System.out.println("ID: " + id);
System.out.println("Encrypted: " + encrypted); System.out.println("Encrypted: " + encrypted);
System.out.println("Signature: " + signature); System.out.println("Signature: " + signature);
@ -47,18 +51,39 @@ public class LicenseRest {
final var filePath = licenseDirectory + File.separator + fileName; final var filePath = licenseDirectory + File.separator + fileName;
final var props = loadProperties(filePath); final var props = loadProperties(filePath);
//TODO: decrypt final var decrypted = KeyUtils
.decryptDataWithAESGCM(Base64.getUrlDecoder().decode(encrypted),
KeyUtils.generateSharedSecret(
KeyUtils.stringToPrivateKey(props.getProperty("receiver-private-key")),
KeyUtils.stringToPublicKey(props.getProperty("sender-public-key"))));
System.out.println("Decrypted: " + decrypted);
//TODO: verify signature // TODO: verify signature
boolean isVerified = KeyUtils.verifySignature(decrypted, signature,
KeyUtils.stringToPublicKey(props.getProperty("sender-public-key")));
System.out.println("Is verified? " + isVerified);
//TODO: veryfy current index match, increment index using timestamp then send new index in response. // TODO: parse data
final var decryptedSplit = decrypted.split("\\:");
final var currentSenderIndex = decryptedSplit[0];
final var timestamp = Long.valueOf(decryptedSplit[1]);
final var currentLocalIndex = props.getProperty("index");
// TODO: check current local index if it matches with current sender index
final var nextLocalIndex = DeterministicHexSequenceWithTimestamp
.nextValueString(props.getProperty("index"), timestamp);
System.out.println("Current sender index: " + currentSenderIndex + " timestamp = " + timestamp
+ " current local index = " + currentLocalIndex + " next index = " + nextLocalIndex);
// TODO: veryfy current index match, increment index using timestamp then send
// new index in response.
System.out.println("Props: " + props); System.out.println("Props: " + props);
System.out.println(props.getProperty("index")); System.out.println(props.getProperty("index"));
if(!StringUtils.hasText(props.getProperty("index"))){ if (!StringUtils.hasText(props.getProperty("index"))) {
props.setProperty("index", "1A3F"); props.setProperty("index", "1A3F");
saveProperties(filePath, props); saveProperties(filePath, props);
} else { } else {
props.setProperty("index", DeterministicHexSequenceWithTimestamp.nextValueString(props.getProperty("index"), 0)); props.setProperty("index", DeterministicHexSequenceWithTimestamp
.nextValueString(props.getProperty("index"), 0));
} }
} }
} }

View file

@ -1,19 +1,27 @@
package com.example.license.utils; package com.example.license.utils;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.KeyPairGenerator; import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.Signature; import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.ECGenParameterSpec; import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.Base64; import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyAgreement; import javax.crypto.KeyAgreement;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
@ -60,8 +68,8 @@ public final class KeyUtils {
// Print Results // Print Results
System.out.println("Original Message: " + originalMessage); System.out.println("Original Message: " + originalMessage);
System.out.println("Encrypted (Base64): " + Base64.getEncoder() System.out.println("Encrypted (Base64): " + Base64.getUrlEncoder()
.encodeToString(encryptedData)); .encodeToString(encryptedData));
System.out.println("Decrypted Message: " + decryptedMessage); System.out.println("Decrypted Message: " + decryptedMessage);
String signature = signMessage(originalMessage, stringToPrivateKey(senderPrivateKeyToString)); String signature = signMessage(originalMessage, stringToPrivateKey(senderPrivateKeyToString));
@ -82,101 +90,165 @@ public final class KeyUtils {
} }
// Generate shared secret using ECDH // Generate shared secret using ECDH
private static SecretKey generateSharedSecret(PrivateKey privateKey, public static SecretKey generateSharedSecret(PrivateKey privateKey,
PublicKey publicKey) throws Exception { PublicKey publicKey) {
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH"); try {
keyAgreement.init(privateKey); KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.doPhase(publicKey, true); keyAgreement.init(privateKey);
byte[] sharedSecretBytes = keyAgreement.generateSecret(); keyAgreement.doPhase(publicKey, true);
return new SecretKeySpec(sharedSecretBytes, 0, 16, "AES"); byte[] sharedSecretBytes = keyAgreement.generateSecret();
return new SecretKeySpec(sharedSecretBytes, 0, 16, "AES");
} catch (InvalidKeyException e) {
throw new RuntimeException("Invalid key!", e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("No such algorithm!", e);
}
} }
// Encrypt data using AES-GCM // Encrypt data using AES-GCM
private static byte[] encryptDataWithAESGCM(String data, public static byte[] encryptDataWithAESGCM(String data,
SecretKey sharedSecret) throws Exception { SecretKey sharedSecret) {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Generate random 12-byte IV try {
SecureRandom random = new SecureRandom(); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] iv = new byte[12];
random.nextBytes(iv);
GCMParameterSpec spec = new GCMParameterSpec(128, iv); // Generate random 12-byte IV
cipher.init(Cipher.ENCRYPT_MODE, sharedSecret, spec); SecureRandom random = new SecureRandom();
byte[] encryptedData = cipher.doFinal(data.getBytes()); byte[] iv = new byte[12];
random.nextBytes(iv);
// Combine IV and encrypted data GCMParameterSpec spec = new GCMParameterSpec(128, iv);
byte[] encryptedDataWithIv = new byte[iv.length + encryptedData.length]; cipher.init(Cipher.ENCRYPT_MODE, sharedSecret, spec);
System.arraycopy(iv, 0, encryptedDataWithIv, 0, iv.length); byte[] encryptedData = cipher.doFinal(data.getBytes());
System.arraycopy(encryptedData, 0, encryptedDataWithIv, iv.length, encryptedData.length);
return encryptedDataWithIv; // Combine IV and encrypted data
byte[] encryptedDataWithIv = new byte[iv.length + encryptedData.length];
System.arraycopy(iv, 0, encryptedDataWithIv, 0, iv.length);
System.arraycopy(encryptedData, 0, encryptedDataWithIv, iv.length, encryptedData.length);
return encryptedDataWithIv;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException!", e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException("NoSuchPaddingException!", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("InvalidKeyException!", e);
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException("InvalidAlgorithmParameterException!", e);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException("IllegalBlockSizeException!", e);
} catch (BadPaddingException e) {
throw new RuntimeException("BadPaddingException!", e);
}
} }
// Decrypt data using AES-GCM // Decrypt data using AES-GCM
private static String decryptDataWithAESGCM(byte[] encryptedDataWithIv, public static String decryptDataWithAESGCM(byte[] encryptedDataWithIv,
SecretKey sharedSecret) throws Exception { SecretKey sharedSecret) {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// Extract IV try {
byte[] iv = new byte[12]; Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
System.arraycopy(encryptedDataWithIv, 0, iv, 0, iv.length);
byte[] encryptedData = new byte[encryptedDataWithIv.length - iv.length];
System.arraycopy(encryptedDataWithIv, iv.length, encryptedData, 0, encryptedData.length);
// Decrypt // Extract IV
GCMParameterSpec spec = new GCMParameterSpec(128, iv); byte[] iv = new byte[12];
cipher.init(Cipher.DECRYPT_MODE, sharedSecret, spec); System.arraycopy(encryptedDataWithIv, 0, iv, 0, iv.length);
byte[] decryptedData = cipher.doFinal(encryptedData); byte[] encryptedData = new byte[encryptedDataWithIv.length - iv.length];
System.arraycopy(encryptedDataWithIv, iv.length, encryptedData, 0, encryptedData.length);
return new String(decryptedData); // Decrypt
GCMParameterSpec spec = new GCMParameterSpec(128, iv);
cipher.init(Cipher.DECRYPT_MODE, sharedSecret, spec);
byte[] decryptedData = cipher.doFinal(encryptedData);
return new String(decryptedData);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException!", e);
} catch (NoSuchPaddingException e) {
throw new RuntimeException("NoSuchPaddingException!", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("InvalidKeyException!", e);
} catch (InvalidAlgorithmParameterException e) {
throw new RuntimeException("InvalidAlgorithmParameterException!", e);
} catch (IllegalBlockSizeException e) {
throw new RuntimeException("IllegalBlockSizeException!", e);
} catch (BadPaddingException e) {
throw new RuntimeException("BadPaddingException!", e);
}
} }
private static String privateKeyToString(PrivateKey privateKey) { private static String privateKeyToString(PrivateKey privateKey) {
return Base64.getEncoder() return Base64.getUrlEncoder()
.encodeToString(privateKey.getEncoded()); .encodeToString(privateKey.getEncoded());
} }
private static String publicKeyToString(PublicKey publicKey) { private static String publicKeyToString(PublicKey publicKey) {
return Base64.getEncoder() return Base64.getUrlEncoder()
.encodeToString(publicKey.getEncoded()); .encodeToString(publicKey.getEncoded());
} }
private static PrivateKey stringToPrivateKey(String privateKeyStr) throws Exception { public static PrivateKey stringToPrivateKey(String privateKeyStr) {
byte[] keyBytes = Base64.getDecoder() try {
.decode(privateKeyStr); byte[] keyBytes = Base64.getUrlDecoder()
KeyFactory keyFactory = KeyFactory.getInstance("EC"); .decode(privateKeyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePrivate(keySpec); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
return keyFactory.generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("Invalid key!", e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("No such algorithm!", e);
}
} }
private static PublicKey stringToPublicKey(String publicKeyStr) throws Exception { public static PublicKey stringToPublicKey(String publicKeyStr) {
byte[] keyBytes = Base64.getDecoder() try {
.decode(publicKeyStr); byte[] keyBytes = Base64.getUrlDecoder()
KeyFactory keyFactory = KeyFactory.getInstance("EC"); .decode(publicKeyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("EC");
return keyFactory.generatePublic(keySpec); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
return keyFactory.generatePublic(keySpec);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("Invalid key!", e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("No such algorithm!", e);
}
} }
private static String signMessage(String message, public static String signMessage(String message,
PrivateKey privateKey) throws Exception { PrivateKey privateKey) {
Signature signature = Signature.getInstance("SHA256withECDSA"); try {
signature.initSign(privateKey); Signature signature = Signature.getInstance("SHA256withECDSA");
signature.update(message.getBytes()); signature.initSign(privateKey);
byte[] signedBytes = signature.sign(); signature.update(message.getBytes());
return Base64.getEncoder() byte[] signedBytes = signature.sign();
.encodeToString(signedBytes); return Base64.getUrlEncoder()
.encodeToString(signedBytes);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException!", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("InvalidKeyException!", e);
} catch (SignatureException e) {
throw new RuntimeException("SignatureException!", e);
}
} }
// Verify the signature // Verify the signature
private static boolean verifySignature(String message, public static boolean verifySignature(String message,
String signatureStr, String signatureStr,
PublicKey publicKey) throws Exception { PublicKey publicKey) {
Signature signature = Signature.getInstance("SHA256withECDSA"); try {
signature.initVerify(publicKey); Signature signature = Signature.getInstance("SHA256withECDSA");
signature.update(message.getBytes()); signature.initVerify(publicKey);
byte[] signatureBytes = Base64.getDecoder() signature.update(message.getBytes());
.decode(signatureStr); byte[] signatureBytes = Base64.getUrlDecoder()
return signature.verify(signatureBytes); .decode(signatureStr);
return signature.verify(signatureBytes);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("NoSuchAlgorithmException!", e);
} catch (InvalidKeyException e) {
throw new RuntimeException("InvalidKeyException!", e);
} catch (SignatureException e) {
throw new RuntimeException("SignatureException!", e);
}
} }
} }