2012-03-30 103 views
6

我試圖移植JXTA以在App Engine上運行。考慮到App Engine尚不支持BouncyCastle「BC」提供程序,我必須移植現有的JXTA代碼以使用白名單類生成X509Certificate。我對Crypto的瞭解很少,我不確定我想要完成的事情甚至是可能的。下面是從PSEUtils.java原始代碼從JXTA項目:使用bouncycastle生成X509Certificate X509v3CertificateBuilder

PSEUtils.java

還有其中包含java.security.cert.X509Certificate一個輔助類:

public static class IssuerInfo { 
    public X509Certificate cert; // subject Cert 
    public PrivateKey subjectPkey; // subject private key 
    public X509Certificate issuer; // issuer Cert 
    public PrivateKey issuerPkey; // issuer private key 
} 

在該方法:

public static IssuerInfo genCert(X500Principal subject, KeyPair keypair, IssuerInfo issuerinfo) 

我傳遞的主題爲:

new X500Principal("CN="+useCN) 

密鑰對(從原始代碼):

KeyPairGenerator g = KeyPairGenerator.getInstance("RSA"); 
g.initialize(1024, UTILS.srng); 
KeyPair keypair = g.generateKeyPair(); 

和JXTA編碼IssuerInfo。

現在,由於我無法使用bouncycastle.jce包,因此我必須刪除JXTA使用的X509Principal和X509V3CertificateGenerator代碼,並嘗試用符合GAE限制的實現替換它。這裏是我目前使用的genCert方法,使用org.bouncycastle.X509.X509v3CertificateBuilder

SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keypair.getPublic().getEncoded()); 

X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
    new X500Name(issuer.getName()), 
    BigInteger.ONE, 
    today, until, 
    new X500Name(subject.getName()), 
    subPubKeyInfo); 

