Passbook and Google App Engine (signing the manifest)


Google App Engine 使ってiOS6で追加された新機能 Passbook のファイルを生成するためのメモ

必要なもの

BouncyCastle
GAEで使う為には コチラを参考にjarを作って war/WEB-INF/lib に入れとく
Apple WWDRCA.p7b
iOS Provisioning Portalでダウンロードできる “Apple WWDRCA.cer” をキーチェーンアクセスに取り込んでp7b形式で書き出したファイル
PassTypeID.p12
iOS Provisioning Portalで登録した PassTypeID.cer をキーチェーンアクセスに取り込んでp12形式で書き出したファイル

同等の処理をするopensslコマンド

openssl smime -binary -sign \
    -certfile WWDRCA.pem \
    -signer certificate.pem -inkey key.pem \
    -in manifest.json -out signature \
    -outform DER

サンプルコード

GetX509Certificate

    /** 証明書を取り出す
     * 
     * @param p12fis
     * @param p12pw
     * @return 証明書
     */
    static Map<String,Object> GetX509Certificate(final InputStream p12fis,final String p12pw){
        Map<String,Object> map = new HashMap<String,Object>();;
        try{
            //AppleWWDRCA.p7b
            {
                FileInputStream wwdrcafis = new FileInputStream("AppleWWDRCA.p7b");
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                @SuppressWarnings("unchecked")
                Collection<X509Certificate> col = (Collection<X509Certificate>) cf.generateCertificates(wwdrcafis);
                X509Certificate cer = null;
                for (Iterator<X509Certificate> it = col.iterator(); it.hasNext();) {
                    cer = it.next();
                }
                map.put("AppleWWDRCA", cer);
            }
        }catch (Exception e) {
            _logger.warning(e.toString());
        }
        
        try{
            {
                //KeyStore 生成
                KeyStore ks = KeyStore.getInstance("PKCS12");
                ks.load(p12fis, p12pw.toCharArray());
                
                //Element
                String ali = ks.aliases().nextElement();
                //証明書取り出し
                // openssl pkcs12 -passin pass:$PASSWORD -in $P12 -clcerts -nokeys -out certificate.pem
                X509Certificate cer = (X509Certificate) ks.getCertificate(ali);
    
                //秘密鍵取り出し
                // openssl pkcs12 -passin pass:$PASSWORD  -in $P12 -nocerts -out key.pem
                //KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(ali, new KeyStore.PasswordProtection( p12pw.toCharArray() ));
                //PrivateKey privateKey = pkEntry.getPrivateKey();
                PrivateKey privateKey = (PrivateKey) ks.getKey(ali, p12pw.toCharArray());
                
                map.put("X509Certificate", cer);
                map.put("PrivateKey", privateKey);
                
                {
                    String subjects = cer.getSubjectDN().getName();
                    String[] pvs = subjects.split(",");
                    for(int i=0;i<pvs.length;i++){
                        String[] pv = pvs[i].split("=");
                        map.put(pv[0].toUpperCase().trim(), pv[1].trim());
                    }
                }
            }
        }
        catch (Exception e) {
            _logger.warning(e.toString());
        }
        return map;
    }

CreateSignatureDer

    /** DER 形式で Signature を作成する BouncyCastleProvider
     * 
     * @param privateKey
     * @param cer
     * @param manifest_json
     * @return
     * @throws Exception
     */
    static private byte[] CreateSignatureDer(final byte[] manifest_json,final Map<String,Object> param) throws Exception {
        ByteArrayOutputStream baos = null;

        //証明書取り出し
        X509Certificate applewwdrca_cer   = (X509Certificate) param.get("AppleWWDRCA");
        
        //証明書取り出し
        X509Certificate cer   = (X509Certificate) param.get("X509Certificate");
        //秘密鍵取り出し
        PrivateKey privateKey = (PrivateKey) param.get("PrivateKey");
        
        //Signature 作成
        Security.addProvider( new BouncyCastleProvider() );
        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
        ContentSigner sha1signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(privateKey);
        DigestCalculatorProvider builder = new JcaDigestCalculatorProviderBuilder().setProvider("BC").build();
        SignerInfoGenerator generator = new JcaSignerInfoGeneratorBuilder(builder).build(sha1signer, cer);
        gen.addSignerInfoGenerator(generator);
        List<Certificate> certList = new ArrayList<Certificate>();
        certList.add(applewwdrca_cer);
        certList.add(cer);
        gen.addCertificates( new JcaCertStore(certList) );
        CMSTypedData msg = new CMSProcessableByteArray(manifest_json);
        CMSSignedData sigdata = gen.generate(msg);
        
        //DER 形式の byte[] を作成
        baos = new ByteArrayOutputStream();
        ASN1InputStream asnIS = new ASN1InputStream( sigdata.getEncoded() );
        DEROutputStream deros = new DEROutputStream(baos);
        deros.writeObject( asnIS.readObject() );
        deros.flush();

        return (baos != null)?baos.toByteArray():null;
    }


コメントを残す

メールアドレスが公開されることはありません。