/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.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.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2006 Ola Bini <ola@ologix.com>
* Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.ext.openssl.x509store;
import java.io.IOException;
import java.io.Writer;
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.Reader;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.cert.X509CRL;
import java.security.interfaces.DSAPublicKey;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.pkcs.EncryptionScheme;
import org.bouncycastle.asn1.pkcs.PBES2Parameters;
import org.bouncycastle.asn1.pkcs.PBKDF2Params;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.engines.DESedeEngine;
import org.bouncycastle.crypto.engines.RC2Engine;
import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.cms.CMSSignedData;
import org.jruby.ext.openssl.Cipher.Algorithm;
import org.jruby.ext.openssl.impl.ASN1Registry;
import org.jruby.ext.openssl.impl.CipherSpec;
import org.jruby.ext.openssl.impl.PKCS10Request;
import org.jruby.ext.openssl.SecurityHelper;
import org.jruby.ext.openssl.util.ByteArrayOutputStream;
/**
* Helper class to read and write PEM files correctly.
*
* @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
*/
public class PEMInputOutput {
public static final String BEF = "-----";
public static final String AFT = "-----";
public static final String BEF_G = BEF + "BEGIN ";
public static final String BEF_E = BEF + "END ";
public static final String PEM_STRING_X509_OLD="X509 CERTIFICATE";
public static final String PEM_STRING_X509="CERTIFICATE";
public static final String PEM_STRING_X509_PAIR="CERTIFICATE PAIR";
public static final String PEM_STRING_X509_TRUSTED="TRUSTED CERTIFICATE";
public static final String PEM_STRING_X509_REQ_OLD="NEW CERTIFICATE REQUEST";
public static final String PEM_STRING_X509_REQ="CERTIFICATE REQUEST";
public static final String PEM_STRING_X509_CRL="X509 CRL";
public static final String PEM_STRING_EVP_PKEY="ANY PRIVATE KEY";
public static final String PEM_STRING_PUBLIC="PUBLIC KEY";
public static final String PEM_STRING_RSA="RSA PRIVATE KEY";
public static final String PEM_STRING_RSA_PUBLIC="RSA PUBLIC KEY";
public static final String PEM_STRING_DSA="DSA PRIVATE KEY";
public static final String PEM_STRING_DSA_PUBLIC="DSA PUBLIC KEY";
public static final String PEM_STRING_PKCS7="PKCS7";
public static final String PEM_STRING_PKCS8="ENCRYPTED PRIVATE KEY";
public static final String PEM_STRING_PKCS8INF="PRIVATE KEY";
public static final String PEM_STRING_DHPARAMS="DH PARAMETERS";
public static final String PEM_STRING_SSL_SESSION="SSL SESSION PARAMETERS";
public static final String PEM_STRING_DSAPARAMS="DSA PARAMETERS";
public static final String PEM_STRING_ECDSA_PUBLIC="ECDSA PUBLIC KEY";
public static final String PEM_STRING_ECPARAMETERS="EC PARAMETERS";
public static final String PEM_STRING_ECPRIVATEKEY="EC PRIVATE KEY";
private static final String BEG_STRING_PUBLIC = BEF_G + PEM_STRING_PUBLIC;
private static final String BEG_STRING_DSA = BEF_G + PEM_STRING_DSA;
private static final String BEG_STRING_RSA = BEF_G + PEM_STRING_RSA;
private static final String BEG_STRING_RSA_PUBLIC = BEF_G + PEM_STRING_RSA_PUBLIC;
private static final String BEG_STRING_X509_OLD = BEF_G + PEM_STRING_X509_OLD;
private static final String BEG_STRING_X509 = BEF_G + PEM_STRING_X509;
private static final String BEG_STRING_X509_TRUSTED = BEF_G + PEM_STRING_X509_TRUSTED;
private static final String BEG_STRING_X509_CRL = BEF_G + PEM_STRING_X509_CRL;
private static final String BEG_STRING_X509_REQ = BEF_G + PEM_STRING_X509_REQ;
private static BufferedReader makeBuffered(Reader in) {
if (in instanceof BufferedReader) {
return (BufferedReader) in;
}
return new BufferedReader(in);
}
private static BufferedWriter makeBuffered(Writer out) {
if (out instanceof BufferedWriter) {
return (BufferedWriter) out;
}
return new BufferedWriter(out);
}
/**
* @deprecated Prefer passing in a buffered-reader esp. in loops as the
* method might return a X.509 object before reading the full PEM file !
*/
public static Object readPEM(final Reader in, final char[] passwd) throws IOException {
return readPEM(makeBuffered(in), passwd);
}
/**
* c: PEM_X509_INFO_read_bio
*/
public static Object readPEM(final BufferedReader reader, final char[] passwd) throws IOException {
String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_PUBLIC) != -1 ) {
try {
return readPublicKey(reader,BEF_E+PEM_STRING_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating public key: ", e);
}
}
else if ( line.indexOf(BEG_STRING_DSA) != -1 ) {
try {
return readKeyPair(reader,passwd, "DSA", BEF_E+PEM_STRING_DSA);
}
catch (Exception e) {
throw mapReadException("problem creating DSA private key: ", e);
}
}
else if ( line.indexOf(BEG_STRING_RSA_PUBLIC) != -1 ) {
try {
return readPublicKey(reader,BEF_E+PEM_STRING_RSA_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating RSA public key: ", e);
}
}
else if ( line.indexOf(BEG_STRING_X509_OLD) != -1 ) {
try {
return readAuxCertificate(reader,BEF_E+PEM_STRING_X509_OLD);
} catch (Exception e) {
throw new IOException("problem creating X509 Aux certificate: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509) != -1 ) {
try {
return readAuxCertificate(reader,BEF_E+PEM_STRING_X509);
} catch (Exception e) {
throw new IOException("problem creating X509 Aux certificate: " + e.toString(), e);
}
}
else if( line.indexOf(BEG_STRING_X509_TRUSTED) != -1 ) {
try {
return readAuxCertificate(reader,BEF_E+PEM_STRING_X509_TRUSTED);
} catch (Exception e) {
throw new IOException("problem creating X509 Aux certificate: " + e.toString(), e);
}
}
else if( line.indexOf(BEG_STRING_X509_CRL) != -1 ) {
try {
return readCRL(reader,BEF_E+PEM_STRING_X509_CRL);
} catch (Exception e) {
throw new IOException("problem creating X509 CRL: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509_REQ) != -1 ) {
try {
return readCertificateRequest(reader,BEF_E+PEM_STRING_X509_REQ);
} catch (Exception e) {
throw new IOException("problem creating X509 REQ: " + e.toString(), e);
}
}
}
return null;
}
public static byte[] readX509PEM(final Reader in) throws IOException {
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_X509_OLD) != -1 ) {
try {
return readBase64Bytes(reader, BEF_E + PEM_STRING_X509_OLD);
} catch (Exception e) {
throw new IOException("problem reading PEM X509 Aux certificate: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509) != -1 ) {
try {
return readBase64Bytes(reader, BEF_E + PEM_STRING_X509);
} catch (Exception e) {
throw new IOException("problem reading PEM X509 Aux certificate: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509_TRUSTED) != -1 ) {
try {
return readBase64Bytes(reader, BEF_E + PEM_STRING_X509_TRUSTED);
} catch (Exception e) {
throw new IOException("problem reading PEM X509 Aux certificate: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509_CRL) != -1 ) {
try {
return readBase64Bytes(reader, BEF_E + PEM_STRING_X509_CRL);
} catch (Exception e) {
throw new IOException("problem reading PEM X509 CRL: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509_REQ) != -1 ) {
try {
return readBase64Bytes(reader, BEF_E + PEM_STRING_X509_REQ);
} catch (Exception e) {
throw new IOException("problem reading PEM X509 REQ: " + e.toString(), e);
}
}
}
return null;
}
/**
* c: PEM_read_PrivateKey + PEM_read_bio_PrivateKey
* CAUTION: KeyPair#getPublic() may be null.
*/
public static KeyPair readPrivateKey(final Reader in, char[] passwd)
throws PasswordRequiredException, IOException {
final String BEG_STRING_ECPRIVATEKEY = BEF_G + PEM_STRING_ECPRIVATEKEY;
final String BEG_STRING_PKCS8INF = BEF_G + PEM_STRING_PKCS8INF;
final String BEG_STRING_PKCS8 = BEF_G + PEM_STRING_PKCS8;
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_RSA) != -1 ) {
try {
return readKeyPair(reader, passwd, "RSA", BEF_E + PEM_STRING_RSA);
}
catch (Exception e) {
throw mapReadException("problem creating RSA private key: ", e);
}
}
else if ( line.indexOf(BEG_STRING_DSA) != -1 ) {
try {
return readKeyPair(reader, passwd, "DSA", BEF_E + PEM_STRING_DSA);
}
catch (Exception e) {
throw mapReadException("problem creating DSA private key: ", e);
}
}
else if ( line.indexOf(BEG_STRING_ECPRIVATEKEY) != -1) {
try {
return readKeyPair(reader, passwd, "ECDSA", BEF_E + PEM_STRING_ECPRIVATEKEY);
}
catch (Exception e) {
throw mapReadException("problem creating DSA private key: ", e);
}
}
else if ( line.indexOf(BEG_STRING_PKCS8INF) != -1) {
try {
byte[] bytes = readBase64Bytes(reader, BEF_E + PEM_STRING_PKCS8INF);
PrivateKeyInfo info = PrivateKeyInfo.getInstance(bytes);
String type = getPrivateKeyTypeFromObjectId(info.getPrivateKeyAlgorithm().getAlgorithm());
return org.jruby.ext.openssl.impl.PKey.readPrivateKey(((ASN1Object) info.parsePrivateKey()).getEncoded(ASN1Encoding.DER), type);
}
catch (Exception e) {
throw mapReadException("problem creating private key: ", e);
}
}
else if ( line.indexOf(BEG_STRING_PKCS8) != -1 ) {
try {
byte[] bytes = readBase64Bytes(reader, BEF_E + PEM_STRING_PKCS8);
EncryptedPrivateKeyInfo eIn = EncryptedPrivateKeyInfo.getInstance(bytes);
AlgorithmIdentifier algId = eIn.getEncryptionAlgorithm();
PrivateKey privKey;
if (algId.getAlgorithm().toString().equals("1.2.840.113549.1.5.13")) { // PBES2
privKey = derivePrivateKeyPBES2(eIn, algId, passwd);
} else {
privKey = derivePrivateKeyPBES1(eIn, algId, passwd);
}
return new KeyPair(null, privKey);
}
catch (Exception e) {
throw mapReadException("problem creating private key: ", e);
}
}
}
return null;
}
private static IOException mapReadException(final String message, final Exception ex) {
if ( ex instanceof PasswordRequiredException ) {
return (PasswordRequiredException) ex;
}
return new IOException(message + ex, ex);
}
private static PrivateKey derivePrivateKeyPBES1(EncryptedPrivateKeyInfo eIn, AlgorithmIdentifier algId, char[] password)
throws GeneralSecurityException, IOException {
// From BC's PEMReader
PKCS12PBEParams pkcs12Params = PKCS12PBEParams.getInstance(algId.getParameters());
PBEKeySpec pbeSpec = new PBEKeySpec(password);
PBEParameterSpec pbeParams = new PBEParameterSpec(
pkcs12Params.getIV(), pkcs12Params.getIterations().intValue()
);
String algorithm = ASN1Registry.o2a(algId.getAlgorithm());
algorithm = (algorithm.split("-"))[0];
SecretKeyFactory secKeyFactory = SecurityHelper.getSecretKeyFactory(algorithm);
Cipher cipher = SecurityHelper.getCipher(algorithm);
cipher.init(Cipher.DECRYPT_MODE, secKeyFactory.generateSecret(pbeSpec), pbeParams);
PrivateKeyInfo pInfo = PrivateKeyInfo.getInstance(
ASN1Primitive.fromByteArray(cipher.doFinal(eIn.getEncryptedData()))
);
KeyFactory keyFactory = getKeyFactory( pInfo.getPrivateKeyAlgorithm() );
return keyFactory.generatePrivate( new PKCS8EncodedKeySpec( pInfo.getEncoded() ) );
}
private static PrivateKey derivePrivateKeyPBES2(EncryptedPrivateKeyInfo eIn, AlgorithmIdentifier algId, char[] password)
throws GeneralSecurityException, InvalidCipherTextException {
PBES2Parameters pbeParams = PBES2Parameters.getInstance((ASN1Sequence) algId.getParameters());
CipherParameters cipherParams = extractPBES2CipherParams(password, pbeParams);
EncryptionScheme scheme = pbeParams.getEncryptionScheme();
BufferedBlockCipher cipher;
if ( scheme.getAlgorithm().equals( PKCSObjectIdentifiers.RC2_CBC ) ) {
RC2CBCParameter rc2Params = RC2CBCParameter.getInstance(scheme);
byte[] iv = rc2Params.getIV();
CipherParameters param = new ParametersWithIV(cipherParams, iv);
cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new RC2Engine()));
cipher.init(false, param);
} else {
byte[] iv = ASN1OctetString.getInstance( scheme.getParameters() ).getOctets();
CipherParameters param = new ParametersWithIV(cipherParams, iv);
cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
cipher.init(false, param);
}
byte[] data = eIn.getEncryptedData();
byte[] out = new byte[cipher.getOutputSize(data.length)];
int len = cipher.processBytes(data, 0, data.length, out, 0);
len += cipher.doFinal(out, len);
byte[] pkcs8 = new byte[len];
System.arraycopy(out, 0, pkcs8, 0, len);
KeyFactory fact = SecurityHelper.getKeyFactory("RSA"); // It seems to work for both RSA and DSA.
return fact.generatePrivate( new PKCS8EncodedKeySpec(pkcs8) );
}
private static CipherParameters extractPBES2CipherParams(char[] password, PBES2Parameters pbeParams) {
PBKDF2Params pbkdfParams = PBKDF2Params.getInstance(pbeParams.getKeyDerivationFunc().getParameters());
int keySize = 192;
if (pbkdfParams.getKeyLength() != null) {
keySize = pbkdfParams.getKeyLength().intValue() * 8;
}
int iterationCount = pbkdfParams.getIterationCount().intValue();
byte[] salt = pbkdfParams.getSalt();
PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
generator.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt, iterationCount);
return generator.generateDerivedParameters(keySize);
}
// PEM_read_bio_PUBKEY
public static PublicKey readPubKey(Reader in) throws IOException {
PublicKey pubKey = readRSAPubKey(in);
if (pubKey == null) pubKey = readDSAPubKey(in);
if (pubKey == null) pubKey = readECPubKey(in);
return pubKey;
}
/*
* c: PEM_read_bio_DSA_PUBKEY
*/
public static DSAPublicKey readDSAPubKey(Reader in) throws IOException {
final String BEG_STRING_DSA_PUBLIC = BEF_G + PEM_STRING_DSA_PUBLIC;
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_DSA_PUBLIC) != -1 ) {
try {
return (DSAPublicKey) readPublicKey(reader, "DSA", BEF_E + PEM_STRING_DSA_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating DSA public key: ", e);
}
}
}
return null;
}
/*
* c: PEM_read_bio_DSAPublicKey
*/
public static DSAPublicKey readDSAPublicKey(final Reader in, final char[] passwd) throws IOException {
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_PUBLIC) != -1 ) {
try {
return (DSAPublicKey) readPublicKey(reader, "DSA", BEF_E + PEM_STRING_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating DSA public key: ", e);
}
}
}
return null;
}
/*
* c: PEM_read_bio_DSAPrivateKey
*/
public static KeyPair readDSAPrivateKey(final Reader in, final char[] passwd)
throws PasswordRequiredException, IOException {
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_DSA) != -1 ) {
try {
return readKeyPair(reader, passwd, "DSA", BEF_E + PEM_STRING_DSA);
}
catch (Exception e) {
throw mapReadException("problem creating DSA private key: ", e);
}
}
}
return null;
}
/**
* reads an RSA public key encoded in an SubjectPublicKeyInfo RSA structure.
* c: PEM_read_bio_RSA_PUBKEY
*/
public static RSAPublicKey readRSAPubKey(Reader in) throws IOException {
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_PUBLIC) != -1 ) {
try {
return readRSAPublicKey(reader, BEF_E + PEM_STRING_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating RSA public key: ", e);
}
}
else if ( line.indexOf(BEG_STRING_RSA_PUBLIC) != -1 ) {
try {
return readRSAPublicKey(reader, BEF_E + PEM_STRING_RSA_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating RSA public key: ", e);
}
}
}
return null;
}
/**
* reads an RSA public key encoded in an PKCS#1 RSA structure.
* c: PEM_read_bio_RSAPublicKey
*/
public static RSAPublicKey readRSAPublicKey(Reader in, char[] f)
throws PasswordRequiredException, IOException {
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_PUBLIC) != -1 ) {
try {
return (RSAPublicKey) readPublicKey(reader, "RSA", BEF_E + PEM_STRING_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating RSA public key: ", e);
}
}
else if ( line.indexOf(BEF_G+PEM_STRING_RSA_PUBLIC) != -1 ) {
try {
return (RSAPublicKey) readPublicKey(reader, "RSA", BEF_E + PEM_STRING_RSA_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating RSA public key: ", e);
}
}
}
return null;
}
/**
* c: PEM_read_bio_RSAPrivateKey
*/
public static KeyPair readRSAPrivateKey(Reader in, char[] f)
throws PasswordRequiredException, IOException {
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_RSA) != -1 ) {
try {
return readKeyPair(reader,f, "RSA", BEF_E + PEM_STRING_RSA);
}
catch (Exception e) {
throw mapReadException("problem creating RSA private key: ", e);
}
}
}
return null;
}
public static ECPublicKey readECPubKey(Reader in) throws IOException {
final String BEG_STRING_EC_PUBLIC = BEF_G + "EC PUBLIC KEY";
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_EC_PUBLIC) != -1 ) {
try {
return (ECPublicKey) readPublicKey(reader, "ECDSA", BEF_E + "EC PUBLIC KEY");
}
catch (Exception e) {
throw mapReadException("problem creating ECDSA public key: ", e);
}
}
}
return null;
}
public static ECPublicKey readECPublicKey(final Reader in, final char[] passwd) throws IOException {
// final String BEG_STRING_EC = BEF_G + "EC PUBLIC KEY";
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_PUBLIC) != -1 ) {
try {
return (ECPublicKey) readPublicKey(reader, "ECDSA", BEF_E + PEM_STRING_PUBLIC);
}
catch (Exception e) {
throw mapReadException("problem creating ECDSA public key: ", e);
}
}
}
return null;
}
public static KeyPair readECPrivateKey(final Reader in, final char[] passwd)
throws PasswordRequiredException, IOException {
final String BEG_STRING_EC = BEF_G + "EC PRIVATE KEY";
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_EC) != -1 ) {
try {
return readKeyPair(reader, passwd, "ECDSA", BEF_E + "EC PRIVATE KEY");
}
catch (Exception e) {
throw mapReadException("problem creating ECDSA private key: ", e);
}
}
}
return null;
}
public static CMSSignedData readPKCS7(Reader in, char[] f) throws IOException {
final String BEG_STRING_PKCS7 = BEF_G + PEM_STRING_PKCS7;
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_PKCS7) != -1 ) {
try {
return readPKCS7(reader,f, BEF_E + PEM_STRING_PKCS7);
}
catch (Exception e) {
throw new IOException("problem creating PKCS7: " + e.toString(), e);
}
}
}
return null;
}
/**
* @deprecated Prefer passing in a buffered-reader esp. in loops as the
* method might return a X.509 object before reading the full PEM file !
*/
public static X509AuxCertificate readX509Certificate(final Reader in, final char[] passwd) throws IOException {
return readX509Certificate(makeBuffered(in), passwd);
}
public static X509AuxCertificate readX509Certificate(final BufferedReader reader, final char[] passwd)
throws IOException {
String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_X509_OLD) != -1 ) {
try {
return new X509AuxCertificate(readCertificate(reader,BEF_E+PEM_STRING_X509_OLD));
}
catch (Exception e) {
throw new IOException("problem creating X509 certificate: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509) != -1 ) {
try {
return new X509AuxCertificate(readCertificate(reader,BEF_E+PEM_STRING_X509));
}
catch (Exception e) {
throw new IOException("problem creating X509 certificate: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509_TRUSTED) != -1 ) {
try {
return new X509AuxCertificate(readCertificate(reader,BEF_E+PEM_STRING_X509_TRUSTED));
}
catch (Exception e) {
throw new IOException("problem creating X509 certificate: " + e.toString(), e);
}
}
}
return null;
}
/**
* @deprecated Prefer passing in a buffered-reader esp. in loops as the
* method might return a X.509 object before reading the full PEM file !
*/
public static X509AuxCertificate readX509Aux(final Reader in, final char[] passwd) throws IOException {
return readX509Aux(makeBuffered(in), passwd);
}
public static X509AuxCertificate readX509Aux(final BufferedReader reader, final char[] passwd)
throws IOException {
String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_X509_OLD) != -1 ) {
try {
return readAuxCertificate(reader, BEF_E + PEM_STRING_X509_OLD);
}
catch (Exception e) {
throw new IOException("problem creating X509 Aux certificate: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509) != -1 ) {
try {
return readAuxCertificate(reader, BEF_E + PEM_STRING_X509);
}
catch (Exception e) {
throw new IOException("problem creating X509 Aux certificate: " + e.toString(), e);
}
}
else if ( line.indexOf(BEG_STRING_X509_TRUSTED) != -1 ) {
try {
return readAuxCertificate(reader, BEF_E + PEM_STRING_X509_TRUSTED);
}
catch (Exception e) {
throw new IOException("problem creating X509 Aux certificate: " + e.toString(), e);
}
}
}
return null;
}
/**
* @deprecated Prefer passing in a buffered-reader esp. in loops as the
* method might return a X.509 object before reading the full PEM file !
*/
public static X509CRL readX509CRL(final Reader reader, final char[] passwd) throws IOException {
return readX509CRL(makeBuffered(reader), passwd);
}
public static X509CRL readX509CRL(final BufferedReader reader, final char[] passwd) throws IOException {
String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_X509_CRL) != -1 ) {
try {
return readCRL(reader, BEF_E + PEM_STRING_X509_CRL);
}
catch (Exception e) {
throw new IOException("problem creating X509 CRL: " + e.toString(), e);
}
}
}
return null;
}
public static PKCS10Request readX509Request(final Reader in, final char[] passwd)
throws IOException {
final BufferedReader reader = makeBuffered(in); String line;
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_X509_REQ) != -1 ) {
try {
return readCertificateRequest(reader, BEF_E + PEM_STRING_X509_REQ);
}
catch (Exception e) {
throw new IOException("problem creating X509 REQ: " + e.toString(), e);
}
}
}
return null;
}
public static DHParameterSpec readDHParameters(final Reader in) throws IOException {
final String BEG_STRING_DHPARAMS = BEF_G + PEM_STRING_DHPARAMS;
final BufferedReader reader = makeBuffered(in); String line;
final StringBuilder lines = new StringBuilder();
while ( ( line = reader.readLine() ) != null ) {
if ( line.indexOf(BEG_STRING_DHPARAMS) >= 0 ) {
final String endParams = BEF_E + PEM_STRING_DHPARAMS;
do {
lines.append(line.trim());
}
while ( line.indexOf(endParams) < 0 && ( line = reader.readLine() ) != null );
break;
}
}
final Pattern DH_PARAMS_PATTERN = Pattern.compile(
"(-----BEGIN DH PARAMETERS-----)(.*)(-----END DH PARAMETERS-----)",
Pattern.MULTILINE);
final int DH_PARAMS_GROUP = 2; // the group above containing encoded params
final Matcher matcher = DH_PARAMS_PATTERN.matcher( lines.toString() );
if ( matcher.find() ) {
try {
byte[] decoded = Base64.decode(matcher.group(DH_PARAMS_GROUP));
return org.jruby.ext.openssl.impl.PKey.readDHParameter(decoded);
}
catch (IOException e) {
// TODO
}
}
return null;
}
private static byte[] getEncoded(java.security.Key key) {
if ( key == null ) return new byte[] { '0', 0 };
return key.getEncoded();
}
private static byte[] getEncoded(ASN1Encodable obj) throws IOException {
if ( obj == null ) return new byte[] { '0', 0 };
return obj.toASN1Primitive().getEncoded();
}
private static byte[] getEncoded(CMSSignedData obj) throws IOException {
if ( obj == null ) return new byte[] { '0', 0 };
return obj.getEncoded();
}
private static byte[] getEncoded(X509Certificate cert) throws IOException {
if ( cert == null ) return new byte[] { '0', 0 };
try {
return cert.getEncoded();
}
catch (GeneralSecurityException e) {
throw new IOException("problem with encoding object in write_X509", e);
}
}
private static byte[] getEncoded(X509CRL crl) throws IOException {
if ( crl == null ) return new byte[] { '0', 0 };
try {
return crl.getEncoded();
}
catch (GeneralSecurityException e) {
throw new IOException("problem with encoding object in write_X509_CRL", e);
}
}
public static void writeDSAPublicKey(Writer _out, DSAPublicKey obj) throws IOException {
BufferedWriter out = makeBuffered(_out);
final byte[] enc = getEncoded(obj);
out.write(BEF_G + PEM_STRING_PUBLIC + AFT);
out.newLine();
writeEncoded(out, enc, enc.length);
out.write(BEF_E + PEM_STRING_PUBLIC + AFT);
out.newLine();
out.flush();
}
/** writes an RSA public key encoded in an PKCS#1 RSA structure. */
public static void writeRSAPublicKey(Writer _out, RSAPublicKey obj) throws IOException {
BufferedWriter out = makeBuffered(_out);
final byte[] enc = getEncoded(obj);
out.write(BEF_G + PEM_STRING_PUBLIC + AFT);
out.newLine();
writeEncoded(out, enc, enc.length);
out.write(BEF_E + PEM_STRING_PUBLIC + AFT);
out.newLine();
out.flush();
}
public static void writeECPublicKey(Writer _out, ECPublicKey obj) throws IOException {
BufferedWriter out = makeBuffered(_out);
final byte[] enc = getEncoded(obj);
out.write(BEF_G); out.write(PEM_STRING_PUBLIC); out.write(AFT);
out.newLine();
writeEncoded(out, enc, enc.length);
out.write(BEF_E); out.write(PEM_STRING_PUBLIC); out.write(AFT);
out.newLine();
out.flush();
}
public static void writePKCS7(Writer _out, ContentInfo obj) throws IOException {
BufferedWriter out = makeBuffered(_out);
final byte[] enc = getEncoded(obj);
out.write(BEF_G + PEM_STRING_PKCS7 + AFT);
out.newLine();
writeEncoded(out, enc, enc.length);
out.write(BEF_E + PEM_STRING_PKCS7 + AFT);
out.newLine();
out.flush();
}
public static void writePKCS7(Writer _out, CMSSignedData obj) throws IOException {
BufferedWriter out = makeBuffered(_out);
final byte[] enc = getEncoded(obj);
out.write(BEF_G + PEM_STRING_PKCS7 + AFT);
out.newLine();
writeEncoded(out, enc, enc.length);
out.write(BEF_E + PEM_STRING_PKCS7 + AFT);
out.newLine();
out.flush();
}
public static void writePKCS7(final Writer _out, final byte[] enc) throws IOException {
BufferedWriter out = makeBuffered(_out);
out.write(BEF_G + PEM_STRING_PKCS7 + AFT);
out.newLine();
writeEncoded(out, enc, enc.length);
out.write(BEF_E + PEM_STRING_PKCS7 + AFT);
out.newLine();
out.flush();
}
public static void writeX509Certificate(final Writer _out, final X509Certificate cert) throws IOException {
BufferedWriter out = makeBuffered(_out);
final byte[] enc = getEncoded(cert);
out.write(BEF_G + PEM_STRING_X509 + AFT);
out.newLine();
writeEncoded(out, enc, enc.length);
out.write(BEF_E + PEM_STRING_X509 + AFT);
out.newLine();
out.flush();
}
public static void writeX509Aux(final Writer _out, final X509AuxCertificate cert) throws IOException {
BufferedWriter out = makeBuffered(_out);
final byte[] encoding; final int encLen;
try {
if ( cert.aux == null ) {
encoding = cert.getEncoded(); encLen = encoding.length;
}
else {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] enc = cert.getEncoded();
baos.write(enc, 0, enc.length);
final X509Aux aux = cert.aux;
ASN1EncodableVector a1 = new ASN1EncodableVector();
if ( aux.trust.size() > 0 ) {
ASN1EncodableVector a2 = new ASN1EncodableVector();
for ( String trust : aux.trust ) {
a2.add(new ASN1ObjectIdentifier(trust));
}
a1.add(new DLSequence(a2));
}
if ( aux.reject.size() > 0 ) {
ASN1EncodableVector a2 = new ASN1EncodableVector();
for ( String reject : aux.reject ) {
a2.add(new ASN1ObjectIdentifier(reject));
}
a1.add(new DERTaggedObject(0,new DLSequence(a2)));
}
if ( aux.alias != null ) {
a1.add(new DERUTF8String(aux.alias));
}
if ( aux.keyid != null ) {
a1.add(new DEROctetString(aux.keyid));
}
if ( aux.other.size() > 0 ) {
ASN1EncodableVector a2 = new ASN1EncodableVector();
for ( ASN1Primitive other : aux.other ) a2.add(other);
a1.add( new DERTaggedObject( 1, new DLSequence(a2) ) );
}
enc = new DLSequence(a1).getEncoded();
baos.write(enc, 0, enc.length);
encoding = baos.buffer(); encLen = baos.size();
}
}
catch (CertificateEncodingException e) {
throw new IOException("problem with encoding object in write_X509_AUX", e);
}
out.write(BEF_G + PEM_STRING_X509_TRUSTED + AFT);
out.newLine();
writeEncoded(out, encoding, encLen);
out.write(BEF_E + PEM_STRING_X509_TRUSTED + AFT);
out.newLine();
out.flush();
}
public static void writeX509CRL(Writer _out, X509CRL obj) throws IOException {
BufferedWriter out = makeBuffered(_out);
byte[] encoding = getEncoded(obj);
out.write(BEF_G + PEM_STRING_X509_CRL + AFT);
out.newLine();
writeEncoded(out, encoding, encoding.length);
out.write(BEF_E + PEM_STRING_X509_CRL + AFT);
out.newLine();
out.flush();
}
public static void writeX509Request(Writer _out, PKCS10Request obj) throws IOException {
BufferedWriter out = makeBuffered(_out);
byte[] encoding = getEncoded(obj.toASN1Structure());
out.write(BEF_G + PEM_STRING_X509_REQ + AFT);
out.newLine();
writeEncoded(out, encoding, encoding.length);
out.write(BEF_E + PEM_STRING_X509_REQ + AFT);
out.newLine();
out.flush();
}
public static void writeDSAPrivateKey(Writer _out, DSAPrivateKey obj, CipherSpec cipher, char[] passwd) throws IOException {
BufferedWriter out = makeBuffered(_out);
PrivateKeyInfo info = new PrivateKeyInfo((ASN1Sequence) new ASN1InputStream(getEncoded(obj)).readObject());
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
DSAParameter p = DSAParameter.getInstance(info.getPrivateKeyAlgorithm().getParameters());
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(BigInteger.ZERO));
v.add(new ASN1Integer(p.getP()));
v.add(new ASN1Integer(p.getQ()));
v.add(new ASN1Integer(p.getG()));
BigInteger x = obj.getX();
BigInteger y = p.getG().modPow(x, p.getP());
v.add(new ASN1Integer(y));
v.add(new ASN1Integer(x));
aOut.writeObject(new DLSequence(v));
if (cipher != null && passwd != null) {
writePemEncrypted(out, PEM_STRING_DSA, bOut.buffer(), bOut.size(), cipher, passwd);
} else {
writePemPlain(out, PEM_STRING_DSA, bOut.buffer(), bOut.size());
}
}
public static void writeRSAPrivateKey(Writer _out, RSAPrivateCrtKey obj, CipherSpec cipher, char[] passwd) throws IOException {
assert (obj != null);
BufferedWriter out = makeBuffered(_out);
org.bouncycastle.asn1.pkcs.RSAPrivateKey keyStruct = new org.bouncycastle.asn1.pkcs.RSAPrivateKey(obj.getModulus(), obj.getPublicExponent(), obj.getPrivateExponent(), obj.getPrimeP(),
obj.getPrimeQ(), obj.getPrimeExponentP(), obj.getPrimeExponentQ(), obj.getCrtCoefficient());
if (cipher != null && passwd != null) {
writePemEncrypted(out, PEM_STRING_RSA, keyStruct.getEncoded(), cipher, passwd);
} else {
writePemPlain(out, PEM_STRING_RSA, keyStruct.getEncoded());
}
}
public static void writeECPrivateKey(Writer _out, ECPrivateKey obj, CipherSpec cipher, char[] passwd) throws IOException {
assert (obj != null);
final String PEM_STRING_EC = "EC PRIVATE KEY";
BufferedWriter out = makeBuffered(_out);
final int bitLength = obj.getParams().getOrder().bitLength();
org.bouncycastle.asn1.sec.ECPrivateKey keyStruct = new org.bouncycastle.asn1.sec.ECPrivateKey(bitLength, obj.getS());
if (cipher != null && passwd != null) {
writePemEncrypted(out, PEM_STRING_EC, keyStruct.getEncoded(), cipher, passwd);
} else {
writePemPlain(out, PEM_STRING_EC, keyStruct.getEncoded());
}
}
public static void writeECParameters(Writer _out, ASN1ObjectIdentifier obj, CipherSpec cipher, char[] passwd) throws IOException {
assert (obj != null);
final String PEM_STRING_EC = "EC PARAMETERS";
BufferedWriter out = makeBuffered(_out);
if (cipher != null && passwd != null) {
writePemEncrypted(out, PEM_STRING_EC, obj.getEncoded(), cipher, passwd);
} else {
writePemPlain(out, PEM_STRING_EC, obj.getEncoded());
}
}
private static void writePemPlain(final BufferedWriter out,
final String PEM_ID, final byte[] encoding) throws IOException {
writePemPlain(out, PEM_ID, encoding, encoding.length);
}
private static void writePemPlain(final BufferedWriter out,
final String PEM_ID, final byte[] encoding, final int encLen) throws IOException {
out.write(BEF_G); out.write(PEM_ID); out.write(AFT);
out.newLine();
writeEncoded(out, encoding, encLen);
out.write(BEF_E); out.write(PEM_ID); out.write(AFT);
out.newLine();
out.flush();
}
private static void writePemEncrypted(final BufferedWriter out,
final String PEM_ID, final byte[] encoding,
final CipherSpec cipherSpec, final char[] passwd) throws IOException {
writePemEncrypted(out, PEM_ID, encoding, encoding.length, cipherSpec, passwd);
}
private static void writePemEncrypted(final BufferedWriter out,
final String PEM_ID, final byte[] encoding, final int encCount,
final CipherSpec cipherSpec, final char[] passwd) throws IOException {
final Cipher cipher = cipherSpec.getCipher();
final byte[] iv = new byte[cipher.getBlockSize()];
secureRandom().nextBytes(iv);
final byte[] salt = new byte[8];
System.arraycopy(iv, 0, salt, 0, 8);
OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator();
pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(passwd), salt);
KeyParameter param = (KeyParameter) pGen.generateDerivedParameters(cipherSpec.getKeyLenInBits());
SecretKey secretKey = new SecretKeySpec(param.getKey(), Algorithm.getAlgorithmBase(cipher));
final byte[] encData;
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
encData = cipher.doFinal(encoding, 0, encCount);
}
catch (InvalidKeyException e) {
final String msg = e.getMessage();
if ( msg != null && msg.startsWith("Invalid key length") ) {
throw new IOException("Invalid key length. See http://wiki.jruby.org/UnlimitedStrengthCrypto", e);
}
throw new IOException("exception using cipher: "+ cipherSpec.getOsslName() + " (" + e + ")", e);
}
catch (GeneralSecurityException e) {
throw new IOException("exception using cipher: "+ cipherSpec.getOsslName() + " (" + e + ")", e);
}
out.write(BEF_G); out.write(PEM_ID); out.write(AFT);
out.newLine();
out.write("Proc-Type: 4,ENCRYPTED");
out.newLine();
out.write("DEK-Info: " + cipherSpec.getOsslName() + ',');
writeHexEncoded(out, iv);
out.newLine();
out.newLine();
writeEncoded(out, encData, encData.length);
out.write(BEF_E); out.write(PEM_ID); out.write(AFT);
out.flush();
}
private static SecureRandom random;
private static SecureRandom secureRandom() {
if ( random == null ) {
try {
random = SecureRandom.getInstance("SHA1PRNG");
}
catch (NoSuchAlgorithmException e) {
random = new SecureRandom();
}
}
return random;
}
public static void writeDHParameters(Writer _out, DHParameterSpec params) throws IOException {
final BufferedWriter out = makeBuffered(_out);
ASN1EncodableVector v = new ASN1EncodableVector();
BigInteger value;
if ( ( value = params.getP() ) != null ) {
v.add( new ASN1Integer(value) );
}
if ( ( value = params.getG() ) != null ) {
v.add( new ASN1Integer(value) );
}
ByteArrayOutputStream bOut = new ByteArrayOutputStream();
ASN1OutputStream aOut = new ASN1OutputStream(bOut);
aOut.writeObject(new DLSequence(v));
out.write(BEF_G); out.write(PEM_STRING_DHPARAMS); out.write(AFT);
out.newLine();
writeEncoded(out, bOut.buffer(), bOut.size());
out.write(BEF_E); out.write(PEM_STRING_DHPARAMS); out.write(AFT);
out.newLine();
out.flush();
}
private static String getPrivateKeyTypeFromObjectId(ASN1ObjectIdentifier oid) {
if ( ASN1Registry.oid2nid(oid) == ASN1Registry.NID_rsaEncryption ) {
return "RSA";
} else {
return "DSA";
}
}
private static RSAPublicKey readRSAPublicKey(BufferedReader in, String endMarker) throws IOException {
Object asnObject = new ASN1InputStream(readBase64Bytes(in, endMarker)).readObject();
ASN1Sequence sequence = (ASN1Sequence) asnObject;
org.bouncycastle.asn1.pkcs.RSAPublicKey rsaPubStructure = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(sequence);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(
rsaPubStructure.getModulus(),
rsaPubStructure.getPublicExponent());
try {
return (RSAPublicKey) SecurityHelper.getKeyFactory("RSA").generatePublic(keySpec);
}
catch (NoSuchAlgorithmException e) { /* ignore */ }
catch (InvalidKeySpecException e) { /* ignore */ }
return null;
}
private static PublicKey readPublicKey(byte[] input, String alg, String endMarker) throws IOException {
KeySpec keySpec = new X509EncodedKeySpec(input);
try {
return SecurityHelper.getKeyFactory(alg).generatePublic(keySpec);
}
catch (NoSuchAlgorithmException e) { /* ignore */ }
catch (InvalidKeySpecException e) { /* ignore */ }
return null;
}
private static PublicKey readPublicKey(BufferedReader in, String alg, String endMarker) throws IOException {
return readPublicKey(readBase64Bytes(in, endMarker), alg, endMarker);
}
private static PublicKey readPublicKey(BufferedReader in, String endMarker) throws IOException {
byte[] input = readBase64Bytes(in, endMarker);
String[] algs = { "RSA", "DSA", "ECDSA" };
for (int i = 0; i < algs.length; i++) {
PublicKey key = readPublicKey(input, algs[i], endMarker);
if (key != null) {
return key;
}
}
return null;
}
/**
* Read a Key Pair
*/
private static KeyPair readKeyPair(BufferedReader in, char[] passwd, String type, String endMarker)
throws PasswordRequiredException, IOException, GeneralSecurityException {
boolean isEncrypted = false;
String dekInfo = null;
String line; StringBuilder buffer = new StringBuilder(512);
while ( ( line = in.readLine() ) != null ) {
if ( line.startsWith("Proc-Type: 4,ENCRYPTED") ) {
isEncrypted = true;
}
else if ( line.startsWith("DEK-Info:") ) {
dekInfo = line.substring(10);
}
else if ( line.contains(endMarker) ) {
break;
}
else {
buffer.append( line.trim() );
}
}
byte[] decoded = Base64.decode( buffer.toString() );
final byte[] keyBytes;
if ( isEncrypted ) {
keyBytes = decrypt(decoded, dekInfo, passwd);
} else {
keyBytes = decoded;
}
return org.jruby.ext.openssl.impl.PKey.readPrivateKey(keyBytes, type);
}
private static byte[] decrypt(byte[] decoded, String dekInfo, char[] passwd)
throws PasswordRequiredException, IOException, GeneralSecurityException {
if ( passwd == null ) throw new PasswordRequiredException();
StringTokenizer tknz = new StringTokenizer(dekInfo, ",");
String algorithm = tknz.nextToken();
byte[] iv = Hex.decode(tknz.nextToken());
// NOTE: shall be fine and bubble up on-demand (if really not supported)
//if ( ! org.jruby.ext.openssl.Cipher.isSupportedCipher(algorithm) ) {
// throw new IOException("Unknown algorithm: " + algorithm);
//}
String realName = Algorithm.getRealName(algorithm);
int[] lengths = Algorithm.osslKeyIvLength(algorithm);
int keyLen = lengths[0];
int ivLen = lengths[1];
if (iv.length != ivLen) {
throw new IOException("Illegal IV length");
}
byte[] salt = new byte[8];
System.arraycopy(iv, 0, salt, 0, 8);
OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator();
pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(passwd), salt);
KeyParameter param = (KeyParameter) pGen.generateDerivedParameters(keyLen * 8);
SecretKey secretKey = new SecretKeySpec(param.getKey(), realName);
Cipher cipher = SecurityHelper.getCipher(realName);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));
return cipher.doFinal(decoded);
}
public static class PasswordRequiredException extends IOException {
PasswordRequiredException() {
super();
}
}
/**
* Reads in a X509Certificate.
*
* @return the X509Certificate
* @throws IOException if an I/O error occured
*/
private static X509Certificate readCertificate(final BufferedReader in, final String endMarker)
throws IOException {
final byte[] bytes = readBase64Bytes(in, endMarker);
try {
return (X509Certificate) getX509CertificateFactory().generateCertificate( new ByteArrayInputStream( bytes ) );
}
catch (CertificateException e) {
throw new IOException("failed to read certificate: " + e, e);
}
//catch (RuntimeException e) {
// throw new IOException("problem generating cert: " + e.toString(), e);
//}
}
private static X509AuxCertificate readAuxCertificate(final BufferedReader in, final String endMarker)
throws IOException {
final byte[] bytes = readBase64Bytes(in, endMarker);
final ASN1InputStream asn1 = new ASN1InputStream(bytes);
ByteArrayInputStream certBytes = new ByteArrayInputStream( ( asn1.readObject() ).getEncoded() );
try {
final X509Certificate cert = (X509Certificate) getX509CertificateFactory().generateCertificate(certBytes);
final ASN1Sequence auxSeq = (ASN1Sequence) asn1.readObject();
final X509Aux aux;
if ( auxSeq != null ) {
// X509Aux fields :
final List<String> trust;
final List<String> reject;
final String alias;
final byte[] keyid;
final List<ASN1Primitive> other;
int ix = 0; ASN1Encodable obj = null;
if ( auxSeq.size() > ix ) obj = auxSeq.getObjectAt(ix);
if ( obj instanceof ASN1Sequence ) {
trust = new ArrayList<String>();
final ASN1Sequence trustSeq = (ASN1Sequence) obj;
for ( int i = 0; i < trustSeq.size(); i++ ) {
trust.add( ((ASN1ObjectIdentifier) trustSeq.getObjectAt(i)).getId() );
}
obj = ( auxSeq.size() > ++ix ) ? auxSeq.getObjectAt(ix) : null; // next obj
}
else trust = Collections.emptyList();
if ( obj instanceof ASN1TaggedObject && ((ASN1TaggedObject) obj).getTagNo() == 0 ) {
reject = new ArrayList<String>();
final ASN1Sequence rejectSeq = (ASN1Sequence) ((ASN1TaggedObject) obj).getObject();
for( int i = 0; i < rejectSeq.size(); i++ ) {
reject.add( ((ASN1ObjectIdentifier) rejectSeq.getObjectAt(i)).getId() );
}
obj = ( auxSeq.size() > ++ix ) ? auxSeq.getObjectAt(ix) : null; // next obj
}
else reject = Collections.emptyList();
if ( obj instanceof DERUTF8String ) {
alias = ((DERUTF8String) obj).getString();
obj = ( auxSeq.size() > ++ix ) ? auxSeq.getObjectAt(ix) : null; // next obj
}
else alias = null;
if ( obj instanceof DEROctetString ) {
keyid = ((DEROctetString) obj).getOctets();
obj = ( auxSeq.size() > ++ix ) ? auxSeq.getObjectAt(ix) : null; // next obj
}
else keyid = null;
if ( obj instanceof ASN1TaggedObject && ((ASN1TaggedObject) obj).getTagNo() == 1 ) {
other = new ArrayList<ASN1Primitive>();
final ASN1Sequence otherSeq = (ASN1Sequence) ((ASN1TaggedObject) obj).getObject();
for( int i = 0; i < otherSeq.size(); i++ ) {
other.add( (ASN1Primitive) otherSeq.getObjectAt(i) );
}
//obj = ( auxSeq.size() > ++ix ) ? auxSeq.getObjectAt(ix) : null; // next obj
}
else other = Collections.emptyList();
aux = new X509Aux(alias, keyid,
Collections.unmodifiableList(trust),
Collections.unmodifiableList(reject),
Collections.unmodifiableList(other));
}
else {
aux = null;
}
return new X509AuxCertificate(cert, aux);
}
catch (CertificateException e) {
throw new IOException("failed to read aux cert: " + e, e);
}
}
/**
* Reads in a X509CRL.
*
* @return the X509CRL
* @throws IOException if an I/O error occured
*/
private static X509CRL readCRL(BufferedReader in, String endMarker) throws IOException {
final byte[] bytes = readBase64Bytes(in, endMarker);
try {
return (X509CRL) getX509CertificateFactory().generateCRL( new ByteArrayInputStream( bytes ) );
}
catch (CRLException e) {
throw new IOException("failed to read crl: " + e, e);
}
//catch (RuntimeException e) {
// throw new IOException("problem parsing cert: " + e.toString(), e);
//}
}
/**
* Reads in a PKCS10 certification request.
*
* @return the certificate request.
* @throws IOException if an I/O error occured
*/
private static PKCS10Request readCertificateRequest(BufferedReader in, String endMarker) throws IOException {
final byte[] bytes = readBase64Bytes(in, endMarker);
try {
return new PKCS10Request( bytes );
}
catch (RuntimeException e) {
throw new IOException("problem parsing cert: " + e.toString(), e);
}
}
/**
* Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS
* API.
*
* @return the X509Certificate
* @throws IOException if an I/O error occured
*/
private static CMSSignedData readPKCS7(BufferedReader in, char[] p, String endMarker) throws IOException {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
String line; StringBuilder buffer = new StringBuilder();
while ( (line = in.readLine()) != null ) {
if ( line.contains(endMarker) ) break;
buffer.append( line.trim() );
final int len = buffer.length();
Base64.decode( buffer.substring(0, (len / 4) * 4), bytes );
buffer.delete(0, (len / 4) * 4);
}
if (buffer.length() != 0) {
throw new IOException("base64 data appears to be truncated");
}
if (line == null) throw new IOException(endMarker + " not found");
try {
ASN1InputStream aIn = new ASN1InputStream(bytes.toByteArray());
return new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
}
catch (CMSException e) {
throw new IOException("problem parsing PKCS7 object: " + e, e);
}
}
public static KeyFactory getKeyFactory(final AlgorithmIdentifier algId)
throws NoSuchAlgorithmException {
final ASN1ObjectIdentifier algIdentifier = algId.getAlgorithm();
String algorithm = null;
if ( X9ObjectIdentifiers.id_ecPublicKey.equals(algIdentifier) ) {
algorithm = "ECDSA";
}
else if ( PKCSObjectIdentifiers.rsaEncryption.equals(algIdentifier) ) {
algorithm = "RSA";
}
else if ( X9ObjectIdentifiers.id_dsa.equals(algIdentifier) ) {
algorithm = "DSA";
}
if ( algorithm == null ) algorithm = algIdentifier.getId();
return SecurityHelper.getKeyFactory(algorithm);
}
private static CertificateFactory getX509CertificateFactory() {
try {
return SecurityHelper.getCertificateFactory("X.509");
}
catch (CertificateException e) {
throw new IllegalStateException(e); // X.509 not supported?!
}
}
private static void writeHexEncoded(BufferedWriter out, byte[] bytes) throws IOException {
bytes = Hex.encode(bytes);
for (int i = 0; i != bytes.length; i++) {
out.write((char)bytes[i]);
}
}
private static void writeEncoded(BufferedWriter out,
byte[] bytes, final int bytesLen) throws IOException {
final char[] buf = new char[64];
bytes = Base64.encode(bytes, 0 ,bytesLen);
for (int i = 0; i < bytes.length; i += buf.length) {
int index = 0;
while (index != buf.length) {
if ((i + index) >= bytes.length) {
break;
}
buf[index] = (char) bytes[i + index];
index++;
}
out.write(buf, 0, index);
out.newLine();
}
}
private static byte[] readBase64Bytes(BufferedReader in, String endMarker) throws IOException {
return Base64.decode( readLines(in, endMarker).toString() );
}
private static StringBuilder readLines(final BufferedReader reader, final String endMarker) throws IOException {
String line;
StringBuilder lines = new StringBuilder(64);
while ( ( line = reader.readLine() ) != null ) {
if ( line.contains(endMarker) ) break;
lines.append( line.trim() );
}
if ( line == null ) {
throw new IOException(endMarker + " not found");
}
return lines;
}
}// PEM