java生成pfx证书[通俗易懂]

java生成pfx证书[通俗易懂]packagecom.zrsf.cert;importjava.io.File;importjava.io.FileInputStream;importjava.io.FileNotFoundException;importjava.io.FileOutputStream;importjava.io.IOException;importjava.mat

大家好,又见面了,我是你们的朋友全栈君。package com.zrsf.cert;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.math.BigInteger;

import java.security.KeyPair;

import java.security.KeyPairGenerator;

import java.security.KeyStore;

import java.security.NoSuchAlgorithmException;

import java.security.PrivateKey;

import java.security.PublicKey;

import java.security.SecureRandom;

import java.security.Signature;

import java.security.SignatureException;

import java.security.cert.Certificate;

import java.security.cert.CertificateException;

import java.security.cert.X509Certificate;

import java.util.Date;

import java.util.HashMap;

import java.util.Map;

import java.util.Vector;

import java.util.Map.Entry;

import sun.security.util.ObjectIdentifier;

import sun.security.x509.AlgorithmId;

import sun.security.x509.CertAndKeyGen;

import sun.security.x509.CertificateAlgorithmId;

import sun.security.x509.CertificateExtensions;

import sun.security.x509.CertificateSerialNumber;

import sun.security.x509.CertificateValidity;

import sun.security.x509.CertificateVersion;

import sun.security.x509.CertificateX509Key;

import sun.security.x509.ExtendedKeyUsageExtension;

import sun.security.x509.KeyIdentifier;

import sun.security.x509.KeyUsageExtension;

import sun.security.x509.SerialNumber;

import sun.security.x509.SubjectKeyIdentifierExtension;

import sun.security.x509.X500Name;

import sun.security.x509.X500Signer;

import sun.security.x509.X509CertImpl;

import sun.security.x509.X509CertInfo;