的問題是,我不能讓keypair.getPublic().getEncoded()SubjectPublicKeyInfo.getInstance()方法工作。拋出java.lang.IllegalArgumentException異常:未知的對象工廠:[B

公鑰出現在檢查中填充:

Sun RSA public key, 1024 bits 
    modulus: 117521430893506212334140912845641570591161279468597426442875306202350445904550279678434051874985419676760802566018092318362676224355315431299979507080364677679613392086245588766565617009250512996843008784370448997729071786062596049780632058501646041736216482596596901215941577208285499619376322050871534546271 
    public exponent: 65537 

我發現下面的鏈接SO這表明該代碼的工作:

Sign CSR using Bouncy Castle

我嘗試轉換gencert將低於但出於某種原因,我不能讓過去從編碼的公共密鑰創建的SubjectPublicKeyInfo?

任何幫助是極大的讚賞。

public static IssuerInfo genCert(X500Principal subject, KeyPair keypair, IssuerInfo issuerinfo) { 
    IssuerInfo info = new IssuerInfo(); 
    try { 
     // set up issuer 
     PrivateKey signer; 
     X500Principal issuer; 

     if (null == issuerinfo) { // self-signed root cert 
      signer = keypair.getPrivate(); 
      issuer = new X500Principal(subject.getEncoded()); 
     } else { // issuer signed service sert 
      signer = issuerinfo.subjectPkey; 
      X500Principal issuer_subject = issuerinfo.cert.getSubjectX500Principal(); 
      issuer = new X500Principal(issuer_subject.getEncoded()); 
     } 

     // set validity 10 years from today 
     Date today = new Date(); 
     Calendar cal = Calendar.getInstance(); 

     cal.setTime(today); 
     cal.add(Calendar.YEAR, 10); 
     Date until = cal.getTime(); 

     SubjectPublicKeyInfo subPubKeyInfo = SubjectPublicKeyInfo.getInstance(keypair.getPublic().getEncoded()); 

    //**Can't get here so i'm not sure if the rest of this works?** 

     AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA"); 
     AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); 

     RSAPrivateCrtKeyParameters cps = (RSAPrivateCrtKeyParameters) keypair.getPrivate(); 
     ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(cps);    

    X509CertificateHolder certHolder = v3CertGen.build(sigGen); 

    CertificateFactory cf = CertificateFactory.getInstance("X.509"); 

    // Read user Certificate 
    InputStream is1 = new ByteArrayInputStream(certHolder.getEncoded()); 
    X509Certificate eeCert = (X509Certificate) cf.generateCertificate(is1); 
    is1.close(); 

回答

5

我能夠從此處提供劉若英梅爾霍費爾代碼的幫助來實現:link

X509CertificateGenerator.java

我提供了我的實現已經只在本地測試環境中進行測試,但它似乎工作:

package net.jxta.impl.membership.pse; 

import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.math.BigInteger; 
import java.security.InvalidKeyException; 
import java.security.Key; 
import java.security.KeyFactory; 
import java.security.KeyPair; 
import java.security.KeyPairGenerator; 
import java.security.KeyStore; 
import java.security.KeyStoreException; 
import java.security.NoSuchAlgorithmException; 
import java.security.NoSuchProviderException; 
import java.security.PrivateKey; 
import java.security.PublicKey; 
import java.security.SecureRandom; 
import java.security.Signature; 
import java.security.SignatureException; 
import java.security.UnrecoverableKeyException; 
import java.security.cert.CertificateException; 
import java.security.cert.X509Certificate; 
import java.security.interfaces.RSAPrivateCrtKey; 
import java.security.spec.InvalidKeySpecException; 
import java.security.spec.RSAPrivateCrtKeySpec; 
import java.security.spec.RSAPublicKeySpec; 
import java.util.Calendar; 
import java.util.Date; 
import java.util.logging.Logger; 

import javax.security.auth.x500.X500Principal; 

import org.apache.commons.codec.binary.Hex; 

import org.bouncycastle.asn1.ASN1Encodable; 
import org.bouncycastle.asn1.ASN1EncodableVector; 
import org.bouncycastle.asn1.ASN1InputStream; 
import org.bouncycastle.asn1.ASN1Sequence; 
import org.bouncycastle.asn1.DERBMPString; 
import org.bouncycastle.asn1.DERBitString; 
import org.bouncycastle.asn1.DERInteger; 
import org.bouncycastle.asn1.DERNull; 
import org.bouncycastle.asn1.DERObjectIdentifier; 
import org.bouncycastle.asn1.DEROutputStream; 
import org.bouncycastle.asn1.DERSequence; 
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 
import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 
import org.bouncycastle.asn1.x509.DigestInfo; 
import org.bouncycastle.asn1.x509.RSAPublicKeyStructure; 
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 
import org.bouncycastle.asn1.x509.TBSCertificateStructure; 
import org.bouncycastle.asn1.x509.Time; 
import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; 
import org.bouncycastle.asn1.x509.X509CertificateStructure; 
import org.bouncycastle.asn1.x509.X509Name; 
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; 
import org.bouncycastle.crypto.AsymmetricBlockCipher; 
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; 
import org.bouncycastle.crypto.CryptoException; 
import org.bouncycastle.crypto.DataLengthException; 
import org.bouncycastle.crypto.digests.SHA1Digest; 
import org.bouncycastle.crypto.encodings.PKCS1Encoding; 
import org.bouncycastle.crypto.engines.RSAEngine; 
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator; 
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters; 
import org.bouncycastle.crypto.params.RSAKeyParameters; 
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; 
import org.bouncycastle.jce.PrincipalUtil; 
import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; 
import org.bouncycastle.jce.provider.X509CertificateObject; 
import org.bouncycastle.x509.X509Util; 
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure; 

/** This class uses the Bouncycastle lightweight API to generate X.509 certificates programmatically. 
* It assumes a CA certificate and its private key to be available and can sign the new certificate with 
* this CA. Some of the code for this class was taken from 
* org.bouncycastle.x509.X509V3CertificateGenerator, but adapted to work with the lightweight API instead of 
* JCE (which is usually not available on MIDP2.0). 
* 
* @author Rene Mayrhofer 
*/ 
public class X509CertificateGenerator { 
    /** Our log4j logger. */ 
    private static Logger logger = Logger.getLogger(X509CertificateGenerator.class.getName()); 

    /** This holds the certificate of the CA used to sign the new certificate. The object is created in the constructor. */ 
    private X509Certificate caCert; 
    /** This holds the private key of the CA used to sign the new certificate. The object is created in the constructor. */ 
    private RSAPrivateCrtKeyParameters caPrivateKey; 

    private boolean useBCAPI; 
    private boolean useCACert; 

    public X509CertificateGenerator(String caFile, String caPassword, String caAlias, boolean useBCAPI) 
      throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, InvalidKeyException, NoSuchProviderException, SignatureException { 
     this.useBCAPI = useBCAPI; 
     this.useCACert = true; 
     logger.info("Loading CA certificate and private key from file '" + caFile + "', using alias '" + caAlias + "' with " 
       + (this.useBCAPI ? "Bouncycastle lightweight API" : "JCE API")); 
     KeyStore caKs = KeyStore.getInstance("PKCS12"); 
     caKs.load(new FileInputStream(new File(caFile)), caPassword.toCharArray()); 

     // load the key entry from the keystore 
     Key key = caKs.getKey(caAlias, caPassword.toCharArray()); 
     if (key == null) { 
      throw new RuntimeException("Got null key from keystore!"); 
     } 
     RSAPrivateCrtKey privKey = (RSAPrivateCrtKey) key; 
     caPrivateKey = new RSAPrivateCrtKeyParameters(privKey.getModulus(), privKey.getPublicExponent(), privKey.getPrivateExponent(), 
       privKey.getPrimeP(), privKey.getPrimeQ(), privKey.getPrimeExponentP(), privKey.getPrimeExponentQ(), privKey.getCrtCoefficient()); 
     // and get the certificate 
     caCert = (X509Certificate) caKs.getCertificate(caAlias); 
     if (caCert == null) { 
      throw new RuntimeException("Got null cert from keystore!"); 
     } 
     logger.info("Successfully loaded CA key and certificate. CA DN is '" + caCert.getSubjectDN().getName() + "'"); 
     caCert.verify(caCert.getPublicKey()); 
     logger.info("Successfully verified CA certificate with its own public key."); 
    } 
    public X509CertificateGenerator(boolean useBCAPI) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, FileNotFoundException, IOException, UnrecoverableKeyException, InvalidKeyException, NoSuchProviderException, SignatureException { 
     this.useBCAPI = useBCAPI; 
     this.useCACert = false; 
    } 

    public X509Certificate createCertificate(String dn, int validityDays, X500Principal issuer, KeyPair keypairca) throws 
      IOException, InvalidKeyException, SecurityException, SignatureException, NoSuchAlgorithmException, DataLengthException, CryptoException, KeyStoreException, NoSuchProviderException, CertificateException, InvalidKeySpecException { 
     logger.info("Generating certificate for distinguished subject name '" + 
       dn + "', valid for " + validityDays + " days"); 

     RSAPrivateCrtKey pK = (RSAPrivateCrtKey) keypairca.getPrivate(); 
     caPrivateKey = new RSAPrivateCrtKeyParameters(pK.getModulus(), pK.getPublicExponent(), pK.getPrivateExponent(), 
       pK.getPrimeP(), pK.getPrimeQ(), pK.getPrimeExponentP(), pK.getPrimeExponentQ(), pK.getCrtCoefficient()); 

     SecureRandom sr = new SecureRandom(); 

     PublicKey pubKey; 
     PrivateKey privKey; 

     logger.info("Creating RSA keypair"); 
     // generate the keypair for the new certificate 
     if (useBCAPI) { 
      RSAKeyPairGenerator gen = new RSAKeyPairGenerator(); 
      gen.init(new RSAKeyGenerationParameters(BigInteger.valueOf(3), sr, 1024, 80)); 
      AsymmetricCipherKeyPair keypair = gen.generateKeyPair(); 
      logger.info("Generated keypair, extracting components and creating public structure for certificate"); 
      RSAKeyParameters publicKey = (RSAKeyParameters) keypair.getPublic(); 
      RSAPrivateCrtKeyParameters privateKey = (RSAPrivateCrtKeyParameters) keypair.getPrivate(); 
      // used to get proper encoding for the certificate 
      RSAPublicKeyStructure pkStruct = new RSAPublicKeyStructure(publicKey.getModulus(), publicKey.getExponent()); 
      logger.info("New public key is '" + new String(Hex.encodeHex(pkStruct.getEncoded())) + 
        ", exponent=" + publicKey.getExponent() + ", modulus=" + publicKey.getModulus()); 
      // JCE format needed for the certificate - because getEncoded() is necessary... 
      pubKey = KeyFactory.getInstance("RSA").generatePublic(
        new RSAPublicKeySpec(publicKey.getModulus(), publicKey.getExponent())); 
      // and this one for the KeyStore 
      privKey = KeyFactory.getInstance("RSA").generatePrivate(
        new RSAPrivateCrtKeySpec(publicKey.getModulus(), publicKey.getExponent(), 
          privateKey.getExponent(), privateKey.getP(), privateKey.getQ(), 
          privateKey.getDP(), privateKey.getDQ(), privateKey.getQInv())); 
     } 
     else { 
      // this is the JSSE way of key generation 
      KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); 
      keyGen.initialize(1024, sr); 
      KeyPair keypair = keyGen.generateKeyPair(); 
      privKey = keypair.getPrivate(); 
      pubKey = keypair.getPublic(); 
     } 

     Calendar expiry = Calendar.getInstance(); 
     expiry.add(Calendar.DAY_OF_YEAR, validityDays); 

     X509Name x509Name = new X509Name("CN=" + dn); 
     X509Name x509Issuer = new X509Name(issuer.getName()); 
     V3TBSCertificateGenerator certGen = new V3TBSCertificateGenerator(); 
     certGen.setSerialNumber(new DERInteger(BigInteger.valueOf(System.currentTimeMillis()))); 
     certGen.setIssuer(x509Issuer);//issuer.getName());//PrincipalUtil.getSubjectX509Principal(caCert)); 
     certGen.setSubject(x509Name); 
     DERObjectIdentifier sigOID = X509Util.getAlgorithmOID("SHA1WithRSAEncryption"); 
     AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(sigOID, new DERNull()); 
     certGen.setSignature(sigAlgId); 
     certGen.setSubjectPublicKeyInfo(new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream(
       new ByteArrayInputStream(pubKey.getEncoded())).readObject())); 
     certGen.setStartDate(new Time(new Date(System.currentTimeMillis()))); 
     certGen.setEndDate(new Time(expiry.getTime())); 

     logger.info("Certificate structure generated, creating SHA1 digest"); 
     // attention: hard coded to be SHA1+RSA! 
     SHA1Digest digester = new SHA1Digest(); 
     AsymmetricBlockCipher rsa = new PKCS1Encoding(new RSAEngine()); 
     TBSCertificateStructure tbsCert = certGen.generateTBSCertificate(); 

     ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 
     DEROutputStream   dOut = new DEROutputStream(bOut); 
     dOut.writeObject(tbsCert); 

     // and now sign 
     byte[] signature; 
     if (useBCAPI) { 
      byte[] certBlock = bOut.toByteArray(); 
      // first create digest 
      logger.info("Block to sign is '" + new String(Hex.encodeHex(certBlock)) + "'");  
      digester.update(certBlock, 0, certBlock.length); 
      byte[] hash = new byte[digester.getDigestSize()]; 
      digester.doFinal(hash, 0); 
      // and sign that 
      rsa.init(true, caPrivateKey); 
      DigestInfo dInfo = new DigestInfo(new AlgorithmIdentifier(X509ObjectIdentifiers.id_SHA1, null), hash); 
      byte[] digest = dInfo.getEncoded(ASN1Encodable.DER); 
      signature = rsa.processBlock(digest, 0, digest.length); 
     } 
     else { 
      // or the JCE way 
      PrivateKey caPrivKey = KeyFactory.getInstance("RSA").generatePrivate(
        new RSAPrivateCrtKeySpec(caPrivateKey.getModulus(), caPrivateKey.getPublicExponent(), 
          caPrivateKey.getExponent(), caPrivateKey.getP(), caPrivateKey.getQ(), 
          caPrivateKey.getDP(), caPrivateKey.getDQ(), caPrivateKey.getQInv())); 

      Signature sig = Signature.getInstance(sigOID.getId()); 
      sig.initSign(caPrivKey, sr); 
      sig.update(bOut.toByteArray()); 
      signature = sig.sign(); 
     } 
     logger.info("SHA1/RSA signature of digest is '" + new String(Hex.encodeHex(signature)) + "'"); 

     // and finally construct the certificate structure 
     ASN1EncodableVector v = new ASN1EncodableVector(); 

     v.add(tbsCert); 
     v.add(sigAlgId); 
     v.add(new DERBitString(signature)); 

     X509CertificateObject clientCert = new X509CertificateObject(new X509CertificateStructure(new DERSequence(v))); 
     logger.info("Verifying certificate for correct signature with CA public key"); 

     if(useCACert) clientCert.verify(caCert.getPublicKey()); 

     return clientCert; 
    } 


} 

+0

可否請你更新這一類,因爲許多這些方法/類現在在BC中被棄用了? – Reddy 2013-02-19 04:41:59

+0

BC現在在App Engine中受支持,因此不需要。 – 2013-02-25 21:30:20

+0

@ChukDiesel如果我們這些人不使用App Engine?請? – Phil 2013-10-25 11:10:40