The Java Security API provides a comprehensive framework for implementing security features in Java applications, including cryptography, authentication, authorization, and secure communication.
1. Introduction to Java Security Architecture
Security Providers Architecture
Java Security Architecture: ┌─────────────────────────────────────────────────────────────┐ │ Application Code │ ├─────────────────────────────────────────────────────────────┤ │ Java Security API (JCA/JCE) │ ├─────────────────────────────────────────────────────────────┤ │ Security Providers (Pluggable) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ SunJCE │ │ SunRsaSign │ │ SunPKCS11 │ │ │ │ │ │ │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ Operating System Security │ └─────────────────────────────────────────────────────────────┘
Key Components
- JCA (Java Cryptography Architecture): Core cryptographic services
- JCE (Java Cryptography Extension): Extended cryptographic algorithms
- JAAS (Java Authentication and Authorization Service): Authentication and access control
- JSSE (Java Secure Socket Extension): SSL/TLS implementation
- JGSS (Java Generic Security Service): Unified security API
2. Setup and Dependencies
Maven Dependencies
<!-- pom.xml --> <dependencies> <!-- Basic Java Security (included in JDK) --> <!-- No additional dependencies needed for basic operations --> <!-- Bouncy Castle Provider (optional) --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>1.76</version> </dependency> <!-- For JWT handling --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.5</version> <scope>runtime</scope> </dependency> </dependencies>
3. Cryptography Basics
Message Digests (Hashing)
package com.example.security;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HexFormat;
public class HashExamples {
public static String calculateSHA256(String input) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes());
return HexFormat.of().formatHex(hash);
}
public static String calculateMD5(String input) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] hash = digest.digest(input.getBytes());
return HexFormat.of().formatHex(hash);
}
public static String calculateSHA512(String input) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] hash = digest.digest(input.getBytes());
return HexFormat.of().formatHex(hash);
}
// Secure password hashing with salt
public static String hashPassword(String password, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(salt);
byte[] hash = digest.digest(password.getBytes());
return HexFormat.of().formatHex(hash);
}
public static void main(String[] args) throws NoSuchAlgorithmException {
String message = "Hello, Java Security!";
System.out.println("SHA-256: " + calculateSHA256(message));
System.out.println("SHA-512: " + calculateSHA512(message));
System.out.println("MD5: " + calculateMD5(message));
// Demo with salt
byte[] salt = "random_salt".getBytes();
String hashedPassword = hashPassword("myPassword123", salt);
System.out.println("Hashed Password: " + hashedPassword);
}
}
Secure Random Number Generation
package com.example.security;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.HexFormat;
public class SecureRandomExample {
public static byte[] generateSecureRandomBytes(int length) throws NoSuchAlgorithmException {
SecureRandom secureRandom = SecureRandom.getInstanceStrong(); // Use strong algorithm
byte[] randomBytes = new byte[length];
secureRandom.nextBytes(randomBytes);
return randomBytes;
}
public static String generateSecureToken(int byteLength) throws NoSuchAlgorithmException {
byte[] randomBytes = generateSecureRandomBytes(byteLength);
return HexFormat.of().formatHex(randomBytes);
}
public static int generateSecureInt(int bound) throws NoSuchAlgorithmException {
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
return secureRandom.nextInt(bound);
}
public static void main(String[] args) throws NoSuchAlgorithmException {
// Generate secure random bytes
byte[] randomBytes = generateSecureRandomBytes(32);
System.out.println("Random Bytes: " + HexFormat.of().formatHex(randomBytes));
// Generate secure token
String token = generateSecureToken(16);
System.out.println("Secure Token: " + token);
// Generate secure random number
int randomNumber = generateSecureInt(1000);
System.out.println("Secure Random Number: " + randomNumber);
// Different SecureRandom algorithms
SecureRandom nativePrng = SecureRandom.getInstance("NativePRNG");
SecureRandom sha1Prng = SecureRandom.getInstance("SHA1PRNG");
System.out.println("NativePRNG Algorithm: " + nativePrng.getAlgorithm());
System.out.println("SHA1PRNG Algorithm: " + sha1Prng.getAlgorithm());
}
}
4. Symmetric Encryption
AES Encryption/Decryption
package com.example.security;
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.util.Base64;
import java.util.HexFormat;
public class SymmetricEncryption {
private static final String AES_ALGORITHM = "AES";
private static final String AES_GCM_NO_PADDING = "AES/GCM/NoPadding";
private static final int GCM_TAG_LENGTH = 128; // bits
private static final int IV_LENGTH = 12; // bytes for GCM
// Generate AES key
public static SecretKey generateAESKey(int keySize) throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
keyGenerator.init(keySize);
return keyGenerator.generateKey();
}
// Encrypt using AES-GCM (recommended)
public static String encryptGCM(String plaintext, SecretKey key) throws Exception {
byte[] iv = new byte[IV_LENGTH];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(iv);
Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
byte[] cipherText = cipher.doFinal(plaintext.getBytes());
// Combine IV and ciphertext
byte[] encryptedData = new byte[IV_LENGTH + cipherText.length];
System.arraycopy(iv, 0, encryptedData, 0, IV_LENGTH);
System.arraycopy(cipherText, 0, encryptedData, IV_LENGTH, cipherText.length);
return Base64.getEncoder().encodeToString(encryptedData);
}
// Decrypt using AES-GCM
public static String decryptGCM(String encryptedData, SecretKey key) throws Exception {
byte[] decodedData = Base64.getDecoder().decode(encryptedData);
// Extract IV and ciphertext
byte[] iv = new byte[IV_LENGTH];
byte[] cipherText = new byte[decodedData.length - IV_LENGTH];
System.arraycopy(decodedData, 0, iv, 0, IV_LENGTH);
System.arraycopy(decodedData, IV_LENGTH, cipherText, 0, cipherText.length);
Cipher cipher = Cipher.getInstance(AES_GCM_NO_PADDING);
GCMParameterSpec parameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
byte[] plaintext = cipher.doFinal(cipherText);
return new String(plaintext);
}
// AES-CBC encryption (legacy)
public static String encryptCBC(String plaintext, SecretKey key, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(iv));
byte[] cipherText = cipher.doFinal(plaintext.getBytes());
return Base64.getEncoder().encodeToString(cipherText);
}
// AES-CBC decryption
public static String decryptCBC(String encryptedData, SecretKey key, byte[] iv) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, new javax.crypto.spec.IvParameterSpec(iv));
byte[] plaintext = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
return new String(plaintext);
}
public static void main(String[] args) throws Exception {
// Generate AES-256 key
SecretKey secretKey = generateAESKey(256);
System.out.println("AES Key: " + HexFormat.of().formatHex(secretKey.getEncoded()));
String originalText = "This is a secret message!";
System.out.println("Original: " + originalText);
// Encrypt with GCM
String encryptedGCM = encryptGCM(originalText, secretKey);
System.out.println("Encrypted (GCM): " + encryptedGCM);
// Decrypt with GCM
String decryptedGCM = decryptGCM(encryptedGCM, secretKey);
System.out.println("Decrypted (GCM): " + decryptedGCM);
// Demonstrate CBC (for comparison)
byte[] iv = new byte[16]; // IV for CBC
new SecureRandom().nextBytes(iv);
String encryptedCBC = encryptCBC(originalText, secretKey, iv);
System.out.println("Encrypted (CBC): " + encryptedCBC);
String decryptedCBC = decryptCBC(encryptedCBC, secretKey, iv);
System.out.println("Decrypted (CBC): " + decryptedCBC);
}
}
5. Asymmetric Encryption
RSA Encryption/Decryption
package com.example.security;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class AsymmetricEncryption {
private static final String RSA_ALGORITHM = "RSA";
private static final int RSA_KEY_SIZE = 2048;
// Generate RSA key pair
public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
keyPairGenerator.initialize(RSA_KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
// Encrypt with public key
public static String encryptRSA(String plaintext, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
// Decrypt with private key
public static String decryptRSA(String encryptedText, PrivateKey privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
return new String(decryptedBytes);
}
// Convert PublicKey to string
public static String publicKeyToString(PublicKey publicKey) {
return Base64.getEncoder().encodeToString(publicKey.getEncoded());
}
// Convert PrivateKey to string
public static String privateKeyToString(PrivateKey privateKey) {
return Base64.getEncoder().encodeToString(privateKey.getEncoded());
}
// Convert string to PublicKey
public static PublicKey stringToPublicKey(String keyString) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyString);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return keyFactory.generatePublic(keySpec);
}
// Convert string to PrivateKey
public static PrivateKey stringToPrivateKey(String keyString) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyString);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
}
public static void main(String[] args) throws Exception {
// Generate RSA key pair
KeyPair keyPair = generateRSAKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
System.out.println("Public Key: " + publicKeyToString(publicKey));
System.out.println("Private Key: " + privateKeyToString(privateKey));
String originalText = "Confidential data for RSA encryption";
System.out.println("Original: " + originalText);
// Encrypt with public key
String encrypted = encryptRSA(originalText, publicKey);
System.out.println("Encrypted: " + encrypted);
// Decrypt with private key
String decrypted = decryptRSA(encrypted, privateKey);
System.out.println("Decrypted: " + decrypted);
// Demonstrate key serialization/deserialization
String publicKeyStr = publicKeyToString(publicKey);
String privateKeyStr = privateKeyToString(privateKey);
PublicKey restoredPublicKey = stringToPublicKey(publicKeyStr);
PrivateKey restoredPrivateKey = stringToPrivateKey(privateKeyStr);
// Test with restored keys
String testEncrypted = encryptRSA("Test message", restoredPublicKey);
String testDecrypted = decryptRSA(testEncrypted, restoredPrivateKey);
System.out.println("Test with restored keys: " + testDecrypted);
}
}
6. Digital Signatures
RSA Digital Signatures
package com.example.security;
import java.security.*;
import java.util.Base64;
public class DigitalSignatureExample {
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
// Generate signature
public static String signData(String data, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(data.getBytes());
byte[] digitalSignature = signature.sign();
return Base64.getEncoder().encodeToString(digitalSignature);
}
// Verify signature
public static boolean verifySignature(String data, String signatureStr, PublicKey publicKey) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(publicKey);
signature.update(data.getBytes());
byte[] signatureBytes = Base64.getDecoder().decode(signatureStr);
return signature.verify(signatureBytes);
}
// Create digital signature for file/content verification
public static class SignedMessage {
private final String data;
private final String signature;
private final String publicKey;
public SignedMessage(String data, String signature, String publicKey) {
this.data = data;
this.signature = signature;
this.publicKey = publicKey;
}
// Getters
public String getData() { return data; }
public String getSignature() { return signature; }
public String getPublicKey() { return publicKey; }
}
public static void main(String[] args) throws Exception {
// Generate key pair
KeyPair keyPair = AsymmetricEncryption.generateRSAKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
String importantData = "This is a very important contract that needs to be signed.";
System.out.println("Original Data: " + importantData);
// Sign the data
String signature = signData(importantData, privateKey);
System.out.println("Digital Signature: " + signature);
// Verify the signature
boolean isValid = verifySignature(importantData, signature, publicKey);
System.out.println("Signature Valid: " + isValid);
// Tamper with data and verify
String tamperedData = "This is a tampered contract that needs to be signed.";
boolean isTamperedValid = verifySignature(tamperedData, signature, publicKey);
System.out.println("Tampered Signature Valid: " + isTamperedValid);
// Create signed message
String publicKeyStr = AsymmetricEncryption.publicKeyToString(publicKey);
SignedMessage signedMessage = new SignedMessage(importantData, signature, publicKeyStr);
System.out.println("\nSigned Message:");
System.out.println("Data: " + signedMessage.getData());
System.out.println("Signature: " + signedMessage.getSignature());
System.out.println("Public Key: " + signedMessage.getPublicKey());
}
}
7. KeyStore and Certificate Management
KeyStore Operations
package com.example.security;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
public class KeyStoreExample {
private static final String KEYSTORE_TYPE = "PKCS12";
private static final String KEY_ALGORITHM = "RSA";
// Create new keystore
public static KeyStore createKeyStore(String password) throws Exception {
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
keyStore.load(null, password.toCharArray()); // Initialize empty keystore
return keyStore;
}
// Generate key pair and store in keystore
public static void generateAndStoreKey(KeyStore keyStore, String alias,
String keyPassword, String keystorePassword) throws Exception {
// Generate key pair
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
// Create self-signed certificate (simplified)
X509Certificate certificate = createSelfSignedCertificate(keyPair);
// Store private key with certificate chain
Certificate[] chain = {certificate};
keyStore.setKeyEntry(alias, keyPair.getPrivate(), keyPassword.toCharArray(), chain);
}
// Load keystore from file
public static KeyStore loadKeyStore(String filename, String password) throws Exception {
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
try (FileInputStream fis = new FileInputStream(filename)) {
keyStore.load(fis, password.toCharArray());
}
return keyStore;
}
// Save keystore to file
public static void saveKeyStore(KeyStore keyStore, String filename, String password) throws Exception {
try (FileOutputStream fos = new FileOutputStream(filename)) {
keyStore.store(fos, password.toCharArray());
}
}
// List all aliases in keystore
public static void listKeyStoreEntries(KeyStore keyStore) throws Exception {
Enumeration<String> aliases = keyStore.aliases();
System.out.println("Keystore Entries:");
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
System.out.println(" - " + alias + " (" + keyStore.getType() + ")");
}
}
// Get private key from keystore
public static PrivateKey getPrivateKey(KeyStore keyStore, String alias, String password) throws Exception {
return (PrivateKey) keyStore.getKey(alias, password.toCharArray());
}
// Get certificate from keystore
public static Certificate getCertificate(KeyStore keyStore, String alias) throws Exception {
return keyStore.getCertificate(alias);
}
// Simplified self-signed certificate creation (for demo purposes)
private static X509Certificate createSelfSignedCertificate(KeyPair keyPair) throws Exception {
// In real applications, use proper certificate generation
// This is a simplified version for demonstration
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
// Note: Actual certificate generation requires more complex code
// Returning a mock for demonstration
return (X509Certificate) certFactory.generateCertificate(
new java.io.ByteArrayInputStream("mock certificate".getBytes()));
}
public static void main(String[] args) throws Exception {
String keystorePassword = "keystore123";
String keyPassword = "key123";
String alias = "mykey";
String filename = "mykeystore.p12";
// Create new keystore
KeyStore keyStore = createKeyStore(keystorePassword);
// Generate and store key
generateAndStoreKey(keyStore, alias, keyPassword, keystorePassword);
// Save keystore to file
saveKeyStore(keyStore, filename, keystorePassword);
System.out.println("Keystore created and saved to: " + filename);
// List entries
listKeyStoreEntries(keyStore);
// Load keystore from file
KeyStore loadedKeyStore = loadKeyStore(filename, keystorePassword);
System.out.println("\nLoaded keystore entries:");
listKeyStoreEntries(loadedKeyStore);
// Retrieve private key
PrivateKey privateKey = getPrivateKey(loadedKeyStore, alias, keyPassword);
System.out.println("\nRetrieved Private Key: " + privateKey.getAlgorithm());
// Retrieve certificate
Certificate certificate = getCertificate(loadedKeyStore, alias);
System.out.println("Retrieved Certificate: " + certificate.getType());
}
}
8. Secure Password Hashing
PBKDF2 and BCrypt
package com.example.security;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.util.HexFormat;
public class PasswordHashing {
private static final int PBKDF2_ITERATIONS = 100000;
private static final int SALT_LENGTH = 32;
private static final int HASH_LENGTH = 256;
// Generate secure salt
public static byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_LENGTH];
random.nextBytes(salt);
return salt;
}
// Hash password with PBKDF2
public static String hashPasswordPBKDF2(String password, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
salt,
PBKDF2_ITERATIONS,
HASH_LENGTH
);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
return HexFormat.of().formatHex(hash);
}
// Verify password with PBKDF2
public static boolean verifyPasswordPBKDF2(String password, String storedHash, byte[] salt)
throws NoSuchAlgorithmException, InvalidKeySpecException {
String computedHash = hashPasswordPBKDF2(password, salt);
return computedHash.equals(storedHash);
}
// Simple BCrypt-like implementation (using existing Java security)
public static class SimpleBCrypt {
private static final String BCrypt_PREFIX = "$2a$";
public static String hashPassword(String password) throws NoSuchAlgorithmException {
byte[] salt = generateSalt();
String hashed = hashPasswordPBKDF2(password, salt);
return BCrypt_PREFIX + Base64.getEncoder().encodeToString(salt) + "$" + hashed;
}
public static boolean checkPassword(String password, String hashedPassword)
throws NoSuchAlgorithmException, InvalidKeySpecException {
String[] parts = hashedPassword.split("\\$");
if (parts.length != 4 || !parts[1].equals("2a")) {
throw new IllegalArgumentException("Invalid BCrypt format");
}
byte[] salt = Base64.getDecoder().decode(parts[2]);
String storedHash = parts[3];
return verifyPasswordPBKDF2(password, storedHash, salt);
}
}
public static void main(String[] args) throws Exception {
String password = "MySecurePassword123!";
// PBKDF2 example
byte[] salt = generateSalt();
String hashedPassword = hashPasswordPBKDF2(password, salt);
System.out.println("Password: " + password);
System.out.println("Salt: " + HexFormat.of().formatHex(salt));
System.out.println("Hashed Password (PBKDF2): " + hashedPassword);
// Verify password
boolean isValid = verifyPasswordPBKDF2(password, hashedPassword, salt);
System.out.println("Password verification: " + isValid);
// Simple BCrypt example
String bcryptHash = SimpleBCrypt.hashPassword(password);
System.out.println("BCrypt-style Hash: " + bcryptHash);
boolean bcryptValid = SimpleBCrypt.checkPassword(password, bcryptHash);
System.out.println("BCrypt verification: " + bcryptValid);
// Test with wrong password
boolean wrongPasswordValid = verifyPasswordPBKDF2("wrongpassword", hashedPassword, salt);
System.out.println("Wrong password verification: " + wrongPasswordValid);
}
}
9. JWT (JSON Web Token) Security
JWT Creation and Validation
package com.example.security;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JWTExample {
private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private static final long EXPIRATION_TIME = 86400000; // 24 hours in milliseconds
// Create JWT token
public static String createJWT(String subject, Map<String, Object> claims) {
return Jwts.builder()
.setSubject(subject)
.addClaims(claims)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SECRET_KEY)
.compact();
}
// Validate and parse JWT token
public static Claims validateAndParseJWT(String token) throws ExpiredJwtException, MalformedJwtException {
return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY)
.build()
.parseClaimsJws(token)
.getBody();
}
// Check if token is expired
public static boolean isTokenExpired(String token) {
try {
Claims claims = validateAndParseJWT(token);
return claims.getExpiration().before(new Date());
} catch (Exception e) {
return true; // Consider invalid tokens as expired
}
}
// Refresh JWT token
public static String refreshJWT(String token) {
try {
Claims claims = validateAndParseJWT(token);
claims.setIssuedAt(new Date());
claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
return Jwts.builder()
.setClaims(claims)
.signWith(SECRET_KEY)
.compact();
} catch (Exception e) {
throw new SecurityException("Cannot refresh invalid token", e);
}
}
public static void main(String[] args) {
// Create user claims
Map<String, Object> claims = new HashMap<>();
claims.put("userId", 12345);
claims.put("role", "ADMIN");
claims.put("email", "[email protected]");
// Create JWT
String jwt = createJWT("admin-user", claims);
System.out.println("Generated JWT: " + jwt);
// Validate and parse JWT
try {
Claims parsedClaims = validateAndParseJWT(jwt);
System.out.println("\nParsed JWT Claims:");
System.out.println("Subject: " + parsedClaims.getSubject());
System.out.println("User ID: " + parsedClaims.get("userId"));
System.out.println("Role: " + parsedClaims.get("role"));
System.out.println("Email: " + parsedClaims.get("email"));
System.out.println("Issued At: " + parsedClaims.getIssuedAt());
System.out.println("Expiration: " + parsedClaims.getExpiration());
System.out.println("Is Expired: " + isTokenExpired(jwt));
} catch (ExpiredJwtException e) {
System.err.println("JWT has expired: " + e.getMessage());
} catch (MalformedJwtException e) {
System.err.println("Invalid JWT: " + e.getMessage());
} catch (Exception e) {
System.err.println("Error parsing JWT: " + e.getMessage());
}
// Test token refresh
try {
String refreshedToken = refreshJWT(jwt);
System.out.println("\nRefreshed JWT: " + refreshedToken);
Claims refreshedClaims = validateAndParseJWT(refreshedToken);
System.out.println("Refreshed Issued At: " + refreshedClaims.getIssuedAt());
} catch (Exception e) {
System.err.println("Error refreshing token: " + e.getMessage());
}
}
}
10. Security Best Practices and Utilities
Security Configuration Checker
package com.example.security;
import java.security.Security;
import java.util.Arrays;
public class SecurityChecker {
public static void checkSecurityConfiguration() {
System.out.println("=== Java Security Configuration ===");
// List all security providers
System.out.println("\nSecurity Providers:");
Arrays.stream(Security.getProviders())
.forEach(provider -> {
System.out.println(" - " + provider.getName() + " v" + provider.getVersion());
});
// Check available algorithms
System.out.println("\nAvailable Algorithms:");
checkAlgorithm("AES");
checkAlgorithm("RSA");
checkAlgorithm("SHA-256");
checkAlgorithm("PBKDF2WithHmacSHA256");
// Security properties
System.out.println("\nSecurity Properties:");
printSecurityProperty("securerandom.source");
printSecurityProperty("jdk.tls.disabledAlgorithms");
printSecurityProperty("jdk.certpath.disabledAlgorithms");
}
private static void checkAlgorithm(String algorithm) {
try {
// Try to get instance to check availability
if (algorithm.startsWith("AES") || algorithm.startsWith("RSA")) {
javax.crypto.Cipher.getInstance(algorithm);
} else if (algorithm.startsWith("SHA")) {
java.security.MessageDigest.getInstance(algorithm);
} else if (algorithm.startsWith("PBKDF2")) {
javax.crypto.SecretKeyFactory.getInstance(algorithm);
}
System.out.println(" ✓ " + algorithm + " - AVAILABLE");
} catch (Exception e) {
System.out.println(" ✗ " + algorithm + " - NOT AVAILABLE");
}
}
private static void printSecurityProperty(String property) {
String value = Security.getProperty(property);
System.out.println(" - " + property + ": " +
(value != null ? value : "Not set"));
}
// Security recommendations
public static void printSecurityRecommendations() {
System.out.println("\n=== Security Recommendations ===");
System.out.println("1. Use AES-GCM for symmetric encryption");
System.out.println("2. Use RSA-2048 or higher for asymmetric encryption");
System.out.println("3. Use SHA-256 or higher for hashing");
System.out.println("4. Use PBKDF2 with high iteration count for password hashing");
System.out.println("5. Always use secure random for IV and salt generation");
System.out.println("6. Store keys in secure key stores");
System.out.println("7. Validate all inputs and use proper exception handling");
System.out.println("8. Keep Java and security providers updated");
}
public static void main(String[] args) {
checkSecurityConfiguration();
printSecurityRecommendations();
}
}
Secure Configuration Manager
package com.example.security;
import java.io.*;
import java.util.Properties;
public class SecureConfigManager {
private final Properties properties;
private final String configFile;
private final byte[] encryptionKey;
public SecureConfigManager(String configFile, byte[] encryptionKey) {
this.configFile = configFile;
this.encryptionKey = encryptionKey;
this.properties = new Properties();
loadConfig();
}
private void loadConfig() {
try (FileInputStream fis = new FileInputStream(configFile)) {
// In real implementation, decrypt the file
properties.load(fis);
} catch (IOException e) {
System.err.println("Error loading config: " + e.getMessage());
}
}
public void saveConfig() {
try (FileOutputStream fos = new FileOutputStream(configFile)) {
// In real implementation, encrypt the file
properties.store(fos, "Secure Configuration");
} catch (IOException e) {
System.err.println("Error saving config: " + e.getMessage());
}
}
public String getProperty(String key) {
return properties.getProperty(key);
}
public void setProperty(String key, String value) {
properties.setProperty(key, value);
}
// Secure method to get sensitive data
public char[] getSensitiveData(String key) {
String value = getProperty(key);
return value != null ? value.toCharArray() : null;
}
// Clear sensitive data from memory
public void clearSensitiveData(char[] data) {
if (data != null) {
Arrays.fill(data, '\0');
}
}
}
Summary
Key Java Security API Components:
- Cryptography: Encryption, decryption, hashing, digital signatures
- Key Management: Key generation, storage, and retrieval
- Authentication: Password hashing, certificate validation
- Secure Communication: SSL/TLS, JWT tokens
- Access Control: Permissions and policies
Best Practices:
- Always use strong algorithms (AES-256, RSA-2048, SHA-256)
- Never store passwords in plain text
- Use secure random for IV and salt generation
- Implement proper key management
- Validate all inputs and handle security exceptions
- Keep security providers updated
Security Providers:
- SunJCE (Default Java provider)
- Bouncy Castle (Third-party, extensive algorithms)
- SunPKCS11 (Hardware security module support)
The Java Security API provides a robust foundation for implementing security in Java applications, but it's crucial to follow best practices and stay updated with security recommendations.