为什么使用Spring Authorization Server?
因为公司一个项目使用的第三方供应商提供的身份认证系统老是挂掉,不稳定.所以决定自主搭建.
由于Spring Security OAuth已经停止维护,而Spring Authorization Server却是在其基础上的增强和扩展,搭建简单方便,支持OAuth2.0,OIDC
Spring Authorization Server 基础
介绍
Spring Authorization Server 是一个基于 Spring Framework 的授权服务器,它提供了一种简单的方式来保护服务的资源.它是一个开源项目,可以快速构建和部署授权服务器.
使用 Spring Authorization Server 有以下几个好处:
-
安全性高 Spring Authorization Server 可以与 Spring Security 和 OAuth 2.0 协议一起使用,以提供安全的身份验证和授权机制.它支持多种授权流程,包括 Authorization Code、
Implicit、Client Credentials 和Password等.这些授权流程都是经过安全验证的,可以保证应用程序的安全性. -
易于使用 Spring Authorization Server 提供了一些可扩展的功能,例如自定义令牌生成和存储、多租户支持等.这些功能可以帮助您轻松地创建和管理客户端应用程序,并为这些应用程序分配不同的权限.此外,它还提供了一些易于使用的API和UI,可以快速构建和部署授权服务器.
-
高度可定制 Spring Authorization Server 是一个高度可定制的授权服务器,可以根据需求进行定制.可以使用自定义令牌生成和存储、多租户支持等功能来满足特定需求.此外,它还提供了一些可扩展的插件和扩展点,可以扩展其功能.
授权模式
授权码模式
authorization_code 这是最常用的授权类型,适用于需要用户交互的客户端应用程序.在这种模式下,客户端首先将用户重定向到授权服务器的授权页面,用户在授权页面上输入凭据并授权.然后,授权服务器将授权码发送给客户端.客户端使用授权码向授权服务器请求访问令牌.这种模式的优点是可以避免将访问令牌直接暴露给用户,提高了安全性.
客户端凭据模式
client_credentials 这种授权类型适用于没有用户交互的后台服务.在这种模式下,客户端使用自己的凭据(客户端 ID 和密钥)向授权服务器请求访问令牌.这种模式的优点是简单且安全性较高,但不适用于需要用户授权的场景.
令牌刷新模式
refresh_token 用于在访问令牌过期时获取新的访问令牌.当客户端用 access_token 向资源服务器请求资源时,如果 access_token 已过期,则资源服务器将返回 401 Unauthorized 响应.此时,客户端可以使用 refresh_token 向授权服务器请求新的 access_token . 在 OAuth2.0 协议中,refresh_token 是在授权码授权、密码授权和客户端凭据授权等授权类型中使用的.当客户端向授权服务器请求访问令牌时,授权服务器可以返回一个 refresh_token ,客户端可以使用该 refresh_token 在 access_token 过期时获取新的 access_token.
密码模式
password 这种授权类型适用于与用户有高度信任关系的应用程序.在这种模式下,客户端直接收集用户的用户名和密码,并使用这些凭据向授权服务器请求访问令牌.这种模式的缺点是需要用户将凭据提供给客户端,安全性较低.该模式在OAuth2.1中已移除,如果有场景需要这种模式,可以自行扩展,后续会讲到.
JWT令牌授权模式
urn:ietf:params:oauth:grant-type:jwt-bearer 授权类型是 OAuth2.0 协议的一种类型,允许客户端使用 JSON Web Token (JWT) 交换访问令牌.
在这种授权类型中,客户端在向授权服务器的令牌端点请求访问令牌时包含一个JWT,JWT包含一组声明,用于声明客户端和代表客户端发出请求的用户的身份.授权服务器验证JWT,如果有效,则向客户端发放访问令牌.
这种授权类型在客户端已经拥有包含获取访问令牌所需信息的JWT的情况下非常有用,例如当客户端通过单独的身份验证过程获得JWT时.它也可以用于客户端希望将用户身份验证委托给第三方身份提供者的情况. 总的来说,urn:ietf:params:oauth:grant-type:jwt-bearer 授权类型提供了一种安全高效的方式,使客户端可以使用JWT获取访问令牌.它在各种场景下都非常有用,Spring Authorization Server 提供了对此授权类型的支持.
这个模式的优点是:
- 简化了授权流程.不需要先拿code换token,直接使用JWT获取访问令牌.
- 客户端可以在JWT中携带更丰富的信息,传递给授权服务器.授权服务器可以根据这些信息判断是否授权.
- 不需要通过重定向携带code,更适合非浏览器客户端.
这个模式要求:
- 客户端和授权服务器之间要有共享的密钥用于签名JWT.
- 授权服务器要支持验证JWT的签名和内容.
- 客户端创建的JWT必须包含足够的信息让授权服务器判断是否授权.
一个使用场景是:
机器到机器的应用,客户端和授权服务器之间有预共享密钥。客户端可以在JWT中包含相当详细的调用凭据和范围,让授权服务器基于这些信息直接授权和返回访问令牌。这是一个更高效和安全的授权模式,但要求客户端和授权服务器有更高层次的信任关系。对于不太信任的客户端,授权服务器可能更倾向使用标准的授权码模式
设备码授权模式
urn:ietf:params:oauth:grant-type:device_code它适用于无法直接在设备上进行授权的场景,例如智能电视、游戏机等。使用该授权方式,用户需要在另一台设备上进行授权,然后将授权码输入到设备上进行登录.
这个模式的优点是:
- 适用于那些无法在浏览器中打开授权页面的设备,比如智能电视,IoT设备等.使用device_code可以完成授权流程.
- 不需要重定向,更加安全和流畅,device_code只能在设备上使用一次.
- 同时也允许用户在另一个设备(比如手机)上输入device_code完成授权,更加灵活.
这个模式的要求是:
- 客户端必须在自己的界面上向用户显示device_code,并提示用户如何使用它.
- 授权服务器必须实现device_code的申请、验证和换取访问令牌的接口.
- 授权服务器生成的device_code必须具有足够的安全性,且能够在有限时间内使用一次.
设备授权模式是一种更加方便和安全的机器设备授权方式,适用于那些无法进行标准浏览器授权流程的场景.但它要求客户端和授权服务器实现较为复杂的交互流程,并确保device_code的安全性.
客户端授权方法
client_secret
这是默认的客户端认证方法.客户端通过请求参数client_id 和client_secret来认证自己.适用于机密客户端.
client_secret_post
客户端通过请求体中的client_id 和client_secret来认证自己.其他与client_secret相同.
client_secret_basic
客户端通过基本认证头的账号(client_id)和密码(client_secret)来认证自己.其他与client_secret相同.
client_secret_jwt
客户端使用私钥签名一个JWT,然后在请求中提交这个JWT来认证自己.JWT的payload需要包含client_id,audience和issuer。适用于公开客户端.
none
不进行客户端认证.只在高度可信的环境使用,因为任何客户端都可以获得访问令牌.
private_key_jwt
和client_secret_jwt类似,但是使用RSA或ECDSA算法的私钥签名JWT.提供更高的安全性,因为私钥更加难以破解.
API调用
这里注意一下,一开始我也掉坑了,客户端如果在注册的时候使用的是ClientAuthenticationMethod.CLIENT_SECRET_BASIC
,那么api请求header 中要加上Authorization :Basic Base64.encode(client_id:client_secret).其他方式根据上文提到放在对应请求体中即可.授权服务器将会通过这个值验证客户端的有效性.
获取授权码
http
# client_id 客户端ID
# response_type 响应模式
# scope 授权范围(不清楚可以百度一下)
# redirect_uri 回调地址,code会通过这个地址返回
GET http://127.0.0.1:8084/oauth2/authorize?client_id=sas-client&response_type=code&scope=message.read+openid&redirect_uri=http://127.0.0.1:8084/test
Authorization: Basic c2FzLWNsaWVudDpzYXMtc2VjcmV0
使用授权码token
http
# grant_type 授权模式
# code 你的授权码
# redirect_uri 回调地址,请求code时所传参数
POST http://127.0.0.1:8084/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic c2FzLWNsaWVudDpzYXMtc2VjcmV0
grant_type=authorization_code&code={code}&redirect_uri=http://127.0.0.1:8084/test
账号密码获取token
http
# grant_type 授权模式
# username 账号
# password 密码
# scope 授权范围(不清楚可以百度一下)
POST http://127.0.0.1:8084/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic c2FzLWNsaWVudDpzYXMtc2VjcmV0
grant_type=password&username={username}&password={password}
客户端获取token
http
# grant_type 授权模式
POST http://127.0.0.1:8084/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic c2FzLWNsaWVudDpzYXMtc2VjcmV0
grant_type=client_credentials
刷新token
http
# grant_type 授权模式
# refresh_token refresh_token
POST http://127.0.0.1:8084/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic c2FzLWNsaWVudDpzYXMtc2VjcmV0
grant_type=refresh_token&refresh_token={refresh_token}
Spring Authorization Server 集成
maven引用
这里使用的是JDK17 ,spring boot的版本是3.1.0.
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
java
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-cas</artifactId>
<version>6.1.0</version>
</dependency>
数据库创建
sas核心的数据库创建有三个方法:
从官方demo或者sql文件
text
在这三个路径下:
org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql
org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql
org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql
从官网获取sql
How-to: Implement core services with JPA
官网的很全面,不仅有创建表单的sql,实体类的创建也有,简直是我等的福音,统统CV😎



