1.jdk keytool
可以用keytool工具直接生成,需要openssl工具Binaries - OpenSSLWiki设置到环境变量里
@echo offcd ../outputIF exist auth.jks del auth.jksIF exist auth.key del auth.keykeytool -genkeypair -alias xxxx_key -keyalg RSA -keypass xxxxxx -keystore auth.jks -storepass xxxxxx -dname CN=xxxkeytool -list -rfc --keystore auth.jks -storepass xxxxxx | openssl x509 -inform pem -pubkey > temp.key(@FOR /f "tokens=1* delims=:" %%a IN ('findstr /n "^" "temp.key"') DO @IF %%a leq 9 ECHO(%%b) > auth.keydel temp.keyecho Build OK!pause
2.认证与鉴权使用
生成的authkey放到gateway下,生成的auth.jks放到auth认证服务下
网关结合鉴权,需要配置如下配置文件
spring: security: oauth2: resourceserver: jwt: public-key-location: classpath:auth.key
认证服务配置Bean
@Bean public JWKSource<SecurityContext> jwkSource() throws Exception { KeyStore keyStore = KeyStore.getInstance("JKS"); // 取得JKS文件 Resource resource = new ClassPathResource(DEFAULT_KEY_STORE_FILE); if (!resource.exists()) { resource = new FileSystemResource(DEFAULT_KEY_STORE_FILE); } try (InputStream inputStream = resource.getInputStream()) { keyStore.load(inputStream, DEFAULT_KEY_STORE_PASSWORD.toCharArray()); RSAPublicKey publicKey = (RSAPublicKey) keyStore.getCertificate(DEFAULT_KEY_ALIAS).getPublicKey(); RSAPrivateKey privateKey = (RSAPrivateKey) keyStore.getKey(DEFAULT_KEY_ALIAS, DEFAULT_KEY_PASSWORD.toCharArray()); RSAKey rsaKey = new RSAKey.Builder(publicKey) .privateKey(privateKey) .keyStore(keyStore) .build(); JWKSet jwkSet = new JWKSet(rsaKey); return new ImmutableJWKSet<>(jwkSet); } }
3.工程项目直接生成
接口AuthToolController
package xxx.authtool.controller;import xxx.authtool.util.RsaUtil;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.nio.file.Files;import java.security.KeyPair;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.List;import java.util.zip.ZipEntry;import java.util.zip.ZipOutputStream;@RestController@RequestMapping("/generate")public class AuthToolController { @Value("${file-path:}") private String filePath; /** * 创建并下载auth.key和auth.jks * @param days 创建证书的有效期 单位:天 不传默认90天 * @param response http返回值 * @throws IOException 异常 */ @GetMapping("/downloadAuthKeyZip") public void downloadAuthKeyZip(@RequestParam(required = false) Integer days, HttpServletResponse response) throws IOException { String dirPath = mkdir(); // 创建一个临时zip文件,用于存放要下载的多个文件 File zipFile = new File(dirPath + "/" + "authKeys.zip"); // 获取需要下载的多个文件,并将它们添加到zip文件中 List<File> filesToDownload = getFilesToDownload(dirPath, days); addFilesToZip(filesToDownload, zipFile); deleteFiles(filesToDownload); //设置响应头信息 response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment; filename=authKeys.zip"); // 将zip文件内容写入到HttpServletResponse输出流中,实现文件下载 try (InputStream inputStream = Files.newInputStream(zipFile.toPath()); OutputStream outputStream = response.getOutputStream()) { byte[] buffer = new byte[4096]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); outputStream.flush(); } } } /** * 删除文件 * @param filesToDownload 文件列表 */ private void deleteFiles(List<File> filesToDownload) { for (File file : filesToDownload) { file.delete(); } } /** * 获取文件列表 * @param dirPath 文件路径 * @param days 时间 * @return 文件列表 */ private List<File> getFilesToDownload(String dirPath, Integer days) { // 获取需要下载的多个文件 List<File> files = new ArrayList<>(); KeyPair keyPair = RsaUtil.genKeyPair(); File publicFile = RsaUtil.genPublicFile(keyPair, dirPath); File privateFile = RsaUtil.genPrivateFile(keyPair, dirPath, days); files.add(publicFile); files.add(privateFile); return files; } /** * 压缩文件 * @param filesToDownload 文件列表 * @param zipFile 压缩文件 * @throws IOException 异常 */ private void addFilesToZip(List<File> filesToDownload, File zipFile) throws IOException { // 将多个文件压缩到zip文件中 try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) { for (File file : filesToDownload) { zipOutputStream.putNextEntry(new ZipEntry(file.getName())); try (FileInputStream inputStream = new FileInputStream(file)) { byte[] buf = new byte[1024]; int len; while ((len = inputStream.read(buf)) > 0) { zipOutputStream.write(buf, 0, len); } } zipOutputStream.closeEntry(); } } } /** * 创建文件夹 * @return 文件路径 */ private String mkdir() { String dateString = getDateString(); // 文件夹路径 String dirPath = filePath + "/" + dateString; File directory = new File(dirPath); // 判断文件夹是否存在 if (!directory.exists()) { // 创建文件夹及其父目录 directory.mkdirs(); } return dirPath; } /** * 获取日期字符串 * @return 日期字符串 */ private String getDateString() { Date currentDate = new Date(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); return dateFormat.format(currentDate); }}
RSAUtil
package xxx.authtool.util;import org.bouncycastle.cert.X509v3CertificateBuilder;import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;import org.bouncycastle.jce.provider.BouncyCastleProvider;import org.bouncycastle.openssl.jcajce.JcaPEMWriter;import org.bouncycastle.operator.ContentSigner;import org.bouncycastle.operator.OperatorCreationException;import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;import sun.security.x509.X500Name;import java.io.BufferedWriter;import java.io.File;import java.io.FileOutputStream;import java.io.FileWriter;import java.io.IOException;import java.io.StringWriter;import java.math.BigInteger;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.KeyStore;import java.security.KeyStoreException;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.security.PublicKey;import java.security.SecureRandom;import java.security.cert.Certificate;import java.security.cert.CertificateException;import java.util.Date;/** * RSA工具类 * * @author xxx */public final class RsaUtil { public static final String DEFAULT_KEY_STORE_FILE = "auth.jks"; public static final String DEFAULT_KEY_FILE = "auth.key"; public static final String DEFAULT_KEY_STORE_PASSWORD = "xxxxxx"; public static final String DEFAULT_KEY_ALIAS = "xxxx"; public static final String DEFAULT_KEY_PASSWORD = "xxxxxx"; /** * 随机生成密钥对 */ public static KeyPair genKeyPair() { try { // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); // 初始化密钥对生成器,密钥大小为96-1024位 keyPairGen.initialize(2048, new SecureRandom()); // 生成一个密钥对,保存在keyPair中 return keyPairGen.generateKeyPair(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } /** * 生成私钥 * @param keyPair 密钥对 * @param dirPath 文件列表 * @param days 时间 * @return 秘钥文件 */ public static File genPrivateFile(KeyPair keyPair, String dirPath, Integer days) { File file = new File(dirPath + "/" + DEFAULT_KEY_STORE_FILE); try (FileOutputStream fos = new FileOutputStream(file)) { KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(null, DEFAULT_KEY_PASSWORD.toCharArray()); // 得到私钥 PrivateKey privateKey = keyPair.getPrivate(); // 为以下对象生成 2048 位RSA密钥对和自签名证书 (SHA256withRSA) (有效期为 90 天): String dn = "CN=xxxx"; // 有效期默认为 90 天 int day = 90; if (days != null) { day = days; } X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder( new X500Name(dn).asX500Principal(), BigInteger.valueOf(System.currentTimeMillis()), new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + day * 24 * 60 * 60 * 1000L), new X500Name(dn).asX500Principal(), keyPair.getPublic()); ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA").setProvider(new BouncyCastleProvider()).build(keyPair.getPrivate()); Certificate cert = new JcaX509CertificateConverter().setProvider(new BouncyCastleProvider()).getCertificate(builder.build(signer)); keyStore.setKeyEntry(DEFAULT_KEY_ALIAS, privateKey, DEFAULT_KEY_PASSWORD.toCharArray(), new Certificate[]{cert}); keyStore.store(fos, DEFAULT_KEY_STORE_PASSWORD.toCharArray()); return file; } catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException | OperatorCreationException e) { throw new RuntimeException(e); } } /** * 生成公钥文件 * @param keyPair 密钥对 * @param dirPath 文件列表 * @return 公钥文件 */ public static File genPublicFile(KeyPair keyPair, String dirPath) { try (StringWriter sw = new StringWriter()) { // 得到公钥 PublicKey publicKey = keyPair.getPublic(); // 获取PublicKey并将其转换为PEM格式的字符串 try (JcaPEMWriter pemWriter = new JcaPEMWriter(sw)) { pemWriter.writeObject(publicKey); } catch (IOException e) { throw new RuntimeException(e); } File file = new File(dirPath + "/" + DEFAULT_KEY_FILE); try (FileWriter fw = new FileWriter(file.getAbsoluteFile(), true); BufferedWriter bw = new BufferedWriter(fw) ) { bw.write(sw.toString()); } catch (IOException e) { throw new RuntimeException(e); } return file; } catch (IOException e) { throw new RuntimeException(e); } }}
依赖
implementation 'org.bouncycastle:bcprov-jdk15on:1.69' implementation 'org.bouncycastle:bcpkix-jdk15on:1.69'
访问localhost:6080/generate/downloadAuthKeyZip可获取90天许可的证书
4.RSA工具类
package xxx.socket.stomp.config;import java.nio.charset.StandardCharsets;import java.security.InvalidKeyException;import java.security.KeyFactory;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.security.PublicKey;import java.security.SecureRandom;import java.security.spec.EncodedKeySpec;import java.security.spec.InvalidKeySpecException;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.Base64;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import org.springframework.util.Assert;/** * RSA工具类 * */public class RsaUtils { private static String publicKeyStr = ""; private static String privateKeyStr = ""; public static void main(String[] args) { genKeyPair(); String password = "xxxx"; String rawPassword = encrypt(password); String password2 = decrypt(rawPassword); Assert.isTrue(password.equals(password2), "Error"); } public static String encrypt(String password) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyStr)); PublicKey publicKey = keyFactory.generatePublic(publicKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] passwordBytes = cipher.doFinal(password.getBytes(StandardCharsets.UTF_8)); String result = new String(Base64.getEncoder().encode(passwordBytes), StandardCharsets.UTF_8); System.out.println("Encrypt Password: " + result); return result; } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { System.out.println("Algorithm Error/n" + e); } catch (NoSuchPaddingException | InvalidKeyException e) { System.out.println("Cipher Error/n" + e); } catch (IllegalBlockSizeException | BadPaddingException e) { System.out.println("Password Error/n" + e); } return null; } public static String decrypt(String password) { try { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyStr)); PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec); Cipher cipher = Cipher.getInstance("RSA"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] passwordBytes = cipher.doFinal(Base64.getDecoder().decode(password.getBytes(StandardCharsets.UTF_8))); String result = new String(passwordBytes, StandardCharsets.UTF_8); System.out.println("Decrypt Password: " + result); return result; } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { System.out.println("Algorithm Error/n" + e); } catch (NoSuchPaddingException | InvalidKeyException e) { System.out.println("Cipher Error/n" + e); } catch (IllegalBlockSizeException | BadPaddingException e) { System.out.println("Password Error/n" + e); } return null; } /** * 随机生成密钥对 */ public static void genKeyPair() { try { // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA"); // 初始化密钥对生成器,密钥大小为96-1024位 keyPairGen.initialize(2048, new SecureRandom()); // 生成一个密钥对,保存在keyPair中 KeyPair keyPair = keyPairGen.generateKeyPair(); // 得到私钥 PrivateKey privateKey = keyPair.getPrivate(); // 得到公钥 PublicKey publicKey = keyPair.getPublic(); String publicKeyString = new String(Base64.getEncoder().encode(publicKey.getEncoded())); // 得到私钥字符串 String privateKeyString = new String(Base64.getEncoder().encode((privateKey.getEncoded()))); System.out.println("随机生成的公钥为:" + publicKeyString); System.out.println("随机生成的私钥为:" + privateKeyString); publicKeyStr = publicKeyString; privateKeyStr = privateKeyString; } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }}
5. 参考资料
www.php1.cn/detail/Java_ShiYong_key_81410199.html