ECC证书操作汇总(ECC certificate operations summary)
背景介绍
项目里用到了ECC的证书使用,有一些证书的常规操作,在此进行汇总说明,以便后浪来踏。ECC的public key长度为65字节,第一个字节是标识符0x04,32字节biginteger X, 32字节biginteger Y; private key的长度是 32字节的biginteger。以下代码示例均为ECC secp256r1 曲线。
协商秘钥
张三李四要加密通信,只要持有对方的ECC公钥就可以了。在具体交互时采用AES加密的方式,但每次交互AES秘钥是不同的,AES秘钥在交互时也不参与传输。对方通过当次会话传输过来的临时公钥(ECC Eph_pubKey)进行动态计算来获得本次的AES秘钥,然后用以解密数据包。
import javax.crypto.KeyAgreement; public static byte[] keyAgree(PrivateKey selfSK, PublicKey otherPK) { try { KeyAgreement ka = KeyAgreement.getInstance("ECDH"); ka.init(selfSK); ka.doPhase(otherPK, true); return ka.generateSecret(); } catch (Exception e) { e.printStackTrace(); } return null; }
举例过程 张三 -> 李四,张三持有李四的长期公钥。
- 张三:发送 加密 AES = KeyAgree(张三的临时私钥,李四的长期公钥), 传输内容:张三的临时公钥 (65字节固定)+ 密文;
- 李四:收到 解密 AES = KeyAgree(李四的长期私钥,张三的临时公钥),解密密文得到明文;
反之,李四 -> 张三 是对等的。
解析ECC证书&私钥
将文本证书及私钥文件反序列化为X509 Cert & ECC private key。
1 public static X509Certificate deserialize(String path) { 2 try { 3 String cert = Files.lines(Paths.get(path)) 4 .filter(line -> !line.startsWith("-----")) 5 .collect(Collectors.joining()); 6 if (StringUtils.isBlank(cert)) { 7 return null; 8 } 9 CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 10 ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decodeBase64(base64Cert)); 11 return (X509Certificate) (certFactory.generateCertificate(bais)); 12 } catch (IOException e) { 13 e.printStackTrace(); 14 } 15 return null; 16 } 17 18 19 20 public static PrivateKey deserialize(String path) { 21 try { 22 String key = Files.lines(Paths.get(path)) 23 .filter(line -> !line.startsWith("-----")) 24 .collect(Collectors.joining()); 25 if (StringUtils.isBlank(key)) { 26 return null; 27 } 28 29 EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(key)); 30 KeyFactory kf = KeyFactory.getInstance("EC"); 31 return kf.generatePrivate(privateKeySpec); 32 } catch (Exception e) { 33 e.printStackTrace(); 34 } 35 return null; 36 }
十六进制字符串公私钥反序列化
How to convert hex string to ECC public key ?
1 public static PublicKey deserializePubKey(String hexPubKey) throws Exception { 2 byte[] pubKey = Hex.decode(hexPubKey) 3 byte[] x = Arrays.copyOfRange(pubKey, 1, 33); // the first byte is 0x04 flag, ignored 4 byte[] y = Arrays.copyOfRange(pubKey, 33, pubKey.length); 5 ECPoint w = new ECPoint(new BigInteger(1, x), new BigInteger(1, y)); 6 KeyFactory kf = KeyFactory.getInstance("EC"); 7 return kf.generatePublic(new ECPublicKeySpec(w, ecParameterSpecForP256())); 8 } 9 10 public static PrivateKey deserializePriKey(String hexPriKey) throws Exception { 11 KeyFactory kf = KeyFactory.getInstance("EC"); 12 return kf.generatePrivate(new ECPrivateKeySpec(new BigInteger(Hex.decode(hexPriKey)), ecParameterSpecForP256())); 13 } 14 15 private static ECParameterSpec ecParameterSpecForP256() throws Exception { 16 AlgorithmParameters params = AlgorithmParameters.getInstance("EC"); 17 params.init(new ECGenParameterSpec("secp256r1")); 18 return params.getParameterSpec(ECParameterSpec.class); 19 }
以上。