首頁>技術>

keytool 可以生成公私鑰對存在 keystore 中,但是對於已有的公私鑰對需要存入 keystore 時需要用程式生成。KeyStore 內有 store 方法存入公私鑰,但其介面長成這樣

    public final void setKeyEntry(String alias, Key key, char[] password, Certificate[] chain)        public final void store(OutputStream stream, char[] password)

這裡需要 Certificate 可如何是好。複製黏貼一個前輩們的做法:

    private X509Certificate generateCertificate(String dn, KeyPair keyPair, int validity, String sigAlgName) throws            IOException, CertificateException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException,            SignatureException {        PrivateKey privateKey = keyPair.getPrivate();        X509CertInfo info = new X509CertInfo();        Date from = new Date();        Date to = new Date(from.getTime() + validity * 24L * 60L * 60L * 1000L);        CertificateValidity interval = new CertificateValidity(from, to);        BigInteger serialNumber = new BigInteger(64, new SecureRandom());        X500Name owner = new X500Name(dn);        AlgorithmId sigAlgId = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);        info.set(X509CertInfo.VALIDITY, interval);        info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(serialNumber));        info.set(X509CertInfo.SUBJECT, owner);        info.set(X509CertInfo.ISSUER, owner);        info.set(X509CertInfo.KEY, new CertificateX509Key(keyPair.getPublic()));        info.set(X509CertInfo.VERSION, new CertificateVersion((CertificateVersion.V3)));        info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(sigAlgId));        // Sign the cert to identify the algorithm that's used.        X509CertImpl certificate = new X509CertImpl(info);        certificate.sign(privateKey, sigAlgName);        // Update the algorithm, and resign.        sigAlgId = (AlgorithmId) certificate.get(X509CertImpl.SIG_ALG);        info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, sigAlgId);        certificate = new X509CertImpl(info);        certificate.sign(privateKey, sigAlgName);        return certificate;    }

這個函式可以將 KeyPair 簽名成 Certificate ,然後再呼叫 KeyStore 介面就能 存入了,比如

    public void store(String fileName, String alias, KeyPair keyPair, int validity) throws CertificateException,            NoSuchAlgorithmException, IOException, SignatureException, NoSuchProviderException, InvalidKeyException,            KeyStoreException {        Certificate[] chain = {generateCertificate("cn=Unknown", keyPair, validity, "SHA256withRSA")};        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());        keyStore.load(null, password);        keyStore.setKeyEntry(alias, keyPair.getPrivate(), password, chain);        FileOutputStream fos = new FileOutputStream(new File(fileName));        keyStore.store(fos, password);    }

這只是針對於有 KeyPair 的情況下,但是萬一你只有公私鑰的 Base64 編碼的字串呢,比如

Public key MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5V4ZmKBcrJB7MT+sf0LL6pccya9jJ31DH3MbWdwelOk7fCfAJ0HZG4lT6FkwngztmAFLgQKGpMzwsMOt5hQIDqOgSCSs/LjDmNX0jPWQgO1n2SBWD9+fboVIGhomd6x2XZ2yLWOvHCRicefzF4RM7mXKnY3lM9Kj2npRY6F51I3BWWxAwS/C5eMcwUarBx4Khqd/cMtGCUp1dPvricqJIj3PR3Yfe7JyT7ht9x93qbqvoNf755xsohI666cF/KDMHjCUq4qaoA77JRoT0CKTWinljYr3WLL2Fb8s40LBbkolCvgzIiaKtGR3GifE/7gdyMEtLp8Dy+yyXHe+q6M3g1eL5wan5DV0z/h8YRBfSZxCpxGP0iaY2KeE4KWc51udh+7sSJ7aNTJRsZFj+LfYD8ncI2QagE3P82ElWGBqO0ewwnDYD4esRrmU/nTEDcO963iHsMh/Yz2uv1GfhyUS53LOnq7RKFhVlrjz52r8XbvaK2xq5whkfOPthgP5vdFIbhztvwGRNTGjnKsIOje2Z+0/X89iP4rJ4x3jaTB3zkNhrbpYgWUikeoksAbbSmyD5QnlVFNieszf9eELQE2torI1F5jBk0RnKHaPBcPRYh9Rel8/U99ZfzFNTb8NiNMUtajNbBeFILxpGDJHdXZf5lcpWga/RDdHkjVaEMLZgf0CAwEAAQ==Private key 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

這時候需要將這兩個字串轉化為 PublicKey 和 PrivateKey 然後再存入 KeyStore 。 其實 KeyStore 預設使用的生成key的標準是 PKCS8EncodedKeySpec 和 X509EncodedKeySpec,透過自己構建即可

    public void store(String fileName, String alias, String privateKeyBase64, String publicKeyBase64, int validity)            throws NoSuchAlgorithmException, InvalidKeySpecException, CertificateException, IOException,            KeyStoreException, SignatureException, NoSuchProviderException, InvalidKeyException {        KeyFactory keyFactory = KeyFactory.getInstance(algorithm);        PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBase64));        PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);        X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64));        PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);        store(fileName, alias, new KeyPair(publicKey, privateKey), validity);    }

