123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- /* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ==================================================================== */
-
- /* ====================================================================
- This product contains an ASLv2 licensed version of the OOXML signer
- package from the eID Applet project
- http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
- Copyright (C) 2008-2014 FedICT.
- ================================================================= */
- package org.apache.poi.poifs.crypt.dsig;
-
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.math.BigInteger;
- import java.nio.charset.StandardCharsets;
- import java.security.GeneralSecurityException;
- import java.security.KeyPair;
- import java.security.KeyPairGenerator;
- import java.security.KeyStore;
- import java.security.KeyStoreException;
- import java.security.NoSuchAlgorithmException;
- import java.security.PrivateKey;
- import java.security.PublicKey;
- import java.security.UnrecoverableKeyException;
- import java.security.cert.Certificate;
- import java.security.cert.CertificateEncodingException;
- import java.security.cert.CertificateException;
- import java.security.cert.CertificateFactory;
- import java.security.cert.X509CRL;
- import java.security.cert.X509Certificate;
- import java.security.interfaces.RSAPublicKey;
- import java.security.spec.RSAKeyGenParameterSpec;
- import java.util.AbstractMap;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Calendar;
- import java.util.Collections;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.function.Function;
- import java.util.stream.Collectors;
- import java.util.stream.Stream;
-
- import javax.security.auth.x500.X500Principal;
-
- import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
- import org.apache.poi.poifs.crypt.CryptoFunctions;
- import org.apache.poi.poifs.storage.RawDataUtil;
- import org.apache.poi.util.LocaleUtil;
- import org.apache.poi.util.RandomSingleton;
- import org.bouncycastle.asn1.DEROctetString;
- import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
- import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
- import org.bouncycastle.asn1.x500.X500Name;
- import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
- import org.bouncycastle.asn1.x509.BasicConstraints;
- import org.bouncycastle.asn1.x509.CRLNumber;
- import org.bouncycastle.asn1.x509.Extension;
- import org.bouncycastle.asn1.x509.Extensions;
- import org.bouncycastle.asn1.x509.KeyUsage;
- import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
- import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
- import org.bouncycastle.cert.X509CRLHolder;
- import org.bouncycastle.cert.X509CertificateHolder;
- import org.bouncycastle.cert.X509ExtensionUtils;
- import org.bouncycastle.cert.X509v2CRLBuilder;
- import org.bouncycastle.cert.X509v3CertificateBuilder;
- import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
- import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
- import org.bouncycastle.cert.ocsp.BasicOCSPResp;
- import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
- import org.bouncycastle.cert.ocsp.CertificateID;
- import org.bouncycastle.cert.ocsp.CertificateStatus;
- import org.bouncycastle.cert.ocsp.OCSPException;
- import org.bouncycastle.cert.ocsp.OCSPReq;
- import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
- import org.bouncycastle.cert.ocsp.OCSPResp;
- import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
- import org.bouncycastle.cert.ocsp.Req;
- import org.bouncycastle.crypto.params.RSAKeyParameters;
- import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
- import org.bouncycastle.openssl.PEMParser;
- import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
- import org.bouncycastle.operator.ContentSigner;
- import org.bouncycastle.operator.DigestCalculator;
- import org.bouncycastle.operator.OperatorCreationException;
- import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
- import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
-
- public class DummyKeystore {
- public static class KeyCertPair {
- private final PrivateKey key;
- private final List<X509Certificate> x509chain;
-
- public KeyCertPair(PrivateKey key, Certificate[] x509chain) {
- this.key = key;
- this.x509chain = Stream.of(x509chain).map(X509Certificate.class::cast).collect(Collectors.toList());
- }
-
- public PrivateKey getKey() {
- return key;
- }
-
- public X509Certificate getX509() {
- return x509chain.get(0);
- }
-
- public List<X509Certificate> getX509Chain() {
- return x509chain;
- }
- }
-
- private static final String DUMMY_ALIAS = "Test";
- private static final String DUMMY_PASS = "test";
-
- private final KeyStore keystore;
-
- public DummyKeystore(String storePass) throws GeneralSecurityException, IOException {
- this((File)null, storePass);
- }
-
- public DummyKeystore(File storeFile, String storePass) throws GeneralSecurityException, IOException {
- CryptoFunctions.registerBouncyCastle();
- keystore = KeyStore.getInstance("PKCS12");
- try (InputStream fis = storeFile != null && storeFile.exists() ? new FileInputStream(storeFile) : null) {
- keystore.load(fis, storePass.toCharArray());
- }
- }
-
- public DummyKeystore(String pfxInput, String storePass) throws GeneralSecurityException, IOException {
- CryptoFunctions.registerBouncyCastle();
- keystore = KeyStore.getInstance("PKCS12");
- try (InputStream fis = new UnsynchronizedByteArrayInputStream(RawDataUtil.decompress(pfxInput))) {
- keystore.load(fis, storePass.toCharArray());
- }
- }
-
- /**
- * Create dummy key
- * @return the alias of the dummy key
- */
- public KeyCertPair createDummyKey() throws GeneralSecurityException, IOException, OperatorCreationException {
- return addEntry(DUMMY_ALIAS, DUMMY_PASS, 2048, 24);
- }
-
- public KeyCertPair addEntryFromPEM(File pemFile, String keyPass) throws IOException, CertificateException, KeyStoreException {
- PrivateKey key = null;
- X509Certificate x509 = null;
-
- try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(pemFile), StandardCharsets.ISO_8859_1))) {
- PEMParser parser = new PEMParser(br);
- for (Object obj; (obj = parser.readObject()) != null; ) {
- if (obj instanceof PrivateKeyInfo) {
- key = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey((PrivateKeyInfo)obj);
- } else if (obj instanceof X509CertificateHolder) {
- x509 = new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)obj);
- }
- }
- }
-
- if (key == null || x509 == null) {
- throw new IOException("Please add private key and certificate in the PEM file.");
- }
-
- String alias = x509.getSubjectX500Principal().getName();
- keystore.setKeyEntry(alias, key, keyPass.toCharArray(), new Certificate[]{x509});
-
- return new KeyCertPair(key, new Certificate[]{x509});
- }
-
-
- /**
- * Add an entry with password, keySize and expiry values. Ignore if alias is already in keystore
- * @param keySize multiple of 1024, e.g. 1024, 2048
- */
- public KeyCertPair addEntry(String keyAlias, String keyPass, int keySize, int expiryInMonths) throws GeneralSecurityException, IOException, OperatorCreationException {
- if (!keystore.isKeyEntry(keyAlias)) {
- KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
- keyPairGenerator.initialize(new RSAKeyGenParameterSpec(keySize, RSAKeyGenParameterSpec.F4), RandomSingleton.getInstance());
- KeyPair pair = keyPairGenerator.generateKeyPair();
-
- Date notBefore = new Date();
- Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
- cal.add(Calendar.MONTH, expiryInMonths);
- Date notAfter = cal.getTime();
- KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
-
- X509Certificate x509 = generateCertificate(pair.getPublic(), notBefore, notAfter, pair.getPrivate(), keyUsage);
- keystore.setKeyEntry(keyAlias, pair.getPrivate(), keyPass.toCharArray(), new Certificate[]{x509});
- return new KeyCertPair(pair.getPrivate(), new X509Certificate[]{x509});
- } else {
- return new KeyCertPair(getKey(keyAlias, keyPass), keystore.getCertificateChain(keyAlias));
- }
- }
-
- public KeyCertPair getKeyPair(String keyAlias, String keyPass) throws GeneralSecurityException {
- return new KeyCertPair(getKey(keyAlias, keyPass), keystore.getCertificateChain(keyAlias));
- }
-
- public KeyCertPair getKeyPair(int index, String keyPass) throws GeneralSecurityException {
- Map.Entry<String, PrivateKey> me = getKeyByIndex(index, keyPass);
- return me != null ? getKeyPair(me.getKey(), keyPass) : null;
- }
-
- public PrivateKey getKey(String keyAlias, String keyPass) throws GeneralSecurityException {
- return (PrivateKey)keystore.getKey(keyAlias, keyPass.toCharArray());
- }
-
- public PrivateKey getKey(int index, String keyPass) throws GeneralSecurityException {
- Map.Entry<String, PrivateKey> me = getKeyByIndex(index, keyPass);
- return me != null ? me.getValue() : null;
- }
-
- public X509Certificate getFirstX509(String alias) throws KeyStoreException {
- return (X509Certificate)keystore.getCertificate(alias);
- }
-
- private Map.Entry<String,PrivateKey> getKeyByIndex(int index, String keyPass) throws GeneralSecurityException {
- for (String a : Collections.list(keystore.aliases())) {
- try {
- PrivateKey pk = (PrivateKey) keystore.getKey(a, keyPass.toCharArray());
- if (pk != null) {
- return new AbstractMap.SimpleEntry<>(a, pk);
- }
- } catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException e) {
- break;
- }
- }
- return null;
- }
-
- public void save(File storeFile, String storePass) throws IOException, GeneralSecurityException {
- try (FileOutputStream fos = new FileOutputStream(storeFile)) {
- keystore.store(fos, storePass.toCharArray());
- }
- }
-
- public X509CRL generateCrl(KeyCertPair certPair)
- throws GeneralSecurityException, IOException, OperatorCreationException {
-
- PrivateKey issuerPrivateKey = certPair.getKey();
- X509Certificate issuer = certPair.getX509();
-
- X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded());
- X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date());
- crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000));
- JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC");
-
- CRLNumber crlNumber = new CRLNumber(new BigInteger("1234"));
-
- crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber);
- X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey));
- return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl);
- }
-
-
-
- private static X509Certificate generateCertificate(PublicKey subjectPublicKey,
- Date notBefore, Date notAfter,
- PrivateKey issuerPrivateKey,
- KeyUsage keyUsage)
- throws IOException, OperatorCreationException, CertificateException {
- final String signatureAlgorithm = "SHA1withRSA";
- final String subjectDn = "CN=Test";
- X500Name issuerName = new X500Name(subjectDn);
-
- RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey;
- RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
-
- SubjectPublicKeyInfo subjectPublicKeyInfo =
- SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec);
-
- DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
- .setProvider("BC").build().get(CertificateID.HASH_SHA1);
-
- X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
- issuerName
- , new BigInteger(128, RandomSingleton.getInstance())
- , notBefore
- , notAfter
- , new X500Name(subjectDn)
- , subjectPublicKeyInfo
- );
-
- X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc);
- SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo);
- AuthorityKeyIdentifier autKeyId = exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
-
- certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
- certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
-
- BasicConstraints bc = new BasicConstraints(0);
- certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
-
- if (null != keyUsage) {
- certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
- }
-
- JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
- signerBuilder.setProvider("BC");
-
- X509CertificateHolder certHolder =
- certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
-
- return new JcaX509CertificateConverter().getCertificate(certHolder);
- }
-
- public OCSPResp createOcspResp(KeyCertPair certPair, long nonceTimeinMillis)
- throws OperatorCreationException, OCSPException, CertificateEncodingException, IOException {
- X509Certificate certificate = certPair.getX509();
- X509Certificate issuerCertificate = certPair.getX509();
- X509Certificate ocspResponderCertificate = certPair.getX509();
- PrivateKey ocspResponderPrivateKey = certPair.getKey();
-
-
- DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
- .setProvider("BC").build().get(CertificateID.HASH_SHA1);
- X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded());
- CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber());
-
- // request
- //create a nonce to avoid replay attack
- BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis);
- DEROctetString nonceDer = new DEROctetString(nonce.toByteArray());
- Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer);
- Extensions exts = new Extensions(ext);
-
- OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
- ocspReqBuilder.addRequest(certId);
- ocspReqBuilder.setRequestExtensions(exts);
- OCSPReq ocspReq = ocspReqBuilder.build();
-
-
- SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo
- (CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded());
-
- BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc);
- basicOCSPRespBuilder.setResponseExtensions(exts);
-
- // request processing
- Req[] requestList = ocspReq.getRequestList();
- for (Req ocspRequest : requestList) {
- CertificateID certificateID = ocspRequest.getCertID();
- CertificateStatus certificateStatus = CertificateStatus.GOOD;
- basicOCSPRespBuilder.addResponse(certificateID, certificateStatus);
- }
-
- // basic response generation
- X509CertificateHolder[] chain = null;
- if (!ocspResponderCertificate.equals(issuerCertificate)) {
- // TODO: HorribleProxy can't convert array input params yet
- chain = new X509CertificateHolder[] {
- new X509CertificateHolder(ocspResponderCertificate.getEncoded()),
- issuerHolder
- };
- }
-
- ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA")
- .setProvider("BC").build(ocspResponderPrivateKey);
- BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis));
-
-
- OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder();
-
- return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp);
- }
-
- public void importX509(File file) throws CertificateException, KeyStoreException, IOException {
- try (InputStream is = new FileInputStream(file)) {
- X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
- keystore.setCertificateEntry(cert.getSubjectX500Principal().getName(), cert);
- }
- }
-
- public void importKeystore(File file, String storePass, String keyPass, Function<String,String> otherKeyPass) throws GeneralSecurityException, IOException {
- DummyKeystore dk = new DummyKeystore(file, storePass);
-
- Map<String,X509Certificate> myCerts = new HashMap<>();
- for (String a : Collections.list(keystore.aliases())) {
- Certificate[] chain = keystore.getCertificateChain(a);
- if (chain == null) {
- Certificate cert = keystore.getCertificate(a);
- if (cert == null) {
- continue;
- }
- chain = new Certificate[]{cert};
- }
- Arrays.stream(chain)
- .map(X509Certificate.class::cast)
- .filter(c -> !myCerts.containsKey(c.getSubjectX500Principal().getName()))
- .forEach(c -> myCerts.put(c.getSubjectX500Principal().getName(), c));
- }
-
- for (String a : Collections.list(dk.keystore.aliases())) {
- KeyCertPair keyPair = dk.getKeyPair(a, otherKeyPass.apply(a));
- ArrayList<X509Certificate> chain = new ArrayList<>(keyPair.getX509Chain());
- Set<String> names = chain.stream().map(X509Certificate::getSubjectX500Principal).map(X500Principal::getName).collect(Collectors.toSet());
- X509Certificate last = chain.get(chain.size() - 1);
- do {
- String issuer = last.getIssuerX500Principal().getName();
- X509Certificate parent = myCerts.get(issuer);
- if (names.contains(issuer) || parent == null) {
- break;
- } else {
- chain.add(parent);
- names.add(issuer);
- }
- last = parent;
- } while (true);
-
- keystore.setKeyEntry(a, keyPair.getKey(), keyPass.toCharArray(), chain.toArray(new X509Certificate[0]));
- }
- }
- }
|