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
最新評論