public class GenX509Cert {


private SecureRandom sr = new SecureRandom();


private String root = “Root”;// 根证书前缀


private static String rootUser = “签名服务器”;// 证书颁发者


public GenX509Cert() {


try {


sr = SecureRandom.getInstance(“SHA1PRNG”, “SUN”);


} catch (Exception e) {


System.err.println(“实例化SecureRandom出错:”+e.getMessage());





}


public void createCert(X509Certificate certificate, PrivateKey rootPrivKey,


KeyPair kp, String certPath, String user, String password,


String sbh, String ip) throws Exception {


byte certbytes[] = certificate.getEncoded();


X509CertImpl x509certimpl = new X509CertImpl(certbytes);


X509CertInfo x509certinfo = (X509CertInfo) x509certimpl


.get(“x509.info”);


x509certinfo.set(“key”, new CertificateX509Key(kp.getPublic()));


CertificateExtensions certificateextensions = new CertificateExtensions();


certificateextensions.set(“SubjectKeyIdentifier”,


new SubjectKeyIdentifierExtension((new KeyIdentifier(kp


.getPublic())).getIdentifier()));


x509certinfo.set(“extensions”, certificateextensions);


// 设置issuer域


X500Name issuer = new X500Name(“CN=” + rootUser


+ “,OU=hackwp,O=wp,L=BJ,S=BJ,C=CN”);


x509certinfo.set(“issuer.dname”, issuer);


X500Name subject = new X500Name(“CN=” + user


+ “, OU=wps, O=wps, L=BJ, ST=BJ, C=CN”);


x509certinfo.set(“subject.dname”, subject);


Signature signature = Signature.getInstance(“MD5WithRSA”);


signature.initSign(kp.getPrivate());


X500Signer signer = new X500Signer(signature, issuer);


AlgorithmId algorithmid = signer.getAlgorithmId();


x509certinfo


.set(“algorithmID”, new CertificateAlgorithmId(algorithmid));


Date bdate = new Date();


Date edate = new Date();


// 天 小时 分 秒 毫秒


edate.setTime(bdate.getTime() + 3650L * 24L * 60L * 60L * 1000L);


// validity为有效时间长度 单位为秒


CertificateValidity certificatevalidity = new CertificateValidity(


bdate, edate);


x509certinfo.set(“validity”, certificatevalidity);


// 设置有效期域(包含开始时间和到期时间)域名等同与x509certinfo.VALIDITY


x509certinfo.set(“serialNumber”, new CertificateSerialNumber(


(int) (new Date().getTime() / 1000L)));


// 设置序列号域


CertificateVersion cv = new CertificateVersion(CertificateVersion.V3);


x509certinfo.set(X509CertInfo.VERSION, cv);


// 设置版本号 只有v1 ,v2,v3这几个合法值


/**


*以上是证书的基本信息 如果要添加用户扩展信息 则比较麻烦 首先要确定version必须是v3否则不行 然后按照以下步骤


**/


String userData = “Digital Signature, Non-Repudiation, Key Encipherment, Data Encipherment (f0)”;


byte l = (byte) userData.length();// 数据总长17位


byte f = 0x04;


byte[] bs = new byte[userData.length() + 2];


bs[0] = f;


bs[1] = l;


for (int i = 2; i < bs.length; i++) {


bs[i] = (byte) userData.charAt(i – 2);


}


KeyUsageExtension keyUsage = new KeyUsageExtension();


keyUsage.set(KeyUsageExtension.DIGITAL_SIGNATURE, true);


keyUsage.set(KeyUsageExtension.NON_REPUDIATION, true);


keyUsage.set(KeyUsageExtension.KEY_ENCIPHERMENT, true);


keyUsage.set(KeyUsageExtension.DATA_ENCIPHERMENT, true);


// 增强密钥用法


ObjectIdentifier ekeyOid = new ObjectIdentifier(new int[] { 1, 3, 6, 1,


5, 5, 7, 3, 3 });


Vector<ObjectIdentifier> vkeyOid = new Vector<ObjectIdentifier>();


vkeyOid.add(ekeyOid);


ExtendedKeyUsageExtension exKeyUsage = new ExtendedKeyUsageExtension(


vkeyOid);


CertificateExtensions exts = new CertificateExtensions();


exts.set(“keyUsage”, keyUsage);


exts.set(“extendedKeyUsage”, exKeyUsage);


// 如果有多个extension则都放入CertificateExtensions 类中,


x509certinfo.set(X509CertInfo.EXTENSIONS, exts);


X509CertImpl x509certimpl1 = new X509CertImpl(x509certinfo);


SerialNumber sn = new SerialNumber(new BigInteger(ip


+ System.currentTimeMillis()));


x509certimpl1.set(X509CertImpl.SERIAL_ID, sn);// 设置证书序列号


x509certimpl1.sign(rootPrivKey, “MD5WithRSA”);// 使用另一个证书的私钥来签名此证书 这里使用


// md5散列 用rsa来加密


Certificate[] certChain = { x509certimpl1 };


savePfx(Util.certCA, kp.getPrivate(), password, certChain, certPath);


// 生成文件


x509certimpl1.verify(certificate.getPublicKey(), null);


}


/**


* 保存pfx文件,里面包括公钥,私钥,证书链别名





* @param alias


* @param privKey


* @param pwd


* @param certChain


* @param filepath


* @throws Exception


*/


public void savePfx(String alias, PrivateKey privKey, String pwd,


Certificate[] certChain, String filepath) throws Exception {


FileOutputStream out = null;


try {


KeyStore outputKeyStore = KeyStore.getInstance(“pkcs12”);


outputKeyStore.load(null, pwd.toCharArray());


outputKeyStore.setKeyEntry(alias, privKey, pwd.toCharArray(),


certChain);


out = new FileOutputStream(filepath);


outputKeyStore.store(out, pwd.toCharArray());


} finally {


if (out != null)


out.close();


}


}


/**


* 生成用户证书





* @param sbh


*            纳税人识别号(文件名,不包含后缀)


* @param user


*            证书使用者


* @param path


*            证书保存路径(不包括文件名),如要保存在D盘的home目录下则输入:D:/home


* @param password


*            证书密码


* @param ip


*            客户端请求的ip


* @return map里面包含两个key,一个code,一个msg,如果code等于0000则为生成成功,如果code不等于0000则为生成失败,


*         msg里面保存失败原因


*/


public Map<String, String> createCA(String sbh, String user, String path,


String password, String ip) {


Map<String, String> map = new HashMap<String, String>();


if (sbh == null || “”.equals(sbh)) {


map.put(“code”, “-1”);


map.put(“msg”, “纳税人识别号不能为空”);


return map;


}


if (user == null || “”.equals(user)) {


map.put(“code”, “-1”);


map.put(“msg”, “证书使用者不能为空”);


return map;


}


if (path == null || “”.equals(path)) {


map.put(“code”, “-1”);


map.put(“msg”, “保存路径不能为空”);


return map;


}


if (password == null || “”.equals(password)) {


map.put(“code”, “-1”);


map.put(“msg”, “证书密码不能为空”);


return map;


}


if (!Util.ipCheck(ip)) {// 验证IP地址是否合法


map.put(“code”, “-1”);


map.put(“msg”, “IP地址不合法”);


return map;


}


if(!new File(path).exists()){


map.put(“code”, “5555”);


map.put(“msg”, “保存文件的目录不存在”);


return map;


}


String rootPath = path + File.separator + root + sbh + “.pfx”;


String certPath = path + File.separator + sbh + “.pfx”;


File file = new File(rootPath);


if (file.exists()) {


map.put(“code”, “6666”);


map.put(“msg”, “文件已存在”);


return map;


}


try {


CertAndKeyGen cak = new CertAndKeyGen(“RSA”, “MD5WithRSA”, null);


// 参数分别为 公钥算法 签名算法 providername(因为不知道确切的 只好使用null 既使用默认的provider)


cak.generate(1024);


cak.setRandom(sr);


// 生成一对key 参数为key的长度 对于rsa不能小于512


X500Name subject = new X500Name(


“CN=root,OU=root,O=wp,L=BJ,S=BJ,C=CN”);


// subject name


X509Certificate certificate = cak.getSelfCertificate(subject,


new Date(), 365L * 24L * 60L * 60L * 1000L * 5L);


X509Certificate[] certs = { certificate };


savePfx(Util.rootCA, cak.getPrivateKey(), password, certs, rootPath);


ip = Util.getIpNum(ip);


signCert(rootPath, certPath, user, password, sbh, ip);


map.put(“code”, “0000”);


map.put(“msg”, “用户证书生成成功”);


return map;


} catch (Exception e) {


if (file != null) {


file.delete();


}


File file2 = new File(certPath);


if (file2.exists())


file2.delete();


map.put(“code”, “9999”);


map.put(“msg”, “生成用户证书发生异常:” + e.getMessage());


return map;


}


}


/**





* @param rootPath


*            根证书路径


* @param certPath


*            用户证书路径


* @param user


*            证书使用者


* @param password


*            证书密码


* @param sbh


*            纳税人识别号


* @param ip


*            请求地址的ip


* @throws Exception


*/


public void signCert(String rootPath, String certPath, String user,


String password, String sbh, String ip) throws Exception {


FileInputStream ksfis = null;


try {


KeyStore ks = KeyStore.getInstance(“pkcs12”);


ksfis = new FileInputStream(rootPath);


char[] storePwd = password.toCharArray();


char[] keyPwd = password.toCharArray();


ks.load(ksfis, storePwd);


// 从密钥仓库得到私钥


PrivateKey privK = (PrivateKey) ks.getKey(Util.rootCA, keyPwd);


X509Certificate certificate = (X509Certificate) ks


.getCertificate(Util.rootCA);


createCert(certificate, privK, genKey(), certPath, user, password,


sbh, ip);


} finally {


if (ksfis != null)


ksfis.close();


}


}


public KeyPair genKey() throws NoSuchAlgorithmException {


KeyPairGenerator kpg = KeyPairGenerator.getInstance(“RSA”);


kpg.initialize(1024, sr);


KeyPair kp = kpg.generateKeyPair();


return kp;


}


/**


* 获取证书序列号和公钥





* @param path


*            证读取证书的路径


* @param pass


*            读取证书的密码


* @return


*/


public Map<String, String> getCertMessage(String path, String pass) {


Map<String, String> map = new HashMap<String, String>();


KeyStore ks;


FileInputStream fis = null;


if (pass == null || “”.equals(pass) || path == null || “”.equals(path)) {


map.put(“code”, “-1”);


map.put(“msg”, “参数错误”);


return map;


}


try {


ks = KeyStore.getInstance(“PKCS12”);


fis = new FileInputStream(path);


char[] nPassword = null;


if ((pass == null) || pass.trim().equals(“”)) {


nPassword = null;


} else {


nPassword = pass.toCharArray();


}


ks.load(fis, nPassword);


X509Certificate cert = (X509Certificate) ks


.getCertificate(Util.certCA);


PublicKey pubkey = cert.getPublicKey();


byte pb[] = Serializer.serialize(pubkey);


map.put(“code”, “0000”);


map.put(“msg”, “数据返回成功”);


map.put(“certNumber”, cert.getSerialNumber().toString());


map.put(“cert”, Base64Utils.encode(pb));


return map;


} catch (FileNotFoundException e) {


map.put(“code”, “1111”);


map.put(“msg”, “文件没有找到:” + e.getMessage());


return map;


} catch (CertificateException e) {


map.put(“code”, “2222”);


map.put(“msg”, “读取证书异常:” + e.getMessage());


return map;


} catch (IOException e) {


map.put(“code”, “4444”);


map.put(“msg”, “IO异常:” + e.getMessage());


return map;


} catch (Exception e) {


map.put(“code”, “9999”);


map.put(“msg”, “未知异常:” + e.getMessage());


return map;


}finally{


if(fis!=null)


try {


fis.close();


} catch (IOException e) {


e.printStackTrace();


}


}


}


/**


* 签名数据





* @param path


*            读取证书路径


* @param pass


*            读取证书密码


* @param data


*            待签名数据


* @return


*/


public Map<String, String> signData(String path, String pass, String data) {


Map<String, String> map = new HashMap<String, String>();


KeyStore ks;


FileInputStream fis = null;


if (pass == null || “”.equals(pass) || path == null || “”.equals(path)


|| data == null || “”.equals(data)) {


map.put(“code”, “-1”);


map.put(“msg”, “参数错误”);


return map;


}


try {


ks = KeyStore.getInstance(“PKCS12”);


fis = new FileInputStream(path);


char[] nPassword = null;


if ((pass == null) || pass.trim().equals(“”)) {


nPassword = null;


} else {


nPassword = pass.toCharArray();


}


ks.load(fis, nPassword);


X509Certificate cert = (X509Certificate) ks.getCertificate(Util.certCA);


PrivateKey prikey = (PrivateKey) ks.getKey(Util.certCA, nPassword);


Signature sig = Signature.getInstance(“MD5WithRSA”);


sig.initSign(prikey);


sig.update(data.getBytes(“UTF-8”));


byte b[] = sig.sign();





map.put(“code”, “0000”);


map.put(“msg”, “数据返回成功”);


map.put(“certNumber”, cert.getSerialNumber().toString());


map.put(“sign”, Base64Utils.encode(b));


return map;


} catch (FileNotFoundException e) {


map.put(“code”, “1111”);


map.put(“msg”, “文件没有找到:” + e.getMessage());


return map;


} catch (CertificateException e) {


map.put(“code”, “2222”);


map.put(“msg”, “读取证书异常:” + e.getMessage());


return map;


} catch (NoSuchAlgorithmException e) {


map.put(“code”, “2222”);


map.put(“msg”, “读取证书异常:” + e.getMessage());


return map;


} catch (SignatureException e) {


map.put(“code”, “3333”);


map.put(“msg”, “签名异常:” + e.getMessage());


return map;


} catch (IOException e) {


map.put(“code”, “4444”);


map.put(“msg”, “IO异常:” + e.getMessage());


return map;


} catch (Exception e) {


map.put(“code”, “9999”);


map.put(“msg”, “未知异常:” + e.getMessage());


return map;


} finally {


if (fis != null)


try {


fis.close();


} catch (IOException e) {


System.out.println(e.getMessage());


}


}


}


public void verify(String key,String sign,String signData) throws Exception{


byte []pb = Base64Utils.decode(key);

         PublicKey pk = (PublicKey) Serializer.unserialize(pb);

         Signature sig2=Signature.getInstance(“MD5WithRSA”);

         sig2.initVerify(pk);

         sig2.update(sign.getBytes(“UTF-8”));

         System.out.println(“验签结果是:”+sig2.verify(Base64Utils.decode(signData)));


}


public static void main(String[] args) {


try {


GenX509Cert gcert = new GenX509Cert();


String data = “adowdn@#&kajdk”;


Map<String, String> map = gcert.createCA(“133456”, “小科”, “e:”,


“123456”, “10.26.27.28”);


System.out.println(map.get(“code”) + “\t” + map.get(“msg”));


Map<String, String> map1 = gcert.getCertMessage(“e:/133456.pfx”,


“123456”);


for (Entry<String, String> e : map1.entrySet()) {


System.out.println(e.getKey() + “–>” + e.getValue());


}


Map<String, String> map2 = gcert.signData(“e:/133456.pfx”,


“123456”, data);


for (Entry<String, String> e : map2.entrySet()) {


System.out.println(e.getKey() + “–>” + e.getValue());


}


gcert.verify(map1.get(“cert”), data, map2.get(“sign”));


} catch (Exception e) {


e.printStackTrace();


}


}

}

package com.zrsf.cert;

import java.io.IOException;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Base64Utils {

/**
* 解码
* @param requestString
* @return
* @throws IOException
*/
public static byte[] decode(String requestString) throws IOException{

return new BASE64Decoder().decodeBuffer(requestString);
}

/*
* 编码
*/
public static String encode(byte[] bytes) throws IOException{

BASE64Encoder enc = new BASE64Encoder();
String encStr =enc.encode(bytes);
return encStr;
}

}

package com.zrsf.cert;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 
* @ClassName Serializer
 */
public final class Serializer
{

