首页 > 编程知识 正文

公钥认证服务器

时间:2023-05-04 03:46:58 阅读:190591 作者:4591

传统的资源服务授权流程如下:

客户端先去授权服务器申请令牌,申请令牌后,携带令牌访问资源服务器;资源服务器访问授权服务器校验令牌的合法性;如果校验成功,授权服务器返回用户信息给资源服务器;资源服务器接收到校验成功的结果后,返回客户端请求的资源。

传统授权方法的问题是用户每次请求资源服务,资源服务都需要携带令牌访问认证服务去校验令牌的合法性,并根据令牌获取用户的相关信息,性能低下。

为了避免每次资源服务器反复校验令牌的合法性,我们可以利用公钥私钥完成对令牌的加密和解密。公钥私钥授权流程如下:

客户端请求认证服务器申请令牌;认证服务器采用非对称加密算法,使用私钥生成令牌;客户端携带令牌访问资源服务客户端,即在Http header中添加Authorization:Bearer令牌;资源服务器使用公钥校验令牌是否有效;令牌有效,资源服务器向客户端响应资源信息。

实际上,认证服务器生成一对公钥和私钥,私钥留给自己,公钥发给其他资源服务器。在发放令牌时,先利用MD5之类的摘要算法生成令牌摘要,并通过私钥对改摘要进行签名(加密)。当客户端携带令牌访问资源时,取下签名,即利用公钥进行解密得到令牌的摘要。再通过采用MD5算法生成令牌摘要,最后,判断两个摘要是否相同,若相同,则令牌有效,否则,令牌无效。

1. 生成公钥私钥

生成密钥证书

使用JDK自带工具keytool生成ssl证书:

keytool -genkeypair -alias changgou -keypass changgou -keyalg RSA -keypass changgou -storepass changgou -keystore C:/Users/Administrator/Desktop/changgou.jks

导出公钥

这里参考大佬的实现

