diff --git a/src/main/java/twgc/gm/sm2/SM2Util.java b/src/main/java/twgc/gm/sm2/SM2Util.java index bcad125..0ce9b95 100644 --- a/src/main/java/twgc/gm/sm2/SM2Util.java +++ b/src/main/java/twgc/gm/sm2/SM2Util.java @@ -1,9 +1,6 @@ package twgc.gm.sm2; -import java.io.FileInputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.StringWriter; +import java.io.*; import java.math.BigInteger; import java.security.*; import java.security.cert.CertificateEncodingException; @@ -13,6 +10,7 @@ import java.security.spec.ECGenParameterSpec; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; +import java.util.function.Supplier; import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.gm.GMNamedCurves; @@ -185,8 +183,99 @@ public static String pemFrom(X509Certificate x509Certificate) throws IOException } public static PrivateKey loadPrivFromFile(String filename, String password) throws IOException, OperatorCreationException, PKCSException { + return loadPriv(password, () -> { + try { + return new FileReader(filename); + } catch (FileNotFoundException e) { + throw new RuntimeException("Private key \"" + filename + "\" not found", e); + } + }); + } + + public static PublicKey loadPublicFromFile(String filename) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { + return loadPublic(() -> { + try { + return new FileReader(filename); + } catch (FileNotFoundException e) { + throw new RuntimeException("Public key \"" + filename + "\" not found", e); + } + }); + } + + public static X509Certificate loadX509CertificateFromFile(String filename) throws IOException, CertificateException, + NoSuchProviderException { + try (FileInputStream in = new FileInputStream(filename)) { + CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); + return (X509Certificate) cf.generateCertificate(in); + } + } + + /** + * 从字符串加载私钥 + * + * @param privateKey 字符串字私钥 + * @param password 密码 + * @return {@link PrivateKey} 私钥对象 + * @throws IOException + * @throws OperatorCreationException + * @throws PKCSException + */ + public static PrivateKey loadPrivFromString(String privateKey, String password) throws IOException, OperatorCreationException, PKCSException { + return loadPriv(password, () -> new StringReader(privateKey)); + } + + /** + * 从字符串加载公钥 + * + * @param publicKey 字符串公钥 + * @return {@link PublicKey} 公钥对象 + * @throws IOException + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + */ + public static PublicKey loadPublicFromString(String publicKey) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { + return loadPublic(() -> new StringReader(publicKey)); + } + + /** + * 从字符串加载证书 + * + * @param cert 字符串证书 + * @return {@link X509Certificate} 证书对象 + * @throws IOException + * @throws CertificateException + * @throws NoSuchProviderException + */ + public static X509Certificate loadX509CertificateFromString(String cert) throws IOException, CertificateException, NoSuchProviderException { + try (InputStream in = new ByteArrayInputStream(cert.getBytes())) { + CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); + return (X509Certificate) cf.generateCertificate(in); + } + } + + public static PublicKey derivePublicFromPrivate(PrivateKey privateKey) { + BCECPrivateKey localECPrivateKey = (BCECPrivateKey) privateKey; + BigInteger d = localECPrivateKey.getD(); + ECPoint ecpoint = new FixedPointCombMultiplier().multiply(GMNamedCurves.getByName(Const.CURVE_NAME).getG(), d); + ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ecpoint, PARAMETER_SPEC); + return new BCECPublicKey(privateKey.getAlgorithm(), pubKeySpec, + BouncyCastleProvider.CONFIGURATION); + } + + /** + * 加载私钥 + * + * @param password 密码 + * @param fx {@link Reader} 回调函数 + * @return {@link PrivateKey} + * @throws IOException + * @throws OperatorCreationException + * @throws PKCSException + */ + public static PrivateKey loadPriv(String password, Supplier fx) throws IOException, OperatorCreationException, PKCSException { PrivateKey priv = null; - try (PEMParser pemParser = new PEMParser(new FileReader(filename))) { + try (PEMParser pemParser = new PEMParser(fx.get())) { Object obj = pemParser.readObject(); if (password != null && password.length() > 0) { if (obj instanceof PKCS8EncryptedPrivateKeyInfo) { @@ -204,28 +293,21 @@ public static PrivateKey loadPrivFromFile(String filename, String password) thro return priv; } - public static PublicKey loadPublicFromFile(String filename) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { - try (PemReader pemReader = new PemReader(new FileReader(filename))) { + /** + * 加载公钥 + * + * @param fx {@link Reader} 回调函数 + * @return {@link PublicKey} + * @throws IOException + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + */ + public static PublicKey loadPublic(Supplier fx) throws IOException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException { + try (PemReader pemReader = new PemReader(fx.get())) { PemObject spki = pemReader.readPemObject(); Security.getProvider(BouncyCastleProvider.PROVIDER_NAME); return KeyFactory.getInstance(Const.EC_VALUE, BouncyCastleProvider.PROVIDER_NAME).generatePublic(new X509EncodedKeySpec(spki.getContent())); } } - - public static X509Certificate loadX509CertificateFromFile(String filename) throws IOException, CertificateException, - NoSuchProviderException { - try (FileInputStream in = new FileInputStream(filename)) { - CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); - return (X509Certificate) cf.generateCertificate(in); - } - } - - public static PublicKey derivePublicFromPrivate(PrivateKey privateKey) { - BCECPrivateKey localECPrivateKey = (BCECPrivateKey) privateKey; - BigInteger d = localECPrivateKey.getD(); - ECPoint ecpoint = new FixedPointCombMultiplier().multiply(GMNamedCurves.getByName(Const.CURVE_NAME).getG(), d); - ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(ecpoint, PARAMETER_SPEC); - return new BCECPublicKey(privateKey.getAlgorithm(), pubKeySpec, - BouncyCastleProvider.CONFIGURATION); - } } \ No newline at end of file diff --git a/src/test/java/SM2UtilTest.java b/src/test/java/SM2UtilTest.java index 6809572..b3d77a6 100644 --- a/src/test/java/SM2UtilTest.java +++ b/src/test/java/SM2UtilTest.java @@ -1,10 +1,12 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Paths; import java.security.*; import java.security.cert.X509Certificate; import java.util.Date; +import java.util.Map; import javax.security.auth.x500.X500Principal; import org.apache.commons.lang3.RandomStringUtils; @@ -25,7 +27,7 @@ import twgc.gm.random.RandomSNAllocator; import twgc.gm.sm2.SM2Util; import twgc.gm.sm2.SM2X509CertFactory; - +import twgc.gm.utils.ConfigLoader; @FixMethodOrder(MethodSorters.JVM) @@ -46,6 +48,8 @@ public class SM2UtilTest { X509Certificate x509Certificate; KeyPair keyPair; + Map> configMap; + public static void saveCSRInPem(PKCS10CertificationRequest csr, String csrFile) throws IOException, OperatorCreationException { String csrPem = SM2Util.pemFrom(csr); Files.write(Paths.get(csrFile), csrPem.getBytes()); @@ -126,6 +130,19 @@ public void generateFile() { Assert.assertTrue(privFile.exists()); Assert.assertTrue(reqFile.exists()); Assert.assertTrue(certFile.exists()); + + } + + @Before + public void loadTestDataConfigMap() { + try { + InputStream in = SM2UtilTest.class.getResourceAsStream("testdata.yml"); + this.configMap = ConfigLoader.loadConfig(in); + + Assert.assertNotNull(this.configMap); + } catch (Exception e) { + Assert.fail(exceptionHappened); + } } //encrypt and decrypt @@ -291,6 +308,144 @@ public void issueCertificate() throws Exception { } + /** + * 测试从 `testdata.yml` 加载配置文件 + * + * @throws IOException + */ + @Test + public void testLoadConfigMap() { + Map javagm = this.configMap.get("javagm"); + Assert.assertNotNull(javagm); + + Object testdata = javagm.get("testdata"); + + Assert.assertNotNull(testdata); + String publicKey = (String) ((Map) testdata).get("public-key"); + String privateKey = (String) ((Map) testdata).get("private-key"); + String cert = (String) ((Map) testdata).get("cert"); + + Assert.assertNotNull(publicKey); + Assert.assertNotNull(privateKey); + Assert.assertNotNull(cert); + } + + /** + * 测试从从字符串加载私钥对象 + * + *
+     * javagm:
+     *   testdata:
+     *     private-key: |
+     *       -----BEGIN PRIVATE KEY-----
+     *       MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgc0UCgfELjC0V+xUm
+     *       ELYFmy0J0cee42ZpKyQ4FRTBlJSgCgYIKoEcz1UBgi2hRANCAATJbIFbxcAaDxMk
+     *       7XExTRU/bBnGEu6YfaleJxnLZS40NDNjZV+ztveWfLZk2+oWieykM3/yZ/6IieJk
+     *       5uuohUjD
+     *       -----END PRIVATE KEY-----
+     *     public-key: |
+     *       -----BEGIN PUBLIC KEY-----
+     *       MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyWyBW8XAGg8TJO1xMU0VP2wZxhLu
+     *       mH2pXicZy2UuNDQzY2Vfs7b3lny2ZNvqFonspDN/8mf+iIniZObrqIVIww==
+     *       -----END PUBLIC KEY-----
+     *     cert: |
+     *       -----BEGIN CERTIFICATE-----
+     *       MIIBdzCCAR2gAwIBAgIJAfA3Qnph7CieMAoGCCqBHM9VAYN1MBIxEDAOBgNVBAMM
+     *       B1Jvb3QgQ0EwHhcNMjMxMjAyMTQzMjMxWhcNODkxMjI3MDAwMDAwWjASMRAwDgYD
+     *       VQQDEwdSb290IENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyWyBW8XAGg8T
+     *       JO1xMU0VP2wZxhLumH2pXicZy2UuNDQzY2Vfs7b3lny2ZNvqFonspDN/8mf+iIni
+     *       ZObrqIVIw6NcMFowHQYDVR0OBBYEFOMvj2LPGlkOw1M1Pj34klVi8SFgMA8GA1Ud
+     *       EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMBgGA1UdEQQRMA+BDXRlc3RAdHdn
+     *       Yy5jb20wCgYIKoEcz1UBg3UDSAAwRQIgTeoLjt+eP3kwQg17G+l12wj4MQNed1hW
+     *       aZZkJe43rkICIQCdI3WhnrvzhbEijsTXL1woIwnFgY9MIci7BmKLMpMM6w==
+     *       -----END CERTIFICATE-----
+     * 
+ */ + @Test + public void testLoadPrivFromString() throws Exception { + Map javagm = this.configMap.get("javagm"); + Object testdata = javagm.get("testdata"); + String privateKey = (String) ((Map) testdata).get("private-key"); + + PrivateKey privKey = SM2Util.loadPrivFromString(privateKey, ""); + Assert.assertNotNull(privKey); + } + + /** + * 测试从从字符串加载公钥对象 + * + * @throws Exception + */ + @Test + public void testLoadPublicFromString() throws Exception { + Map javagm = this.configMap.get("javagm"); + Object testdata = javagm.get("testdata"); + + String publicKey = (String) ((Map) testdata).get("public-key"); + PublicKey pubKey = SM2Util.loadPublicFromString(publicKey); + Assert.assertNotNull(pubKey); + } + + /** + * 测试从字符串加载密钥对并测试加解密 + * + * @throws Exception + */ + @Test + public void testLoadPublicAndPrivFromString() throws Exception { + Map javagm = this.configMap.get("javagm"); + Object testdata = javagm.get("testdata"); + + String publicKey = (String) ((Map) testdata).get("public-key"); + String privateKey = (String) ((Map) testdata).get("private-key"); + + PublicKey pubKey = SM2Util.loadPublicFromString(publicKey); + PrivateKey privKey = SM2Util.loadPrivFromString(privateKey, ""); + + Assert.assertNotNull(pubKey); + Assert.assertNotNull(privKey); + + SM2EnginePool sm2EnginePool = new SM2EnginePool(1, SM2Engine.Mode.C1C3C2); + SM2Engine sm2Engine = null; + + try { + SM2Util instance = new SM2Util(); + sm2Engine = sm2EnginePool.borrowObject(); + byte[] encrypted = instance.encrypt(sm2Engine, pubKey, message); + byte[] rs = instance.decrypt(sm2Engine, privKey, encrypted); + Assert.assertEquals(new String(message), new String(rs)); + + byte[] encrypted2 = instance.encrypt(sm2Engine, pubKey, "msg".getBytes()); + rs = instance.decrypt(sm2Engine, privKey, encrypted2); + Assert.assertNotEquals(new String(message), new String(rs)); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(exceptionHappened); + } finally { + if (sm2Engine != null) { + sm2EnginePool.returnObject(sm2Engine); + } + } + } + + /** + * 测试从字符串加载证书对象 + * + * @throws Exception + */ + @Test + public void testLoadX509CertificateFromString() throws Exception { + Map javagm = this.configMap.get("javagm"); + Object testdata = javagm.get("testdata"); + + String cert = (String) ((Map) testdata).get("cert"); + Assert.assertNotNull(cert); + + X509Certificate certificate = SM2Util.loadX509CertificateFromString(cert); + Assert.assertNotNull(certificate); + Assert.assertEquals("SM3WITHSM2", certificate.getSigAlgName()); + } + static { try { Security.addProvider(new BouncyCastleProvider()); diff --git a/src/test/resources/testdata.yml b/src/test/resources/testdata.yml new file mode 100644 index 0000000..036743b --- /dev/null +++ b/src/test/resources/testdata.yml @@ -0,0 +1,25 @@ +javagm: + testdata: + private-key: | + -----BEGIN PRIVATE KEY----- + MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgc0UCgfELjC0V+xUm + ELYFmy0J0cee42ZpKyQ4FRTBlJSgCgYIKoEcz1UBgi2hRANCAATJbIFbxcAaDxMk + 7XExTRU/bBnGEu6YfaleJxnLZS40NDNjZV+ztveWfLZk2+oWieykM3/yZ/6IieJk + 5uuohUjD + -----END PRIVATE KEY----- + public-key: | + -----BEGIN PUBLIC KEY----- + MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyWyBW8XAGg8TJO1xMU0VP2wZxhLu + mH2pXicZy2UuNDQzY2Vfs7b3lny2ZNvqFonspDN/8mf+iIniZObrqIVIww== + -----END PUBLIC KEY----- + cert: | + -----BEGIN CERTIFICATE----- + MIIBdzCCAR2gAwIBAgIJAfA3Qnph7CieMAoGCCqBHM9VAYN1MBIxEDAOBgNVBAMM + B1Jvb3QgQ0EwHhcNMjMxMjAyMTQzMjMxWhcNODkxMjI3MDAwMDAwWjASMRAwDgYD + VQQDEwdSb290IENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEyWyBW8XAGg8T + JO1xMU0VP2wZxhLumH2pXicZy2UuNDQzY2Vfs7b3lny2ZNvqFonspDN/8mf+iIni + ZObrqIVIw6NcMFowHQYDVR0OBBYEFOMvj2LPGlkOw1M1Pj34klVi8SFgMA8GA1Ud + EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMBgGA1UdEQQRMA+BDXRlc3RAdHdn + Yy5jb20wCgYIKoEcz1UBg3UDSAAwRQIgTeoLjt+eP3kwQg17G+l12wj4MQNed1hW + aZZkJe43rkICIQCdI3WhnrvzhbEijsTXL1woIwnFgY9MIci7BmKLMpMM6w== + -----END CERTIFICATE----- \ No newline at end of file