然後再寫一點輔助的函式來驗證一下

public class KeyStoreManager {    private String algorithm;    private int keySize;    private char[] password;    public KeyStoreManager() {        this("RSA", 4096, "123456");    }    public KeyStoreManager(String algorithm, int keySize, String password) {        this.algorithm = algorithm;        this.keySize = keySize;        this.password = password.toCharArray();    }    public KeyPair generate() throws NoSuchAlgorithmException {        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);        keyPairGenerator.initialize(keySize);        return keyPairGenerator.generateKeyPair();    }    public KeyPair load(String fileName, String alias) throws IOException, KeyStoreException, CertificateException,            NoSuchAlgorithmException, UnrecoverableKeyException {        FileInputStream fis = new FileInputStream(new File(fileName));        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());        keyStore.load(fis, password);        PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password);        PublicKey publicKey = keyStore.getCertificate(alias).getPublicKey();        return new KeyPair(publicKey, privateKey);    }

寫個 psvm 來試試

    public static void main(String[] args) {        KeyStoreManager keyStoreManager = new KeyStoreManager();        try {            KeyPair keyPair = keyStoreManager.generate();            String publicKeyString = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());            String privateKeyString = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());            System.out.println("Public key " + publicKeyString);            System.out.println("Private key " + privateKeyString);            String alias = "test";            String testFile = "test.keystore";            keyStoreManager.store(testFile, alias, keyPair, 365);            KeyPair keyPairLoaded = keyStoreManager.load(testFile, alias);            String publicKeyStringLoaded = Base64.getEncoder().encodeToString(keyPairLoaded.getPublic().getEncoded());            String privateKeyStringLoaded = Base64.getEncoder().encodeToString(keyPairLoaded.getPrivate().getEncoded());            String testFileBase = "test.base64.keystore";            keyStoreManager.store(testFileBase, alias, privateKeyString, publicKeyString, 365);            KeyPair keyPairBaseLoaded = keyStoreManager.load(testFileBase, alias);            String publicKeyStringBaseLoaded = Base64.getEncoder().encodeToString(keyPairBaseLoaded.getPublic().getEncoded());            String privateKeyStringBaseLoaded = Base64.getEncoder().encodeToString(keyPairBaseLoaded.getPrivate().getEncoded());            System.out.println("Loaded public key equals " + publicKeyString.equals(publicKeyStringLoaded));            System.out.println("Loaded private key equals " + privateKeyString.equals(privateKeyStringLoaded));            System.out.println("Loaded base64 public key equals " + publicKeyString.equals(publicKeyStringBaseLoaded));            System.out.println("Loaded base64 private key equals " + privateKeyString.equals(privateKeyStringBaseLoaded));        } catch (Exception e) {            e.printStackTrace();        }    }

輸出可能是:

Public key MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5V4ZmKBcrJB7MT+sf0LL6pccya9jJ31DH3MbWdwelOk7fCfAJ0HZG4lT6FkwngztmAFLgQKGpMzwsMOt5hQIDqOgSCSs/LjDmNX0jPWQgO1n2SBWD9+fboVIGhomd6x2XZ2yLWOvHCRicefzF4RM7mXKnY3lM9Kj2npRY6F51I3BWWxAwS/C5eMcwUarBx4Khqd/cMtGCUp1dPvricqJIj3PR3Yfe7JyT7ht9x93qbqvoNf755xsohI666cF/KDMHjCUq4qaoA77JRoT0CKTWinljYr3WLL2Fb8s40LBbkolCvgzIiaKtGR3GifE/7gdyMEtLp8Dy+yyXHe+q6M3g1eL5wan5DV0z/h8YRBfSZxCpxGP0iaY2KeE4KWc51udh+7sSJ7aNTJRsZFj+LfYD8ncI2QagE3P82ElWGBqO0ewwnDYD4esRrmU/nTEDcO963iHsMh/Yz2uv1GfhyUS53LOnq7RKFhVlrjz52r8XbvaK2xq5whkfOPthgP5vdFIbhztvwGRNTGjnKsIOje2Z+0/X89iP4rJ4x3jaTB3zkNhrbpYgWUikeoksAbbSmyD5QnlVFNieszf9eELQE2torI1F5jBk0RnKHaPBcPRYh9Rel8/U99ZfzFNTb8NiNMUtajNbBeFILxpGDJHdXZf5lcpWga/RDdHkjVaEMLZgf0CAwEAAQ==Private key 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Loaded public key equals trueLoaded private key equals trueLoaded base64 public key equals trueLoaded base64 private key equals true

5
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 用Transformer完全替代CNN——ViT