import sun.misc.BASE64Encoder;import java.io.File;import java.io.FileInputStream;import java.io.FileWriter;import java.security.*;import java.security.cert.Certificate;/** * 从证书中导出公钥私钥 */public class ExportCert { //导出证书 base64格式 public static void exportCert(KeyStore keystore, String alias, String exportFile) throws Exception { Certificate cert = keystore.getCertificate(alias); BASE64Encoder encoder = new BASE64Encoder(); String encoded = encoder.encode(cert.getEncoded()); FileWriter fw = new FileWriter(exportFile); fw.write("-----BEGIN CERTIFICATE-----"); fw.write(encoded); fw.write("-----END CERTIFICATE-----"); fw.close(); } //得到KeyPair public static KeyPair getKeyPair(KeyStore keystore, String alias, char[] password) { try { Key key = keystore.getKey(alias, password); if (key instanceof PrivateKey) { Certificate cert = keystore.getCertificate(alias); PublicKey publicKey = cert.getPublicKey(); return new KeyPair(publicKey, (PrivateKey) key); } } catch (UnrecoverableKeyException e) { } catch (NoSuchAlgorithmException e) { } catch (KeyStoreException e) { } return null; } //导出私钥 public static void exportPrivateKey(PrivateKey privateKey,String exportFile) throws Exception { BASE64Encoder encoder = new BASE64Encoder(); String encoded = encoder.encode(privateKey.getEncoded()); FileWriter fw = new FileWriter(exportFile); fw.write("-----BEGIN PRIVATE KEY-----"); fw.write(encoded); fw.write("-----END PRIVATE KEY-----"); fw.close(); } //导出公钥 public static void exportPublicKey(PublicKey publicKey,String exportFile) throws Exception { BASE64Encoder encoder = new BASE64Encoder(); String encoded = encoder.encode(publicKey.getEncoded()); FileWriter fw = new FileWriter(exportFile); fw.write("-----BEGIN PUBLIC KEY-----"); fw.write(encoded); fw.write("-----END PUBLIC KEY-----"); fw.close(); } public static void main(String args[]) throws Exception { String keyStoreType = "JKS"; String keystoreFile = "C:/Users/Administrator/Desktop/changgou.jks"; String password = "changgou"; String alias = "changgou"; KeyStore keystore = KeyStore.getInstance(keyStoreType); keystore.load(new FileInputStream(new File(keystoreFile)), password.toCharArray()); String exportCertFile = "C:/Users/Administrator/Desktop/cms.cer"; String exportPrivateFile = "C:/Users/Administrator/Desktop/cmsPrivateKey.txt"; String exportPublicFile = "C:/Users/Administrator/Desktop/cmsPublicKey.txt"; ExportCert.exportCert(keystore, alias, exportCertFile); KeyPair keyPair = ExportCert.getKeyPair(keystore, alias, password.toCharArray()); ExportCert.exportPrivateKey(keyPair.getPrivate(), exportPrivateFile); ExportCert.exportPublicKey(keyPair.getPublic(), exportPublicFile); System.out.println("OK"); }} 2. 生成令牌

JWT签名算法中,常采用的两种加密算法:

HS256;RS256。

签名实际上是一个加密的过程,生成一段标识(也是JWT的一部分)作为接收方验证信息是否被篡改的依据。

RS256 (采用SHA-256 的 RSA 签名) 是一种非对称算法, 它使用公共/私钥对: 标识提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名。由于公钥 (与私钥相比) 不需要保护, 因此大多数标识提供方使其易于使用方获取和使用 (通常通过一个元数据URL)。

另一方面, HS256 (带有 SHA-256 的 HMAC 是一种对称算法, 双方之间仅共享一个 密钥。由于使用相同的密钥生成签名和验证签名, 因此必须注意确保密钥不被泄密。

在开发应用的时候启用JWT,使用RS256更加安全,你可以控制谁能使用什么类型的密钥。生成令牌的实现:

/** * 利用私钥创建jwt令牌 */ @Test public void testGenerateRSAJwt() { // 加载证书 ClassPathResource resource = new ClassPathResource("changgou.jks"); // 读取证书 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, "changgou".toCharArray()); // 获取私钥 KeyPair keyPair = keyStoreKeyFactory.getKeyPair("changgou", "changgou".toCharArray()); RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); // 创建令牌,使用RSA算法进行加盐加密 Map<String, Object> payload = new HashMap<>(); payload.put("name", "jack"); payload.put("age", 24); Jwt jwt = JwtHelper.encode(JSON.toJSONString(payload), new RsaSigner(privateKey)); System.out.println(jwt.getEncoded()); } 3. 解析令牌

利用公钥解析Jwt令牌。

/** * 利用公钥解析jwt令牌 */ @Test public void testCheckRSAJwt(){ String jwtStr = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiamFjayIsImFnZSI6MjR9.UTt8SEfzL3T7Xr_zj2ipWdt_ROiw2QOahJVAc5qSEb1keNAGcnKOu2Z-_Oe1kD1fZFIY3Y17QVGIjqONFa54BkXmeb2d6Hfq6pW2lD3ht_1iyVdZ2FPkDHVzyYMvUMgH1V85hJagUsGK0NyWBVIaODUj83Kpdky2DsXqUmkCrNQu03g-kKV3LLQ514EPpkcjAo2CrxbFHhQjOpPjQAd2C_FdJllVnQBgXD8z-h1VBOkhe1N3QH8AZx9PnKZMLEtKsN6U5cSbbj0vf5DKaYiyf5P_5DJ_OdkRcd5XvTZjGmFZA8FL5IYOtax4-ILPbdaMsYh2CYDct2n02crxpThkjw"; String publicKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvFsEiaLvij9C1Mz+oyAmt47whAaRkRu/8kePM+X8760UGU0RMwGti6Z9y3LQ0RvK6I0brXmbGB/RsN38PVnhcP8ZfxGUH26kX0RK+tlrxcrG+HkPYOH4XPAL8Q1lu1n9x3tLcIPxq8ZZtuIyKYEmoLKyMsvTviG5flTpDprT25unWgE4md1kthRWXOnfWHATVY7Y/r4obiOL1mS5bEa/iNKotQNnvIAKtjBM4RlIDWMa6dmz+lHtLtqDD2LF1qwoiSIHI75LQZ/CNYaHCfZSxtOydpNKq8eb1/PGiLNolD4La2zf0/1dlcr5mkesV570NxRmU1tFm8Zd3MZlZmyv9QIDAQAB-----END PUBLIC KEY-----"; Jwt jwt = JwtHelper.decodeAndVerify(jwtStr, new RsaVerifier(publicKey)); System.out.println(jwt.getClaims()); }

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。