You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DummyKeystore.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. /* ====================================================================
  16. This product contains an ASLv2 licensed version of the OOXML signer
  17. package from the eID Applet project
  18. http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
  19. Copyright (C) 2008-2014 FedICT.
  20. ================================================================= */
  21. package org.apache.poi.poifs.crypt.dsig;
  22. import java.io.BufferedReader;
  23. import java.io.ByteArrayInputStream;
  24. import java.io.File;
  25. import java.io.FileInputStream;
  26. import java.io.FileOutputStream;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.InputStreamReader;
  30. import java.math.BigInteger;
  31. import java.nio.charset.StandardCharsets;
  32. import java.security.GeneralSecurityException;
  33. import java.security.KeyPair;
  34. import java.security.KeyPairGenerator;
  35. import java.security.KeyStore;
  36. import java.security.KeyStoreException;
  37. import java.security.PrivateKey;
  38. import java.security.PublicKey;
  39. import java.security.cert.Certificate;
  40. import java.security.cert.CertificateEncodingException;
  41. import java.security.cert.CertificateException;
  42. import java.security.cert.X509CRL;
  43. import java.security.cert.X509Certificate;
  44. import java.security.interfaces.RSAPublicKey;
  45. import java.security.spec.RSAKeyGenParameterSpec;
  46. import java.util.Calendar;
  47. import java.util.Date;
  48. import java.util.List;
  49. import java.util.stream.Collectors;
  50. import java.util.stream.Stream;
  51. import org.apache.poi.poifs.crypt.CryptoFunctions;
  52. import org.apache.poi.poifs.storage.RawDataUtil;
  53. import org.apache.poi.util.LocaleUtil;
  54. import org.apache.poi.util.RandomSingleton;
  55. import org.bouncycastle.asn1.DEROctetString;
  56. import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
  57. import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
  58. import org.bouncycastle.asn1.x500.X500Name;
  59. import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
  60. import org.bouncycastle.asn1.x509.BasicConstraints;
  61. import org.bouncycastle.asn1.x509.CRLNumber;
  62. import org.bouncycastle.asn1.x509.Extension;
  63. import org.bouncycastle.asn1.x509.Extensions;
  64. import org.bouncycastle.asn1.x509.KeyUsage;
  65. import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
  66. import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
  67. import org.bouncycastle.cert.X509CRLHolder;
  68. import org.bouncycastle.cert.X509CertificateHolder;
  69. import org.bouncycastle.cert.X509ExtensionUtils;
  70. import org.bouncycastle.cert.X509v2CRLBuilder;
  71. import org.bouncycastle.cert.X509v3CertificateBuilder;
  72. import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
  73. import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
  74. import org.bouncycastle.cert.ocsp.BasicOCSPResp;
  75. import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
  76. import org.bouncycastle.cert.ocsp.CertificateID;
  77. import org.bouncycastle.cert.ocsp.CertificateStatus;
  78. import org.bouncycastle.cert.ocsp.OCSPException;
  79. import org.bouncycastle.cert.ocsp.OCSPReq;
  80. import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
  81. import org.bouncycastle.cert.ocsp.OCSPResp;
  82. import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
  83. import org.bouncycastle.cert.ocsp.Req;
  84. import org.bouncycastle.crypto.params.RSAKeyParameters;
  85. import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
  86. import org.bouncycastle.openssl.PEMParser;
  87. import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
  88. import org.bouncycastle.operator.ContentSigner;
  89. import org.bouncycastle.operator.DigestCalculator;
  90. import org.bouncycastle.operator.OperatorCreationException;
  91. import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
  92. import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
  93. public class DummyKeystore {
  94. public static class KeyCertPair {
  95. private final PrivateKey key;
  96. private final List<X509Certificate> x509chain;
  97. public KeyCertPair(PrivateKey key, Certificate[] x509chain) {
  98. this.key = key;
  99. this.x509chain = Stream.of(x509chain).map(X509Certificate.class::cast).collect(Collectors.toList());
  100. }
  101. public PrivateKey getKey() {
  102. return key;
  103. }
  104. public X509Certificate getX509() {
  105. return x509chain.get(0);
  106. }
  107. public List<X509Certificate> getX509Chain() {
  108. return x509chain;
  109. }
  110. }
  111. private static final String DUMMY_ALIAS = "Test";
  112. private static final String DUMMY_PASS = "test";
  113. private final KeyStore keystore;
  114. public DummyKeystore(String storePass) throws GeneralSecurityException, IOException {
  115. this((File)null, storePass);
  116. }
  117. public DummyKeystore(File storeFile, String storePass) throws GeneralSecurityException, IOException {
  118. CryptoFunctions.registerBouncyCastle();
  119. keystore = KeyStore.getInstance("PKCS12");
  120. try (InputStream fis = storeFile != null && storeFile.exists() ? new FileInputStream(storeFile) : null) {
  121. keystore.load(fis, storePass.toCharArray());
  122. }
  123. }
  124. public DummyKeystore(String pfxInput, String storePass) throws GeneralSecurityException, IOException {
  125. CryptoFunctions.registerBouncyCastle();
  126. keystore = KeyStore.getInstance("PKCS12");
  127. try (InputStream fis = new ByteArrayInputStream(RawDataUtil.decompress(pfxInput))) {
  128. keystore.load(fis, storePass.toCharArray());
  129. }
  130. }
  131. /**
  132. * Create dummy key
  133. * @return the alias of the dummy key
  134. */
  135. public KeyCertPair createDummyKey() throws GeneralSecurityException, IOException, OperatorCreationException {
  136. return addEntry(DUMMY_ALIAS, DUMMY_PASS, 2048, 24);
  137. }
  138. public KeyCertPair addEntryFromPEM(File pemFile, String keyPass) throws IOException, CertificateException, KeyStoreException {
  139. PrivateKey key = null;
  140. X509Certificate x509 = null;
  141. try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(pemFile), StandardCharsets.ISO_8859_1))) {
  142. PEMParser parser = new PEMParser(br);
  143. for (Object obj; (obj = parser.readObject()) != null; ) {
  144. if (obj instanceof PrivateKeyInfo) {
  145. key = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey((PrivateKeyInfo)obj);
  146. } else if (obj instanceof X509CertificateHolder) {
  147. x509 = new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)obj);
  148. }
  149. }
  150. }
  151. if (key == null || x509 == null) {
  152. throw new IOException("Please add private key and certificate in the PEM file.");
  153. }
  154. String alias = x509.getSubjectX500Principal().getName();
  155. keystore.setKeyEntry(alias, key, keyPass.toCharArray(), new Certificate[]{x509});
  156. return new KeyCertPair(key, new Certificate[]{x509});
  157. }
  158. /**
  159. * Add an entry with password, keySize and expiry values. Ignore if alias is already in keystore
  160. * @param keySize multiple of 1024, e.g. 1024, 2048
  161. */
  162. public KeyCertPair addEntry(String keyAlias, String keyPass, int keySize, int expiryInMonths) throws GeneralSecurityException, IOException, OperatorCreationException {
  163. if (!keystore.isKeyEntry(keyAlias)) {
  164. KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
  165. keyPairGenerator.initialize(new RSAKeyGenParameterSpec(keySize, RSAKeyGenParameterSpec.F4), RandomSingleton.getInstance());
  166. KeyPair pair = keyPairGenerator.generateKeyPair();
  167. Date notBefore = new Date();
  168. Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
  169. cal.add(Calendar.MONTH, expiryInMonths);
  170. Date notAfter = cal.getTime();
  171. KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
  172. X509Certificate x509 = generateCertificate(pair.getPublic(), notBefore, notAfter, pair.getPrivate(), keyUsage);
  173. keystore.setKeyEntry(keyAlias, pair.getPrivate(), keyPass.toCharArray(), new Certificate[]{x509});
  174. return new KeyCertPair(pair.getPrivate(), new X509Certificate[]{x509});
  175. } else {
  176. return new KeyCertPair(getKey(keyAlias, keyPass), keystore.getCertificateChain(keyAlias));
  177. }
  178. }
  179. public KeyCertPair getKeyPair(String keyAlias, String keyPass) throws GeneralSecurityException {
  180. return new KeyCertPair(getKey(keyAlias, keyPass), keystore.getCertificateChain(keyAlias));
  181. }
  182. public PrivateKey getKey(String keyAlias, String keyPass) throws GeneralSecurityException {
  183. return (PrivateKey)keystore.getKey(keyAlias, keyPass.toCharArray());
  184. }
  185. public X509Certificate getFirstX509(String alias) throws KeyStoreException {
  186. return (X509Certificate)keystore.getCertificate(alias);
  187. }
  188. public void save(File storeFile, String storePass) throws IOException, GeneralSecurityException {
  189. try (FileOutputStream fos = new FileOutputStream(storeFile)) {
  190. keystore.store(fos, storePass.toCharArray());
  191. }
  192. }
  193. public X509CRL generateCrl(KeyCertPair certPair)
  194. throws GeneralSecurityException, IOException, OperatorCreationException {
  195. PrivateKey issuerPrivateKey = certPair.getKey();
  196. X509Certificate issuer = certPair.getX509();
  197. X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded());
  198. X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date());
  199. crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000));
  200. JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC");
  201. CRLNumber crlNumber = new CRLNumber(new BigInteger("1234"));
  202. crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber);
  203. X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey));
  204. return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl);
  205. }
  206. private static X509Certificate generateCertificate(PublicKey subjectPublicKey,
  207. Date notBefore, Date notAfter,
  208. PrivateKey issuerPrivateKey,
  209. KeyUsage keyUsage)
  210. throws IOException, OperatorCreationException, CertificateException {
  211. final String signatureAlgorithm = "SHA1withRSA";
  212. final String subjectDn = "CN=Test";
  213. X500Name issuerName = new X500Name(subjectDn);
  214. RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey;
  215. RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
  216. SubjectPublicKeyInfo subjectPublicKeyInfo =
  217. SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec);
  218. DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
  219. .setProvider("BC").build().get(CertificateID.HASH_SHA1);
  220. X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
  221. issuerName
  222. , new BigInteger(128, RandomSingleton.getInstance())
  223. , notBefore
  224. , notAfter
  225. , new X500Name(subjectDn)
  226. , subjectPublicKeyInfo
  227. );
  228. X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc);
  229. SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo);
  230. AuthorityKeyIdentifier autKeyId = exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
  231. certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
  232. certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
  233. BasicConstraints bc = new BasicConstraints(0);
  234. certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
  235. if (null != keyUsage) {
  236. certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
  237. }
  238. JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
  239. signerBuilder.setProvider("BC");
  240. X509CertificateHolder certHolder =
  241. certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
  242. return new JcaX509CertificateConverter().getCertificate(certHolder);
  243. }
  244. public OCSPResp createOcspResp(KeyCertPair certPair, long nonceTimeinMillis)
  245. throws OperatorCreationException, OCSPException, CertificateEncodingException, IOException {
  246. X509Certificate certificate = certPair.getX509();
  247. X509Certificate issuerCertificate = certPair.getX509();
  248. X509Certificate ocspResponderCertificate = certPair.getX509();
  249. PrivateKey ocspResponderPrivateKey = certPair.getKey();
  250. DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
  251. .setProvider("BC").build().get(CertificateID.HASH_SHA1);
  252. X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded());
  253. CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber());
  254. // request
  255. //create a nonce to avoid replay attack
  256. BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis);
  257. DEROctetString nonceDer = new DEROctetString(nonce.toByteArray());
  258. Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer);
  259. Extensions exts = new Extensions(ext);
  260. OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
  261. ocspReqBuilder.addRequest(certId);
  262. ocspReqBuilder.setRequestExtensions(exts);
  263. OCSPReq ocspReq = ocspReqBuilder.build();
  264. SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo
  265. (CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded());
  266. BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc);
  267. basicOCSPRespBuilder.setResponseExtensions(exts);
  268. // request processing
  269. Req[] requestList = ocspReq.getRequestList();
  270. for (Req ocspRequest : requestList) {
  271. CertificateID certificateID = ocspRequest.getCertID();
  272. CertificateStatus certificateStatus = CertificateStatus.GOOD;
  273. basicOCSPRespBuilder.addResponse(certificateID, certificateStatus);
  274. }
  275. // basic response generation
  276. X509CertificateHolder[] chain = null;
  277. if (!ocspResponderCertificate.equals(issuerCertificate)) {
  278. // TODO: HorribleProxy can't convert array input params yet
  279. chain = new X509CertificateHolder[] {
  280. new X509CertificateHolder(ocspResponderCertificate.getEncoded()),
  281. issuerHolder
  282. };
  283. }
  284. ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA")
  285. .setProvider("BC").build(ocspResponderPrivateKey);
  286. BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis));
  287. OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder();
  288. return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp);
  289. }
  290. }