Looly 5 年之前
父节点
当前提交
d5506b7d9e

+ 1 - 0
CHANGELOG.md

@@ -11,6 +11,7 @@
 * 【core  】     Bean字段支持Alias注解(包括转map,转bean等)
 * 【core  】     增加ValueListHandler,优化结果集获取方式
 * 【http  】     支持patch方法(issue#666@Github)
+* 【crypto】     BCUtil支持更加灵活的密钥类型,增加writePemObject方法
 
 ### Bug修复
 

+ 22 - 0
hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java

@@ -297,6 +297,17 @@ public class IoUtil {
 
 	// -------------------------------------------------------------------------------------- getReader and getWriter start
 	/**
+	 * 获得一个文件读取器,默认使用UTF-8编码
+	 *
+	 * @param in 输入流
+	 * @return BufferedReader对象
+	 * @since 5.1.6
+	 */
+	public static BufferedReader getUtf8Reader(InputStream in) {
+		return getReader(in, CharsetUtil.CHARSET_UTF_8);
+	}
+
+	/**
 	 * 获得一个文件读取器
 	 * 
 	 * @param in 输入流
@@ -359,6 +370,17 @@ public class IoUtil {
 	}
 
 	/**
+	 * 获得一个Writer,默认编码UTF-8
+	 *
+	 * @param out 输入流
+	 * @return OutputStreamWriter对象
+	 * @since 5.1.6
+	 */
+	public static OutputStreamWriter getUtf8Writer(OutputStream out) {
+		return getWriter(out, CharsetUtil.CHARSET_UTF_8);
+	}
+
+	/**
 	 * 获得一个Writer
 	 * 
 	 * @param out 输入流

+ 101 - 28
hutool-crypto/src/main/java/cn/hutool/crypto/BCUtil.java

@@ -1,33 +1,34 @@
 package cn.hutool.crypto;
 
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
+import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
+import org.bouncycastle.jce.ECNamedCurveTable;
+import org.bouncycastle.jce.ECPointUtil;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
+import org.bouncycastle.jce.spec.ECNamedCurveSpec;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.io.pem.PemObject;
+import org.bouncycastle.util.io.pem.PemObjectGenerator;
+import org.bouncycastle.util.io.pem.PemReader;
+import org.bouncycastle.util.io.pem.PemWriter;
+
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
 import java.security.GeneralSecurityException;
 import java.security.Key;
 import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.PublicKey;
-import java.security.cert.Certificate;
 import java.security.spec.ECFieldFp;
 import java.security.spec.ECParameterSpec;
 import java.security.spec.ECPoint;
 import java.security.spec.ECPublicKeySpec;
 import java.security.spec.EllipticCurve;
 
-import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
-import org.bouncycastle.jce.ECNamedCurveTable;
-import org.bouncycastle.jce.ECPointUtil;
-import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
-import org.bouncycastle.jce.spec.ECNamedCurveSpec;
-import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.util.io.pem.PemObject;
-import org.bouncycastle.util.io.pem.PemReader;
-
-import cn.hutool.core.io.IORuntimeException;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.CharsetUtil;
-import cn.hutool.core.util.StrUtil;
-
 /**
  * Bouncy Castle相关工具类封装
  *
@@ -98,7 +99,7 @@ public class BCUtil {
 	 * @since 4.5.2
 	 */
 	public static PrivateKey readPrivateKey(InputStream pemStream) {
-		return KeyUtil.generateRSAPrivateKey(readKeyBytes(pemStream));
+		return (PrivateKey) readPemKey(pemStream);
 	}
 
 	/**
@@ -109,29 +110,46 @@ public class BCUtil {
 	 * @since 4.5.2
 	 */
 	public static PublicKey readPublicKey(InputStream pemStream) {
-		final Certificate certificate = KeyUtil.readX509Certificate(pemStream);
-		if (null == certificate) {
-			return null;
-		}
-		return certificate.getPublicKey();
+		return (PublicKey) readPemKey(pemStream);
 	}
 
 	/**
 	 * 从pem文件中读取公钥或私钥<br>
 	 * 根据类型返回{@link PublicKey} 或者 {@link PrivateKey}
 	 *
-	 * @param keyStream pem流
+	 * @param pemKeyStream pem流
 	 * @return {@link Key}
 	 * @since 4.5.2
+	 * @deprecated 请使用{@link #readPemKey(InputStream)}
+	 */
+	@Deprecated
+	public static Key readKey(InputStream pemKeyStream) {
+		return readPemKey(pemKeyStream);
+	}
+
+	/**
+	 * 从pem文件中读取公钥或私钥<br>
+	 * 根据类型返回{@link PublicKey} 或者 {@link PrivateKey}
+	 *
+	 * @param keyStream pem流
+	 * @return {@link Key},null表示无法识别的密钥类型
+	 * @since 5.1.6
 	 */
-	public static Key readKey(InputStream keyStream) {
+	public static Key readPemKey(InputStream keyStream) {
 		final PemObject object = readPemObject(keyStream);
 		final String type = object.getType();
-		if (StrUtil.isNotBlank(type) && type.endsWith("PRIVATE KEY")) {
-			return KeyUtil.generateRSAPrivateKey(object.getContent());
-		} else {
-			return KeyUtil.readX509Certificate(keyStream).getPublicKey();
+		if (StrUtil.isNotBlank(type)) {
+			if (type.endsWith("PRIVATE KEY")) {
+				return KeyUtil.generateRSAPrivateKey(object.getContent());
+			} else if (type.endsWith("PUBLIC KEY")) {
+				return KeyUtil.generateRSAPublicKey(object.getContent());
+			} else if (type.endsWith("CERTIFICATE")) {
+				return KeyUtil.readPublicKeyFromCert(IoUtil.toStream(object.getContent()));
+			}
 		}
+
+		//表示无法识别的密钥类型
+		return null;
 	}
 
 	/**
@@ -140,8 +158,21 @@ public class BCUtil {
 	 * @param keyStream pem流
 	 * @return 密钥bytes
 	 * @since 4.5.2
+	 * @deprecated 使用{@link #readPem(InputStream)}
 	 */
+	@Deprecated
 	public static byte[] readKeyBytes(InputStream keyStream) {
+		return readPem(keyStream);
+	}
+
+	/**
+	 * 从pem流中读取公钥或私钥
+	 *
+	 * @param keyStream pem流
+	 * @return 密钥bytes
+	 * @since 5.1.6
+	 */
+	public static byte[] readPem(InputStream keyStream) {
 		PemObject pemObject = readPemObject(keyStream);
 		if (null != pemObject) {
 			return pemObject.getContent();
@@ -157,9 +188,20 @@ public class BCUtil {
 	 * @since 4.5.2
 	 */
 	public static PemObject readPemObject(InputStream keyStream) {
+		return readPemObject(IoUtil.getUtf8Reader(keyStream));
+	}
+
+	/**
+	 * 读取pem文件中的信息,包括类型、头信息和密钥内容
+	 *
+	 * @param reader pem Reader
+	 * @return {@link PemObject}
+	 * @since 5.1.6
+	 */
+	public static PemObject readPemObject(Reader reader) {
 		PemReader pemReader = null;
 		try {
-			pemReader = new PemReader(IoUtil.getReader(keyStream, CharsetUtil.CHARSET_UTF_8));
+			pemReader = new PemReader(reader);
 			return pemReader.readPemObject();
 		} catch (IOException e) {
 			throw new IORuntimeException(e);
@@ -167,4 +209,35 @@ public class BCUtil {
 			IoUtil.close(pemReader);
 		}
 	}
+
+	/**
+	 * 写出pem密钥(私钥、公钥、证书)
+	 *
+	 * @param type      密钥类型(私钥、公钥、证书)
+	 * @param content   密钥内容
+	 * @param keyStream pem流
+	 * @since 5.1.6
+	 */
+	public static void writePemObject(String type, byte[] content, OutputStream keyStream) {
+		writePemObject(new PemObject(type, content), keyStream);
+	}
+
+	/**
+	 * 写出pem密钥(私钥、公钥、证书)
+	 *
+	 * @param pemObject pem对象,包括密钥和密钥类型等信息
+	 * @param keyStream pem流
+	 * @since 5.1.6
+	 */
+	public static void writePemObject(PemObjectGenerator pemObject, OutputStream keyStream) {
+		PemWriter writer = null;
+		try {
+			writer = new PemWriter(IoUtil.getUtf8Writer(keyStream));
+			writer.writeObject(pemObject);
+		} catch (IOException e) {
+			throw new IORuntimeException(e);
+		} finally {
+			IoUtil.close(writer);
+		}
+	}
 }

+ 7 - 8
hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2.java

@@ -1,9 +1,8 @@
 package cn.hutool.crypto.asymmetric;
 
-import java.security.InvalidKeyException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
+import cn.hutool.crypto.CryptoException;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.crypto.asymmetric.SM2Engine.SM2Mode;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
@@ -12,9 +11,9 @@ import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.SM2Signer;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 
-import cn.hutool.crypto.CryptoException;
-import cn.hutool.crypto.SecureUtil;
-import cn.hutool.crypto.asymmetric.SM2Engine.SM2Mode;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
 
 /**
  * 国密SM2算法实现,基于BC库<br>
@@ -41,7 +40,7 @@ public class SM2 extends AbstractAsymmetricCrypto<SM2> {
 	 * 构造,生成新的私钥公钥对
 	 */
 	public SM2() {
-		this((byte[]) null, (byte[]) null);
+		this(null, (byte[]) null);
 	}
 
 	/**

+ 2 - 4
hutool-crypto/src/main/java/cn/hutool/crypto/asymmetric/SM2Engine.java

@@ -199,12 +199,10 @@ public class SM2Engine {
 		this.digest.doFinal(c3, 0);
 
 		// 按照对应模式输出结果
-		switch (mode) {
-		case C1C3C2:
+		if (mode == SM2Mode.C1C3C2) {
 			return Arrays.concatenate(c1, c3, c2);
-		default:
-			return Arrays.concatenate(c1, c2, c3);
 		}
+		return Arrays.concatenate(c1, c2, c3);
 	}
 
 	/**

+ 7 - 1
hutool-crypto/src/test/java/cn/hutool/crypto/test/BCUtilTest.java

@@ -24,7 +24,13 @@ public class BCUtilTest {
 		PublicKey publicKey = BCUtil.readPublicKey(ResourceUtil.getStream("test_public_key.csr"));
 		Assert.assertNotNull(publicKey);
 	}
-	
+
+	@Test
+	public void readPemKeyTest() {
+		PublicKey publicKey = (PublicKey) BCUtil.readPemKey(ResourceUtil.getStream("test_public_key.csr"));
+		Assert.assertNotNull(publicKey);
+	}
+
 	@Test
 	public void validateKey() {
 		PrivateKey privateKey = BCUtil.readPrivateKey(ResourceUtil.getStream("test_private_key.pem"));

+ 2 - 0
hutool-crypto/src/test/java/cn/hutool/crypto/test/SM2Test.java

@@ -4,6 +4,7 @@ import java.security.KeyPair;
 import java.security.PrivateKey;
 import java.security.PublicKey;
 
+import cn.hutool.core.lang.Console;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -47,6 +48,7 @@ public class SM2Test {
 		KeyPair pair = SecureUtil.generateKeyPair("SM2");
 		byte[] privateKey = pair.getPrivate().getEncoded();
 		byte[] publicKey = pair.getPublic().getEncoded();
+		Console.log(HexUtil.encodeHexStr(publicKey));
 
 		SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
 		sm2.setMode(SM2Mode.C1C3C2);