BCを使用してCSRに署名する方法を説明するコード/ドキュメントが見つかりません。入力として、バイト配列としてのCSRがあり、PEMまたはDER形式、あるいはその両方で証明書を取得したいと考えています。
ここまで来た
def signCSR(csrData:Array[Byte], ca:CACertificate, caPassword:String) = {
val csr = new PKCS10CertificationRequestHolder(csrData)
val spi = csr.getSubjectPublicKeyInfo
val ks = new Java.security.spec.X509EncodedKeySpec(spi.getDEREncoded())
val kf = Java.security.KeyFactory.getInstance("RSA")
val pk = kf.generatePublic(ks)
val (caCert, caPriv) = parsePKCS12(ca.pkcs12data, caPassword)
val fromDate : Java.util.Date = new Java.util.Date // FixMe
val toDate = fromDate // FixMe
val issuer = PrincipalUtil.getIssuerX509Principal(caCert)
val contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(caPriv)
val serial = BigInt(CertSerialnumber.nextSerialNumber)
val certgen = new JcaX509v3CertificateBuilder(new X500Name(issuer.getName), serial.bigInteger, fromDate, toDate, csr.getSubject, pk)
これをPEMまたはDER形式で保存するための証明書ジェネレーターからの取得を理解するのに問題があります。
それとも私は間違った道を一緒に下っていますか?
わかりました...同じことをしたいと思っていたのですが、私の人生の中で、どうすればいいのかわかりませんでした。 APIはすべて、キーペアの生成と証明書の生成について話しますが、CSRへの署名方法については話しません。どういうわけか、かなり偶然-ここが私が見つけたものです。
PKCS10は(CSRの)要求の形式を表すため、最初にCSRをPKCS10Holderに配置する必要があります。次に、それをCertificateBuilderに渡します(CertificateGeneratorは非推奨であるため)。渡す方法は、ホルダーでgetSubjectを呼び出すことです。
これがコードです(Java、必要に応じて調整してください):
public static X509Certificate sign(PKCS10CertificationRequest inputCSR, PrivateKey caPrivate, KeyPair pair)
throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchProviderException, SignatureException, IOException,
OperatorCreationException, CertificateException {
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
.find("SHA1withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder()
.find(sigAlgId);
AsymmetricKeyParameter foo = PrivateKeyFactory.createKey(caPrivate
.getEncoded());
SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pair
.getPublic().getEncoded());
PKCS10CertificationRequestHolder pk10Holder = new PKCS10CertificationRequestHolder(inputCSR);
//in newer version of BC such as 1.51, this is
//PKCS10CertificationRequest pk10Holder = new PKCS10CertificationRequest(inputCSR);
X509v3CertificateBuilder myCertificateGenerator = new X509v3CertificateBuilder(
new X500Name("CN=issuer"), new BigInteger("1"), new Date(
System.currentTimeMillis()), new Date(
System.currentTimeMillis() + 30 * 365 * 24 * 60 * 60
* 1000), pk10Holder.getSubject(), keyInfo);
ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
.build(foo);
X509CertificateHolder holder = myCertificateGenerator.build(sigGen);
X509CertificateStructure eeX509CertificateStructure = holder.toASN1Structure();
//in newer version of BC such as 1.51, this is
//org.spongycastle.asn1.x509.Certificate eeX509CertificateStructure = holder.toASN1Structure();
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
// Read Certificate
InputStream is1 = new ByteArrayInputStream(eeX509CertificateStructure.getEncoded());
X509Certificate theCert = (X509Certificate) cf.generateCertificate(is1);
is1.close();
return theCert;
//return null;
}
ご覧のとおり、このメソッドの外でリクエストを生成しましたが、渡しました。次に、これをコンストラクター引数として受け入れるPKCS10CertificationRequestHolderを用意しました。
次に、X509v3CertificateBuilderの引数にpk10Holder.getSubjectが表示されます-これだけで十分でしょうか?何か足りない場合はお知らせください!!!それは私のために働いた。私が正しく生成した証明書には、必要なDN情報が含まれていました。
ウィキペディアにはPKCSに関するキラーセクションがあります- http://en.wikipedia.org/wiki/PKCS
次のコードは上記の回答に基づいていますが、コンパイルされ、(keytoolによってエクスポートされた種類の)PEMエンコードされたCSRが指定されると、(インポート可能なタイプの)署名済み証明書チェーンを含む有効なPEMエンコードされたsignedDataオブジェクトを返しますkeytoolによる)。
ああ、それはBouncyCastle 1.49に対するものです。
import Java.security.*;
import Java.io.*;
import Java.util.Date;
import Java.math.BigInteger;
import Java.security.cert.X509Certificate;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.asn1.x500.*;
import org.bouncycastle.asn1.pkcs.*;
import org.bouncycastle.openssl.*;
import org.bouncycastle.pkcs.*;
import org.bouncycastle.cert.*;
import org.bouncycastle.cms.*;
import org.bouncycastle.cms.jcajce.*;
import org.bouncycastle.crypto.util.*;
import org.bouncycastle.operator.*;
import org.bouncycastle.operator.bc.*;
import org.bouncycastle.operator.jcajce.*;
import org.bouncycastle.util.encoders.Base64;
/**
* Given a Keystore containing a private key and certificate and a Reader containing a PEM-encoded
* Certificiate Signing Request (CSR), sign the CSR with that private key and return the signed
* certificate as a PEM-encoded PKCS#7 signedData object. The returned value can be written to a file
* and imported into a Java KeyStore with "keytool -import -trustcacerts -alias subjectalias -file file.pem"
*
* @param pemcsr a Reader from which will be read a PEM-encoded CSR (begins "-----BEGIN NEW CERTIFICATE REQUEST-----")
* @param validity the number of days to sign the Certificate for
* @param keystore the KeyStore containing the CA signing key
* @param alias the alias of the CA signing key in the KeyStore
* @param password the password of the CA signing key in the KeyStore
*
* @return a String containing the PEM-encoded signed Certificate (begins "-----BEGIN PKCS #7 SIGNED DATA-----")
*/
public static String signCSR(Reader pemcsr, int validity, KeyStore keystore, String alias, char[] password) throws Exception {
PrivateKey cakey = (PrivateKey)keystore.getKey(alias, password);
X509Certificate cacert = (X509Certificate)keystore.getCertificate(alias);
PEMReader reader = new PEMReader(pemcsr);
PKCS10CertificationRequest csr = new PKCS10CertificationRequest((CertificationRequest)reader.readObject());
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
X500Name issuer = new X500Name(cacert.getSubjectX500Principal().getName());
BigInteger serial = new BigInteger(32, new SecureRandom());
Date from = new Date();
Date to = new Date(System.currentTimeMillis() + (validity * 86400000L));
X509v3CertificateBuilder certgen = new X509v3CertificateBuilder(issuer, serial, from, to, csr.getSubject(), csr.getSubjectPublicKeyInfo());
certgen.addExtension(X509Extension.basicConstraints, false, new BasicConstraints(false));
certgen.addExtension(X509Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(csr.getSubjectPublicKeyInfo()));
certgen.addExtension(X509Extension.authorityKeyIdentifier, false, new AuthorityKeyIdentifier(new GeneralNames(new GeneralName(new X509Name(cacert.getSubjectX500Principal().getName()))), cacert.getSerialNumber()));
ContentSigner signer = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(PrivateKeyFactory.createKey(cakey.getEncoded()));
X509CertificateHolder holder = certgen.build(signer);
byte[] certencoded = holder.toASN1Structure().getEncoded();
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
signer = new JcaContentSignerBuilder("SHA1withRSA").build(cakey);
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(signer, cacert));
generator.addCertificate(new X509CertificateHolder(certencoded));
generator.addCertificate(new X509CertificateHolder(cacert.getEncoded()));
CMSTypedData content = new CMSProcessableByteArray(certencoded);
CMSSignedData signeddata = generator.generate(content, true);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write("-----BEGIN PKCS #7 SIGNED DATA-----\n".getBytes("ISO-8859-1"));
out.write(Base64.encode(signeddata.getEncoded()));
out.write("\n-----END PKCS #7 SIGNED DATA-----\n".getBytes("ISO-8859-1"));
out.close();
return new String(out.toByteArray(), "ISO-8859-1");
}
アーチーありがとう!
コードをいくつか変更しました。以下を参照してください。
主な変更は、発行者の名前を渡し、CSRからの公開鍵を使用することです。
val caCert = PEMToCert(issuerPEM).get
val issuer = PrincipalUtil.getIssuerX509Principal(caCert)
val csr = new PKCS10CertificationRequestHolder(csrData)
val serial = BigInt(CertSerialNumber.nextSerialNumber)
val spi = csr.getSubjectPublicKeyInfo();
val certgen = new X509v3CertificateBuilder(
new X500Name(issuer.getName),
serial.bigInteger,
new Java.util.Date(),
new Date(System.currentTimeMillis() + 30 * 365 * 24 * 60 * 60 * 1000),
csr.getSubject,
csr.getSubjectPublicKeyInfo())
certgen.addExtension(
X509Extension.subjectKeyIdentifier,
false,
spi
)
val issuerPK = PEMToPK(issuerPKPEM, caPassword).get
val contentSigner = new JcaContentSignerBuilder(contentSignerAlg).setProvider(BC).build(issuerPK.getPrivate())
val x509 = (new JcaX509CertificateConverter).setProvider(BC).getCertificate(certgen.build(contentSigner))
結局、これは私にとってうまくいったものです:
KeyPair serverKeyPair = keyPairLoader.getKeyPair(); //my own class
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate serverCertificate = getServerCertificate(certificateFactory);
org.spongycastle.asn1.x509.Certificate eeX509CertificateStructure = signCertificateSigningRequest(
jcaPKCS10CertificationRequest, keyPair, serverCertificate);
Java.security.cert.X509Certificate signedCertificate = readCertificateFromASN1Certificate(
eeX509CertificateStructure, certificateFactory);
コードがどこにあるか
private org.spongycastle.asn1.x509.Certificate signCertificateSigningRequest(
JcaPKCS10CertificationRequest jcaPKCS10CertificationRequest,
KeyPair keyPair, X509Certificate serverCertificate)
throws IOException, OperatorCreationException, NoSuchAlgorithmException, InvalidKeyException
{
// Signing CSR
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
.find("SHA1withRSA");
X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
serverCertificate,
new BigInteger("1"), //serial
new Date(System.currentTimeMillis()),
new Date(System.currentTimeMillis() + 30L * 365L * 24L * 60L * 60L * 1000L),
jcaPKCS10CertificationRequest.getSubject(),
jcaPKCS10CertificationRequest.getPublicKey()
/*).addExtension(
new ASN1ObjectIdentifier("2.5.29.35"),
false,
new AuthorityKeyIdentifier(keyPair.getPublic().getEncoded())*/
).addExtension(
new ASN1ObjectIdentifier("2.5.29.19"),
false,
new BasicConstraints(false) // true if it is allowed to sign other certs
).addExtension(
new ASN1ObjectIdentifier("2.5.29.15"),
true,
new X509KeyUsage(
X509KeyUsage.digitalSignature |
X509KeyUsage.nonRepudiation |
X509KeyUsage.keyEncipherment |
X509KeyUsage.dataEncipherment));
AsymmetricKeyParameter asymmetricKeyParameter =
PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
//ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(asymmetricKeyParameter);
ContentSigner sigGen = new JcaContentSignerBuilder("SHA1withRSA").build(keyPair.getPrivate());
X509CertificateHolder x509CertificateHolder = certificateBuilder.build(sigGen);
org.spongycastle.asn1.x509.Certificate eeX509CertificateStructure =
x509CertificateHolder.toASN1Structure();
return eeX509CertificateStructure;
}
private X509Certificate readCertificateFromASN1Certificate(
org.spongycastle.asn1.x509.Certificate eeX509CertificateStructure,
CertificateFactory certificateFactory)
throws IOException, CertificateException {
// Read Certificate
InputStream is1 = new ByteArrayInputStream(eeX509CertificateStructure.getEncoded());
X509Certificate signedCertificate =
(X509Certificate) certificateFactory.generateCertificate(is1);
return signedCertificate;
}
そして、これはPEMに変換できます:
private String convertCertificateToPEM(X509Certificate signedCertificate) throws IOException {
StringWriter signedCertificatePEMDataStringWriter = new StringWriter();
JcaPEMWriter pemWriter = new JcaPEMWriter(signedCertificatePEMDataStringWriter);
pemWriter.writeObject(signedCertificate);
pemWriter.close();
log.info("PEM data:");
log.info("" + signedCertificatePEMDataStringWriter.toString());
return signedCertificatePEMDataStringWriter.toString();
}
@マイクB-例を徹底的にテストしましたか?私はあなたのコードで奇妙な振る舞いをします:私はbc15onバージョンを使用しています。自己署名CAを使用してクライアント要求に署名すると、IEにインポートされ、チェーン内のCAで証明書が有効であることが示されます
ただし、FFにインポートすると、チェーン内のCAの右側にあるイメージが欠落していることがわかり、ffはそれを信頼された機関に確認することができません。また、IEまたはFFを使用すると、それを使用してWebサーバーを認証しようとすると、httpが信頼できる機関に対しても検証できないため、失敗します。
私は自分のニーズに合うようにコードにいくつかの変更を加えましたが、一般的には同じである必要があります。
public static String GenCert(long SerNum, int addYear, int addHours,
String reqText,
String reqName) throws Exception,
SQLException {
String result = "";
reqText = csr; // hard code base64 csr for testing purposes
reqText =
"-----BEGIN CERTIFICATE REQUEST-----\n" + reqText +
"\n-----END CERTIFICATE REQUEST-----\n";
try {
String castr = ca + "\n"; // hard code base64 CA pub key for testing
String strPriv = caPrivk + "\n"; // hard code base64 CA private key for testing
byte[] encKey = castr.getBytes();
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate caCert =
(X509Certificate)cf.generateCertificate(new ByteArrayInputStream(encKey));
PEMParser pr = new PEMParser(new StringReader(strPriv));
Object obj = pr.readObject();
JcaPEMKeyConverter converter =
new JcaPEMKeyConverter().setProvider("BC");
KeyPair kp;
kp = converter.getKeyPair((PEMKeyPair)obj);
PrivateKey privateKey = kp.getPrivate();
// parse the request
PEMParser pRd =
new PEMParser(new InputStreamReader(new ByteArrayInputStream(reqText.getBytes())));
PKCS10CertificationRequest csr =
(PKCS10CertificationRequest)pRd.readObject();
String strReq = csr.getSubject().toString();
strReq = strReq.substring(strReq.indexOf("CN=") + 3).trim();
if (strReq.indexOf(",") > 0)
strReq = strReq.substring(0, strReq.indexOf(",")).trim();
if (!strReq.equals(reqName)) {
return "";
}
AlgorithmIdentifier sigAlgId =
new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA"); //SHA1withRSA
AlgorithmIdentifier digAlgId =
new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
X500Name issuer =
new X500Name(caCert.getSubjectX500Principal().getName());
BigInteger serial = BigInteger.valueOf(SerNum);
// The date object returns GMT format
Date date = new Date(System.currentTimeMillis() - 180 * 1000);
date.setHours(date.getHours() + addHours);
Calendar cal = Calendar.getInstance();
Date from = date;
cal.setTime(date);
cal.add(1, addYear);
Date to = cal.getTime();
SubjectPublicKeyInfo pkInfo = csr.getSubjectPublicKeyInfo();
//SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded());
RSAKeyParameters rsa =
(RSAKeyParameters)PublicKeyFactory.createKey(pkInfo);
RSAPublicKeySpec rsaSpec =
new RSAPublicKeySpec(rsa.getModulus(), rsa.getExponent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey rsaPub = kf.generatePublic(rsaSpec);
X509v3CertificateBuilder certgen =
new X509v3CertificateBuilder(issuer, serial, from, to,
csr.getSubject(),
csr.getSubjectPublicKeyInfo());
certgen.addExtension(X509Extension.basicConstraints, false,
new BasicConstraints(false));
certgen.addExtension(X509Extension.subjectKeyIdentifier, false,
new SubjectKeyIdentifier(pkInfo));
// certgen.addExtension(X509Extension.subjectKeyIdentifier, false,
// new SubjectKeyIdentifierStructure(rsaPub)); // In old version done with much more extensive parsing
certgen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
new AuthorityKeyIdentifierStructure(caCert));
// certgen.addExtension(X509Extension.authorityKeyIdentifier, false,
// new AuthorityKeyIdentifier(new GeneralNames(new GeneralName(new X509Name(caCert.getSubjectX500Principal().getName()))),
// caCert.getSerialNumber()));
// add certificate purposes
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(new DERObjectIdentifier("1.3.6.1.5.5.7.3.2"));
vector.add(new DERObjectIdentifier("1.3.6.1.4.1.311.20.2.2"));
vector.add(new DERObjectIdentifier("1.3.6.1.4.1.311.10.3.12"));
vector.add(new DERObjectIdentifier("1.3.6.1.5.5.7.3.4"));
DERSequence seq = new DERSequence(vector);
certgen.addExtension(X509Extensions.ExtendedKeyUsage, false, seq);
ContentSigner signer =
new BcRSAContentSignerBuilder(sigAlgId,
digAlgId).build(PrivateKeyFactory.createKey(privateKey.getEncoded()));
X509CertificateHolder holder = certgen.build(signer);
byte[] certencoded = holder.toASN1Structure().getEncoded();
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
signer =
new JcaContentSignerBuilder("SHA1withRSA").build(privateKey);
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(signer,
caCert));
generator.addCertificate(new X509CertificateHolder(certencoded));
generator.addCertificate(new X509CertificateHolder(caCert.getEncoded()));
CMSTypedData content = new CMSProcessableByteArray(certencoded);
CMSSignedData signeddata = generator.generate(content, true);
result = Base64Utils.base64Encode(signeddata.getEncoded());
} catch (Exception e) {
result = e.toString();
getStackTrace(e);
}
return result;
}
バウンシーキャッスル1.4を使用した古いバージョンのコードでは、X509V3CertificateGeneratorを使用し、コンテンツを返す直前に、次のようにチェーンを構築するために使用しました。
X509Certificate newCert =
certGen.generateX509Certificate(privateKey, "BC");
//=============================
List chain = new ArrayList();
chain.add(newCert);
//-------------------------------------------------
// create the CertPath with old BouncyCastle
CertificateFactory fact =
CertificateFactory.getInstance("X.509", "BC");
CertPath path = fact.generateCertPath(chain);
result = Base64Utils.base64Encode(path.getEncoded("PKCS7"));
[〜#〜] update [〜#〜]:OKケースが解決されました。おかげで this thread 明らかに使用する場合:
cacert.getSubjectX500Principal()。getName()
私は代わりに発行者の名前を逆に取得しましたが、チェーンを壊しました:
cert.getSubjectX500Principal()。getEncoded()が解決しました!したがって、CAが信頼できる機関まで検証されない場合は、名前を正しく取得していることを確認してください。