从我这里CV
SQL
CREATE TABLE oauth2_registered_client
(
id varchar(100) NOT NULL,
client_id varchar(100) NOT NULL,
client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
client_secret varchar(200) DEFAULT NULL,
client_secret_expires_at timestamp DEFAULT NULL,
client_name varchar(200) NOT NULL,
client_authentication_methods varchar(1000) NOT NULL,
authorization_grant_types varchar(1000) NOT NULL,
redirect_uris varchar(1000) DEFAULT NULL,
post_logout_redirect_uris varchar(1000) DEFAULT NULL,
scopes varchar(1000) NOT NULL,
client_settings varchar(2000) NOT NULL,
token_settings varchar(2000) NOT NULL,
PRIMARY KEY (id)
);
SQL
CREATE TABLE oauth2_authorization_consent
(
registered_client_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
authorities varchar(1000) NOT NULL,
PRIMARY KEY (registered_client_id, principal_name)
);
SQL
CREATE TABLE oauth2_authorization
(
id varchar(100) NOT NULL,
registered_client_id varchar(100) NOT NULL,
principal_name varchar(200) NOT NULL,
authorization_grant_type varchar(100) NOT NULL,
authorized_scopes varchar(1000) DEFAULT NULL,
attributes blob DEFAULT NULL,
state varchar(500) DEFAULT NULL,
authorization_code_value blob DEFAULT NULL,
authorization_code_issued_at timestamp DEFAULT NULL,
authorization_code_expires_at timestamp DEFAULT NULL,
authorization_code_metadata blob DEFAULT NULL,
access_token_value blob DEFAULT NULL,
access_token_issued_at timestamp DEFAULT NULL,
access_token_expires_at timestamp DEFAULT NULL,
access_token_metadata blob DEFAULT NULL,
access_token_type varchar(100) DEFAULT NULL,
access_token_scopes varchar(1000) DEFAULT NULL,
oidc_id_token_value blob DEFAULT NULL,
oidc_id_token_issued_at timestamp DEFAULT NULL,
oidc_id_token_expires_at timestamp DEFAULT NULL,
oidc_id_token_metadata blob DEFAULT NULL,
refresh_token_value blob DEFAULT NULL,
refresh_token_issued_at timestamp DEFAULT NULL,
refresh_token_expires_at timestamp DEFAULT NULL,
refresh_token_metadata blob DEFAULT NULL,
user_code_value blob DEFAULT NULL,
user_code_issued_at timestamp DEFAULT NULL,
user_code_expires_at timestamp DEFAULT NULL,
user_code_metadata blob DEFAULT NULL,
device_code_value blob DEFAULT NULL,
device_code_issued_at timestamp DEFAULT NULL,
device_code_expires_at timestamp DEFAULT NULL,
device_code_metadata blob DEFAULT NULL,
PRIMARY KEY (id)
);
作为专业CV工程师,我当然懂原地CV有多美好了,这三张表是支持sas基本功能的表,没有跑不起来,至于作用,把表名直接翻译就知道是干嘛用的了.
配置
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.session.HttpSessionEventPublisher;
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class DefaultSecurityConfig {
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize ->
authorize
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)
.formLogin(formLogin ->
formLogin
.loginPage("/login")
)
.oauth2Login(oauth2Login ->
oauth2Login
.loginPage("/login")
);
return http.build();
}
@Bean
public UserDetailsService users() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user1")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
}
java
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
import java.time.Duration;
@Configuration
public class AuthorizationServerConfig {
private static final String CUSTOM_CONSENT_PAGE_URI = "/oauth2/consent";
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 关于Protocol Endpoints.(协议端点)配置
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
throws Exception {
return defaultConfig(http);
}
public DefaultSecurityFilterChain minConfig(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
).build();
}
public DefaultSecurityFilterChain defaultConfig(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.tokenEndpoint(oAuth2TokenEndpointConfigurer -> oAuth2TokenEndpointConfigurer
.accessTokenRequestConverter(new Oauth2PasswordAuthenticationConverter())
.accessTokenRequestConverter(new Oauth2SmsCodeAuthenticationConverter()))
.authorizationEndpoint(authorizationEndpoint ->
authorizationEndpoint.consentPage(CUSTOM_CONSENT_PAGE_URI))
.oidc(Customizer.withDefaults())
;
http
.exceptionHandling((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
)
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.jwt(Customizer.withDefaults()))
.csrf(CsrfConfigurer::disable);
return http.build();
}
/**
* 启动时创建客户端
*/
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
RegisteredClient registeredClient = RegisteredClient.withId(String.valueOf(SnowFlakeUtils.getInstance().nextId()))
.clientId("sas-client")
.clientSecret(passwordEncoder().encode("sas-secret"))
.clientName("sas测试客户端")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(new AuthorizationGrantType(CustomGrantType.PASSWORD))
.redirectUri("http://127.0.0.1:8084/test")
.postLogoutRedirectUri("http://127.0.0.1:8084/logged-out")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("message.read")
.scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
// token配置项
.tokenSettings(TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(100L))
// 使用默认JWT相关格式
.accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED)
.reuseRefreshTokens(true)
.refreshTokenTimeToLive(Duration.ofMinutes(120L))
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256).build()
)
.build();
JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
//判断客户端是否已经存在,不存在即创建
if (null == registeredClientRepository.findByClientId("sas-client")) {
registeredClientRepository.save(registeredClient);
}
return registeredClientRepository;
}
/**
* 关于OAuth2Authorization 授权信息的处理(入库)
*/
@Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
}
/**
* 关于OAuth2AuthorizationConsent信息的处理(入库)
*/
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer() {
return new FederatedIdentityTokenCustomizer();
}
/**
* 关于token生成规则的处理
*/
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = JwksUtils.generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
/**
* 关于AuthorizationServerSettings【授权服务器】的配置,含路径及接口
*/
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
}
所用到的工具类
java
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.ECFieldFp;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
final class KeyGeneratorUtils {
private KeyGeneratorUtils() {
}
static SecretKey generateSecretKey() {
SecretKey hmacKey;
try {
hmacKey = KeyGenerator.getInstance("HmacSha256").generateKey();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return hmacKey;
}
static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
static KeyPair generateEcKey() {
EllipticCurve ellipticCurve = new EllipticCurve(
new ECFieldFp(
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853951")),
new BigInteger("115792089210356248762697446949407573530086143415290314195533631308867097853948"),
new BigInteger("41058363725152142129326129780047268409114441015993725554835256314039467401291"));
ECPoint ecPoint = new ECPoint(
new BigInteger("48439561293906451759052585252797914202762949526041747995844080717082404635286"),
new BigInteger("36134250956749795798585127919587881956611106672985015071877198253568414405109"));
ECParameterSpec ecParameterSpec = new ECParameterSpec(
ellipticCurve,
ecPoint,
new BigInteger("115792089210356248762697446949407573529996955224135760342422259061068512044369"),
1);
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
keyPairGenerator.initialize(ecParameterSpec);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
}
java
import javax.crypto.SecretKey;
import java.security.KeyPair;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
public final class JwksUtils {
private JwksUtils() {
}
public static RSAKey generateRsa() {
KeyPair keyPair = KeyGeneratorUtils.generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
public static ECKey generateEc() {
KeyPair keyPair = KeyGeneratorUtils.generateEcKey();
ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
Curve curve = Curve.forECParameterSpec(publicKey.getParams());
return new ECKey.Builder(curve, publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
public static OctetSequenceKey generateSecret() {
SecretKey secretKey = KeyGeneratorUtils.generateSecretKey();
return new OctetSequenceKey.Builder(secretKey)
.keyID(UUID.randomUUID().toString())
.build();
}
}
总结
从6月份就写了,到现在还没写完,大家不会怪我吧.扩展部分放到下一章吧,这篇字数够多了,不足之处请大家指出,谢谢!