网关上实现https私有证书的管理

api网关作为后端服务的代理,提供网络请求的反向代理,需要保证http请求的安全性。根据实际服务对安全性的不同要求,一般可以采用http协议结合认证方式来保证报文防篡改,或采用https协议保证数据传输的安全。

本文主要介绍http的几种认证方式、https的原理以及https私有证书的管理扩展。

http

http协议下,传输的报文是明文传输,对于敏感数据,需要服务提供方和消费方约定加密方式,对敏感数据进行加密后传输。采用一定的认证方式,保证客户端和服务端之间的报文一定程度上不被篡改。这些认证方式并不能完全保证信息安全,仍存在风险,对于安全要求高的服务需采用https协议。

Basic认证

basic认证方式:
客户端在http请求的时候,将accessKey信息和accessSecret信息以’:'拼接,并进行Base64加密得到Authorization报文头,添加在header中进行http请求。

服务端在收到请求后,对Authorization报文头进行Base64解密,提取出accessKey和accessSecret信息,并进行数据库校验,判断请求方是否合法。

Token认证

token认证是在http请求前,先根据用户名和密码向服务器请求一个token,token一般有一定的时效性。在后续请求时,把这个token以query参数的形式传给服务端,服务端根据这个token值来校验请求的合法性。

Digest认证

digest认证相比于basic认证和token认证比较复杂。摘要的格式、采用的算法可以由服务端约定。

客户端:

  • 将httpMethod parameters timestamp等信息进行拼接得到要加密的摘要信息
  • 将要加密的摘要和accessSecret进行加密,比如采用hmac-sha256算法
  • 加密后的信息再进行Base64加密得到signature,
  • 将算法、时间戳、accessKey信息和signature组成 Authorization加入到http的header中进行http请求。

服务端:

  • 从http的header中取出Authorization信息,进行解密
  • 从数据库取出accessKey对应的密钥
  • 采用客户端相同的算法进行加密得到signature
  • 比较计算得到的signature和Authorization中的signature的值是否相等。若不相等则信息被篡改。

https

HTTPS在HTTP的基础上加入了SSL协议,对信息、数据加密,用来保证数据传输的安全。

https和http的区别

HTTPS和HTTP的区别主要为以下四点:

  • https协议需要到ca申请证书,一般免费证书很少,需要交费。
  • http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
  • http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  • http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

https认证流程

网关上实现https私有证书的管理_第1张图片
(为什么server端私钥可以解密出随机数)

公有证书

比如https://www.taobao.com 的证书是由GlobalSign颁发的,是中国权威的可信CA机构,浏览器认可这些第三方机构,所以可以直接访问https请求。
网关上实现https私有证书的管理_第2张图片

私有证书

私有证书是服务端应用自己生成的证书,浏览器或JDK无法鉴别认证,需要将证书导入到浏览器或JDK中。

API网关需要维护后端服务的私有证书,可行的方式有两种:

  • 在通过keyTool导入到jdk的cert中,导入后需要重启应用才能识别导入的证书。
  • 通过解析certString,构造自定义扩展X509TrustManager,代码维护自定义添加的tm,可现动态实时的添加证书。
    但是应用重启后,https服务请求时证书需要重新导入。

关键代码

X509TrustManager接口定义
public interface X509TrustManager extends TrustManager {
    void checkClientTrusted(X509Certificate[] var1, String var2) throws CertificateException;

    void checkServerTrusted(X509Certificate[] var1, String var2) throws CertificateException;

    X509Certificate[] getAcceptedIssuers();
}

以下代码是刚毕业时写的,提供关键部分的思路。

添加证书

这部分代码做了2个事情,根据certString解析出对应的cert,导入到TrustManagerFactory中,保证应用重启后可直接访问。
另一方面自己维护了一个trustManager的list,重写x509TrustManager接口的三个方法,保证在不重启应用时也可以通过cert验证。

然后在https请求的时候,指定使用自己自定义的trustManager。

//keyStore初始化
KeyStore ks;
try {
     ks = KeyStore.getInstance(KeyStore.getDefaultType());
     // Note: KeyStore requires it be loaded even if you don't load anything into it:
     ks.load(null);
 } catch (Exception e) {
     log.error("keystore load cert error:" + e.getMessage());
     return;
 }

 // Base64解码
 BASE64Decoder decoder = new BASE64Decoder();
 //获取CertificateFactory 
 CertificateFactory cf = null;
 try {
     cf = CertificateFactory.getInstance("X509");
 } catch (CertificateException e) {
     log.error(e);
 }

//将指定证书certString加载到CertificateFactory 
InputStream in = null;
X509Certificate cert = null;
try {
    byte[] byteCert = decoder.decodeBuffer(certString);
    in = new ByteArrayInputStream(byteCert);
    cert = (X509Certificate) cf.generateCertificate(in);
} catch (IOException | CertificateException e) {
    log.error(e);
    return;
} finally {
    if (null != in) {
        try {
            in.close();
        } catch (IOException e) {
            //ignore
            log.debug(e);
        }
    }
}
//将添加了证书的cert设置到keyStore
ks.setCertificateEntry(UUID.randomUUID().toString(), cert);
TrustManagerFactory tmf = null;
try {
    tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    //将keyStore初始化到TrustManagerFactory
    tmf.init(ks);
} catch (NoSuchAlgorithmException | KeyStoreException e) {
    log.error(e);
    return;
}

//将新增证书的trustManager添加到extraX509TrustManagers中
for (TrustManager tm : tmf.getTrustManagers()) {
     if (tm instanceof X509TrustManager) {
         extraX509TrustManagers.add((X509TrustManager) tm);
         break;
     }
 }
重写check方法
@Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        try {
        //JDK自带的check
            defaultX509TrustManager.checkServerTrusted(x509Certificates, s);
        } catch (CertificateException e) {
        	//extraX509TrustManagers中包含动态添加的证书
            while (extraX509TrustManagers.iterator().hasNext()) {
                X509TrustManager tm = extraX509TrustManagers.iterator().next();
                try {
                    tm.checkServerTrusted(x509Certificates, s);
                    return;
                } catch( CertificateException exception ) {
                    // ignore
                    log.error(exception);
                }
            }
            //动态添加的证书也未校验通过,则抛出异常,无法识别https的安全性
            throw new CertificateException(e.getMessage());
        }
    }
获取所有授信的证书列表
 @Override
    public X509Certificate[] getAcceptedIssuers() {
        final ArrayList list = new ArrayList();
        list.addAll(Arrays.asList(defaultX509TrustManager.getAcceptedIssuers()));
        Iterator iterator = extraX509TrustManagers.iterator();
        while (iterator.hasNext()) {
            X509TrustManager tm = iterator.next();
            list.addAll(Arrays.asList(tm.getAcceptedIssuers()));
        }

        return list.toArray(new X509Certificate[list.size()]);
    }

你可能感兴趣的:(java)