/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.pki; import org.candlepin.common.config.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; 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.PrivateKey; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509CRL; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Date; import java.util.List; import java.util.Set; /** * PKIUtility */ public abstract class PKIUtility { private static Logger log = LoggerFactory.getLogger(PKIUtility.class); // TODO : configurable? public static final int RSA_KEY_SIZE = 2048; public static final String SIGNATURE_ALGO = "SHA1WITHRSA"; protected PKIReader reader; protected SubjectKeyIdentifierWriter subjectKeyWriter; protected Configuration config; public PKIUtility(PKIReader reader, SubjectKeyIdentifierWriter subjectKeyWriter, Configuration config) { this.reader = reader; this.subjectKeyWriter = subjectKeyWriter; this.config = config; } public abstract X509Certificate createX509Certificate(String dn, Set<X509ExtensionWrapper> extensions, Set<X509ByteExtensionWrapper> byteExtensions, Date startDate, Date endDate, KeyPair clientKeyPair, BigInteger serialNumber, String alternateName) throws GeneralSecurityException, IOException; /** * Generate CRL. * * @param entries the entries * @return the x509 CRL */ public abstract X509CRL createX509CRL(List<X509CRLEntryWrapper> entries, BigInteger crlNumber); public KeyPair decodeKeys(byte[] privKeyBits, byte[] pubKeyBits) throws InvalidKeySpecException, NoSuchAlgorithmException { KeyFactory keyFactory = KeyFactory.getInstance("RSA"); // build the private key PrivateKey privKey = keyFactory .generatePrivate(new PKCS8EncodedKeySpec(privKeyBits)); // build the public key PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec( pubKeyBits)); // make them a key pair return new KeyPair(pubKey, privKey); } public KeyPair generateNewKeyPair() throws NoSuchAlgorithmException { KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); generator.initialize(RSA_KEY_SIZE); return generator.generateKeyPair(); } /** * Take an X509Certificate object and return a byte[] of the certificate, * PEM encoded * @param cert * @return PEM-encoded bytes of the certificate * @throws IOException if there is i/o problem */ public abstract byte[] getPemEncoded(X509Certificate cert) throws IOException; public abstract byte[] getPemEncoded(Key key) throws IOException; public abstract byte[] getPemEncoded(X509CRL crl) throws IOException; /** * Writes the specified certificate to the given output stream in PEM encoding. * * @param cert * The certificate to encode * * @param out * The output stream to which the certificate should be written * * @throws IOException * If an IOException occurs while writing the certificate */ public abstract void writePemEncoded(X509Certificate cert, OutputStream out) throws IOException; /** * Writes the specified key to the given output stream in PEM encoding. * * @param key * The key to encode * * @param out * The output stream to which the key should be written * * @throws IOException * If an IOException occurs while writing the key */ public abstract void writePemEncoded(Key key, OutputStream out) throws IOException; /** * Writes the specified certificate revocation list to the given output stream in PEM encoding. * * @param crl * The certificate revocation list to encode * * @param out * The output stream to which the certificate revocation list should be written * * @throws IOException * If an IOException occurs while writing the certificate revocation list */ public abstract void writePemEncoded(X509CRL crl, OutputStream out) throws IOException; public static X509Certificate createCert(byte[] certData) { try { CertificateFactory cf = CertificateFactory.getInstance("X509"); X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certData)); return cert; } catch (Exception e) { throw new RuntimeException(e); } } public byte[] getSHA256WithRSAHash(InputStream input) { try { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(reader.getCaKey()); updateSignature(input, signature); return signature.sign(); } catch (Exception e) { throw new RuntimeException(e); } } public boolean verifySHA256WithRSAHashAgainstCACerts( File input, byte[] signedHash) throws CertificateException, IOException { log.debug("Verify against: " + reader.getCACert().getSerialNumber()); if (verifySHA256WithRSAHash(new FileInputStream(input), signedHash, reader.getCACert())) { return true; } for (X509Certificate cert : reader.getUpstreamCACerts()) { log.debug("Verify against: " + cert.getSerialNumber()); if (verifySHA256WithRSAHash(new FileInputStream(input), signedHash, cert)) { return true; } } return false; } public boolean verifySHA256WithRSAHash(InputStream input, byte[] signedHash, Certificate certificate) { try { Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(certificate); updateSignature(input, signature); return signature.verify(signedHash); } catch (SignatureException se) { return false; } catch (Exception e) { throw new RuntimeException(e); } } private void updateSignature(InputStream input, Signature signature) throws IOException, SignatureException { byte[] dataBytes = new byte[4096]; int nread = 0; while ((nread = input.read(dataBytes)) != -1) { signature.update(dataBytes, 0, nread); } } public abstract String decodeDERValue(byte[] value); }