    /**
     * 序列化。
     * @param object 需要序列化的对象。
     * @return 字节数组。
     */
    public static final byte[] serialize(Object object)
    {

        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;
        try
        {

            baos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            return baos.toByteArray();
        }
        catch(Exception ex)
        {

            System.err.println(“序列化对象失败”);
        }
        finally
        {

            if(oos != null)
            {

                try {

                    oos.close();
                }catch (IOException e) {

                    e.printStackTrace();
                }
            }
            if(baos != null)
            {

                try {

                    baos.close();
                }catch (IOException e) {

                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 反序列化。
     * @param bytes 字节数组。
     * @return 反序列化后的对象。
     */
    public static final Object unserialize(byte[] bytes)
    {

        ByteArrayInputStream bais = null;
        ObjectInputStream ois = null;
        try
        {

            bais = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bais);
            return ois.readObject();
        }
        catch(Exception ex)
        {

        System.err.println(“反序列化对象失败”);
        }
        finally
        {

            if(ois != null)
            {

                try {

                    ois.close();
                }catch (IOException e) {

                    e.printStackTrace();
                }
            }
            if(bais != null)
            {

                try {

                    bais.close();
                }catch (IOException e) {

                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}

package com.zrsf.cert;

import java.security.SecureRandom;

public class Util {

public static String rootCA = “RootCA”;
public static String certCA = “client”;

/**
* 生成三位随机数
* @return
*/
public static String getRandomNum(){

SecureRandom sr = new SecureRandom();
return “”+sr.nextInt(10)+sr.nextInt(10)+sr.nextInt(10);
}

/**
     * 验证ip是否合法
     * @param ip ip地址
     * @return ip合法返回true,否则返回false
     */
    public static boolean ipCheck(String ip) {

        if (ip != null && !ip.isEmpty()) {

            String regex = “^(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|[1-9])\\.” +
            “(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)\\.” +
            “(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)\\.” +
            “(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|[1-9])$”;
            if (ip.matches(regex)) {

                return true;
            } else {

                return false;
            }
        }
        return false;
    }
    /**
     * 得到ip的数字,不足三位的在后面补0,调用ipCheck方法之后再调用此方法
     * @param ip
     * @return
     */
    public static String getIpNum(String ip){

    StringBuffer sb = new StringBuffer();
    String len[] = ip.split(“\\.”);
    for(int i = 0;i<len.length;i++){

    String s = len[i];
    if(s.length()==3){

    sb.append(s);
    }else if(s.length()==2){

    sb.append(s).append(“0”);
    }else if(s.length()==1){

    sb.append(s).append(“00”);
    }
    }
    return sb.toString();
    }
    

}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/136361.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 最详细的maven配置——报错了你打我[通俗易懂]

    最详细的maven配置——报错了你打我[通俗易懂]目录1、前言2、下载3、配置PATH、settings.xml以及本地仓库3.1、配置path3.2、配置settings.xml和本地仓库4、在IDEA中配置Maven1、前言maven说的简短一点就是一个大型的jar包管理工具,类似于工具人。只要有了maven,就不用去幸幸苦苦的找jar包了。wc,爽哉。好了,还是不多bb,我么还是直接干正事。(切记切记:安装maven必须装好jdk)2、下载首先我们还是去官网。瞅瞅最新版的是哪个版本。点我直达当然,玩Windows的人都知道,我们下

    2022年5月28日
    53
  • windows update更新返回错误码统计(WUSA.exe)「建议收藏」

    windows update更新返回错误码统计(WUSA.exe)「建议收藏」windowsupdate更新返回错误码统计(WUSA.exe)ThisExitCodesorReturnValuescanberetrievedfromanWindowsUpdateInstallation.ReturnCodeReturnCodeHEXMessage23592970x240001WU_S_SERVICE_STOP23592980x240002WU_S_SELFUPDATE23592990x240003W

    2022年5月22日
    52
  • 第12章方差分析介绍课_t检验中的第一类错误是指

    第12章方差分析介绍课_t检验中的第一类错误是指方差分析用以比较两组及以上处理之间的平均数差异的情况因素:方差分析中,用于分派组别的变量水平:因素的个别情形或个别值方差分析与t检验比较(为什么使用方差分析而不用t检验多次比较均值差异):检验α水平:做一个单独的假设检验时犯第一类错误的可能性。实验α水平:当一个实验包括多个不同的假设检验时,实验中全部独立的检验所犯第一类错误积累后的犯错总概率。通常,实验α水平比任何一个单独的检验的α值大。方差分析可在一次实验中同时进行三个及以上均值差异的比较,避免了实验α的膨胀。方差分析…

    2022年10月10日
    3
  • 全免费建站攻略!freewebhostingarea空间+免费tk域名

    全免费建站攻略!freewebhostingarea空间+免费tk域名原文地址:http://www.cguage.com/2010/05/free_space_domain.html前几天,无意中发现dot.tk提供的免费tk域名,现在已经开始支持绑定DNS了。而一个

    2022年7月3日
    27
  • 详细理解HashMap数据结构,太齐全了!「建议收藏」

    写在前面:小伙伴儿们,大家好!今天来学习HashMap相关内容,作为面试必问的知识点,来深入了解一波!思维导图:1,HashMap集合简介HashMap基于哈希表的Map接口实现,是以key-value存储形式存在,即主要用来存放键值对。HashMap的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。JDK1.8之前的HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了节解决哈希碰

    2022年4月17日
    42
  • Unbound classpath container: &#39;JRE System Library [jdk17060]&#39; in project ***

    Unbound classpath container: &#39;JRE System Library [jdk17060]&#39; in project ***

    2022年1月14日
    47

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号