问题
I have some odd requirements that I have to live with. I need to pass my crypto system a TBS certificate, they will sign it and send back a String of the signature which I need to incorporate into a certificate to make a signed certificate.
Looking at com.ibm.security.x509.X509CertImpl and various BouncyCastle posts on SO, I can't find out how to do that.
Questions:
Is this possible ? If so, how ?
回答1:
I'd refer to the source code for the BouncyCastle X509v3CertificateBuilder class (pkix jar), and tweak it to suit your needs. Note that this class uses a V3TBSCertificateGenerator to produce the TBSCertificate. That's an ASN.1 object that you can DER encode. Then you can get the DER encoding signed by the "crypto system". Then consult X509v3CertificateBuilder.build() method as to how to put the TBS certificate and signature together into the final X.509 certificate.
回答2:
I've put together an example that shows one way to do it. Most of this code was stolen from the bouncycastle pkix or lwcrypto libraries, but any bugs are almost certainly mine. The most important method to concentrate on below is generateCert
. The rest of the code is test harness to drive the test.
The code is specifically written to need only the bounycastle bcpkix and lwcrypto jars. It could be shortened somewhat if the bcprov jar was used instead of lwcrypto.
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.TBSCertificate;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.bc.BcX509v3CertificateBuilder;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
import javax.security.auth.x500.X500Principal;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateCrtKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
public class Main {
private static class TBSCertPlusSignature {
private final byte[] encodedTbsCert;
private final byte[] signature;
public TBSCertPlusSignature(byte[] encodedTbsCert, byte[] signature) {
this.encodedTbsCert = encodedTbsCert;
this.signature = signature;
}
public byte[] getEncodedTbsCert() {
return encodedTbsCert;
}
public byte[] getSignature() {
return signature;
}
}
private static TBSCertPlusSignature makeTestCert(KeyPair keyPair) throws Exception {
Date now = new Date();
Date nowPlus1Hour = new Date(now.getTime() + 1000 * 60 * 60 * 1L);
byte[] encodedName = new X500Principal("CN=Duke, OU=JavaSoft, O=Sun Microsystems, C=US").getEncoded();
X500Name issuer = X500Name.getInstance(encodedName);
X500Name subject = issuer;
RSAPublicKey rsaPub = (RSAPublicKey) keyPair.getPublic();
RSAKeyParameters rsaPubParams = new RSAKeyParameters(false, rsaPub.getModulus(), rsaPub.getPublicExponent());
BcX509v3CertificateBuilder certBuilder = new BcX509v3CertificateBuilder(
issuer,
BigInteger.valueOf(100L),
now,
nowPlus1Hour,
subject,
rsaPubParams
);
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256WithRSA");
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) keyPair.getPrivate();
RSAPrivateCrtKeyParameters rsaPrivParams = new RSAPrivateCrtKeyParameters(
rsaPriv.getModulus(),
rsaPriv.getPublicExponent(),
rsaPriv.getPrivateExponent(),
rsaPriv.getPrimeP(),
rsaPriv.getPrimeQ(),
rsaPriv.getPrimeExponentP(),
rsaPriv.getPrimeExponentQ(),
rsaPriv.getCrtCoefficient()
);
ContentSigner contentSigner = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(rsaPrivParams);
final X509CertificateHolder x509CertificateHolder = certBuilder.build(contentSigner);
byte[] tbsCertDER = x509CertificateHolder.toASN1Structure().getTBSCertificate().getEncoded();
byte[] signature = x509CertificateHolder.getSignature();
return new TBSCertPlusSignature(tbsCertDER, signature);
}
private static X509Certificate generateCert(byte[] tbsCertEncoded, byte[] signature) throws Exception {
// Given the der encoded TBS cert and signature, create the corresponding X509 certificate
TBSCertificate tbsCert = TBSCertificate.getInstance(tbsCertEncoded);
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(tbsCert);
v.add(tbsCert.getSignature());
v.add(new DERBitString(signature));
DERSequence derSequence = new DERSequence(v);
ByteArrayInputStream baos = new ByteArrayInputStream(derSequence.getEncoded());
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(baos);
}
public static void main(String[] args) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair keyPair = kpg.generateKeyPair();
TBSCertPlusSignature testData = makeTestCert(keyPair);
X509Certificate x509Cert = generateCert(testData.getEncodedTbsCert(), testData.getSignature());
// Verify the signature
x509Cert.verify(keyPair.getPublic());
// Print the cert
PublicKey publicKey = x509Cert.getPublicKey();
System.out.println(x509Cert);
}
}
来源:https://stackoverflow.com/questions/39731781/adding-a-signature-to-a-certificate