Java tutorial
/* * Copyright (C) 2017 FormKiQ Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.formkiq.core.service.crypto; import java.io.IOException; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Base64; import java.util.Date; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v1CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import com.formkiq.core.util.Strings; /** * Crypto helper class for generating keys and certificates. * */ public class KeyGenerator { /** Add {@link BouncyCastleProvider}. */ private static final JcaX509CertificateConverter CONVERTER = new JcaX509CertificateConverter() .setProvider(new BouncyCastleProvider()); /** Signature Algorithm. */ private static final String SIGNATURE_ALGORITHM = "SHA256WithRSA"; /** serial the certificate serial number. */ private static final BigInteger SERIAL = new BigInteger("1"); /** * Generate Key Pair. * @param keysize int * @return {@link KeyPair} * @throws NoSuchAlgorithmException NoSuchAlgorithmException */ public KeyPair generateKeyPair(final int keysize) throws NoSuchAlgorithmException { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(keysize); KeyPair kp = kpg.generateKeyPair(); return kp; } /** * Generate Certificate for KeyPair. * @param keys {@link KeyPair} * @param issuer {@link X500Name} * @return {@link Certificate}[] */ public X509Certificate getCertificate(final KeyPair keys, final X500Name issuer) { try { X509v1CertificateBuilder certificateBuilder = getCertificateBuilder(keys.getPublic(), issuer); X509CertificateHolder certificateHolder = certificateBuilder.build(getSigner(keys)); return CONVERTER.getCertificate(certificateHolder); } catch (CertificateException e) { throw new RuntimeException(e); } } /** * Build {@link X509v1CertificateBuilder}. * @param publicKey {@link PublicKey} * @param issuer {@link X500Name} * @return {@link X509v1CertificateBuilder}. */ private X509v1CertificateBuilder getCertificateBuilder(final PublicKey publicKey, final X500Name issuer) { X500Name subject = issuer; try { Date notafter = new SimpleDateFormat("yyyy-MM-dd").parse("3000-01-01"); Date notbefore = new SimpleDateFormat("yyyy-MM-dd").parse("2000-01-01"); return new X509v1CertificateBuilder(issuer, SERIAL, notbefore, notafter, subject, getPublicKeyInfo(publicKey)); } catch (ParseException e) { throw new RuntimeException(e); } } /** * Get {@link SubjectPublicKeyInfo}. * @param publicKey {@link PublicKey} * @return {@link SubjectPublicKeyInfo} */ private SubjectPublicKeyInfo getPublicKeyInfo(final PublicKey publicKey) { if (!(publicKey instanceof RSAPublicKey)) { throw new RuntimeException("publicKey is not an RSAPublicKey"); } RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey; try { return SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo( new RSAKeyParameters(false, rsaPublicKey.getModulus(), rsaPublicKey.getPublicExponent())); } catch (IOException e) { throw new RuntimeException(e); } } /** * Get Signer {@link KeyPair}. * @param keys {@link KeyPair} * @return {@link ContentSigner} */ private ContentSigner getSigner(final KeyPair keys) { try { return new JcaContentSignerBuilder(SIGNATURE_ALGORITHM).setProvider(new BouncyCastleProvider()) .build(keys.getPrivate()); } catch (OperatorCreationException e) { throw new RuntimeException(e); } } /** * Converts Text to RSAPrivateKey. * @param text {@link String} * @return {@link RSAPublicKey} * @throws GeneralSecurityException GeneralSecurityException */ public RSAPrivateKey stringToPrivateKey(final String text) throws GeneralSecurityException { PKCS8EncodedKeySpec specPriv = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(text)); KeyFactory kf = KeyFactory.getInstance("RSA"); RSAPrivateKey key = (RSAPrivateKey) kf.generatePrivate(specPriv); return key; } /** * Converts Text to RSAPublicKey. * @param text {@link String} * @return {@link RSAPublicKey} * @throws GeneralSecurityException GeneralSecurityException */ public RSAPublicKey stringToPublicKey(final String text) throws GeneralSecurityException { X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(text)); KeyFactory kf = KeyFactory.getInstance("RSA"); return (RSAPublicKey) kf.generatePublic(spec); } /** * Convert Key to String. * @param key {@link Key} * @return {@link String} */ public String toString(final Key key) { return Base64.getEncoder().encodeToString(key.getEncoded()); } /** * Convert {@link Certificate} to {@link String}. * @param cert {@link Certificate} * @return {@link String} * @throws CertificateEncodingException CertificateEncodingException */ public String convertToPem(final Certificate cert) throws CertificateEncodingException { String begin = "-----BEGIN CERTIFICATE-----\n"; String end = "-----END CERTIFICATE-----"; byte[] derCert = cert.getEncoded(); return begin + Strings.toString(Base64.getEncoder().encode(derCert)) + end; } }