diff --git a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/pom.xml b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/pom.xml index f36866903200899673024fa6026157c1e4c5ea2d..fa122c3c976c0622f41c6cea0ac4c3d96e68ccca 100644 --- a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/pom.xml +++ b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/pom.xml @@ -33,7 +33,6 @@ <properties> <!-- Name to display by the Extension Manager --> <xwiki.extension.name>Script Service for the Cryptographic API</xwiki.extension.name> - <checkstyle.suppressions.location>${basedir}/src/checkstyle/checkstyle-suppressions.xml</checkstyle.suppressions.location> <xwiki.jacoco.instructionRatio>0.00</xwiki.jacoco.instructionRatio> </properties> <dependencies> diff --git a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/checkstyle/checkstyle-suppressions.xml b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/checkstyle/checkstyle-suppressions.xml deleted file mode 100644 index f937ace71cb699faca550f78a1baf7d39afa498c..0000000000000000000000000000000000000000 --- a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/checkstyle/checkstyle-suppressions.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- - ~ See the NOTICE file distributed with this work for additional - ~ information regarding copyright ownership. - ~ - ~ This is free software; you can redistribute it and/or modify it - ~ under the terms of the GNU Lesser General Public License as - ~ published by the Free Software Foundation; either version 2.1 of - ~ the License, or (at your option) any later version. - ~ - ~ This software is distributed in the hope that it will be useful, - ~ but WITHOUT ANY WARRANTY; without even the implied warranty of - ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - ~ Lesser General Public License for more details. - ~ - ~ You should have received a copy of the GNU Lesser General Public - ~ License along with this software; if not, write to the Free - ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org. - --> - -<!DOCTYPE suppressions PUBLIC - "-//Puppy Crawl//DTD Suppressions 1.0//EN" - "http://www.puppycrawl.com/dtds/suppressions_1_0.dtd"> - -<suppressions> - <suppress checks="FanOutComplexity" files="RSACryptoScriptService.java"/> -</suppressions> diff --git a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/java/org/xwiki/crypto/internal/RSACryptoHelper.java b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/java/org/xwiki/crypto/internal/RSACryptoHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..6f4c071b04dec59f758c12fa65fdeb1ac1423b1a --- /dev/null +++ b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/java/org/xwiki/crypto/internal/RSACryptoHelper.java @@ -0,0 +1,210 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.xwiki.crypto.internal; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Collection; +import java.util.Date; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Provider; +import javax.inject.Singleton; + +import org.xwiki.component.annotation.Component; +import org.xwiki.crypto.params.cipher.asymmetric.AsymmetricKeyPair; +import org.xwiki.crypto.params.cipher.asymmetric.PublicKeyParameters; +import org.xwiki.crypto.pkix.CertificateGeneratorFactory; +import org.xwiki.crypto.pkix.CertifyingSigner; +import org.xwiki.crypto.pkix.X509ExtensionBuilder; +import org.xwiki.crypto.pkix.params.CertifiedKeyPair; +import org.xwiki.crypto.pkix.params.CertifiedPublicKey; +import org.xwiki.crypto.pkix.params.x509certificate.DistinguishedName; +import org.xwiki.crypto.pkix.params.x509certificate.X509CertificateGenerationParameters; +import org.xwiki.crypto.pkix.params.x509certificate.X509CertificateParameters; +import org.xwiki.crypto.pkix.params.x509certificate.X509CertifiedPublicKey; +import org.xwiki.crypto.pkix.params.x509certificate.extension.ExtendedKeyUsages; +import org.xwiki.crypto.pkix.params.x509certificate.extension.KeyUsage; +import org.xwiki.crypto.pkix.params.x509certificate.extension.X509DnsName; +import org.xwiki.crypto.pkix.params.x509certificate.extension.X509GeneralName; +import org.xwiki.crypto.pkix.params.x509certificate.extension.X509IpAddress; +import org.xwiki.crypto.pkix.params.x509certificate.extension.X509Rfc822Name; +import org.xwiki.crypto.signer.SignerFactory; + +/** + * Helper component for operations provided in {@link org.xwiki.crypto.script.RSACryptoScriptService}. + * + * @version $Id$ + * @since 16.8.0RC1 + */ +@Component(roles = RSACryptoHelper.class) +@Singleton +public class RSACryptoHelper +{ + @Inject + private Provider<X509ExtensionBuilder> extensionBuilder; + + @Inject + @Named("X509") + private CertificateGeneratorFactory certificateGeneratorFactory; + + @Inject + @Named("SHA256withRSAEncryption") + private SignerFactory signerFactory; + + /** + * Create an end entity certificate. By default, the key can be used for encryption and signing. If the end entity + * contains some alternate subject names of type X509Rfc822Name a extended email protection usage is added. If the + * end entity contains some alternate subject names of type X509DnsName or X509IpAddress extended server and client + * authentication usages are added. + * + * @param issuer the keypair for issuing the certificate + * @param publicKey the public key to certify + * @param dn the distinguished name for the new the certificate. + * @param validity the validity of the certificate from now in days. + * @param subjectAltName the alternative names for the certificate + * @return a certified public key. + * @throws IOException in case on error while reading the public key. + * @throws GeneralSecurityException in case of error. + */ + public CertifiedPublicKey issueCertificate(CertifiedKeyPair issuer, PublicKeyParameters publicKey, + String dn, int validity, List<X509GeneralName> subjectAltName) throws IOException, GeneralSecurityException + { + X509CertificateParameters params; + X509ExtensionBuilder builder = extensionBuilder.get().addKeyUsage(EnumSet.of(KeyUsage.digitalSignature, + KeyUsage.dataEncipherment)); + + if (subjectAltName != null) { + params = new X509CertificateParameters( + extensionBuilder.get().addSubjectAltName(false, subjectAltName.toArray(new X509GeneralName[]{})) + .build()); + Set<String> extUsage = new HashSet<>(); + for (X509GeneralName genName : subjectAltName) { + if (genName instanceof X509Rfc822Name) { + extUsage.add(ExtendedKeyUsages.EMAIL_PROTECTION); + } else if (genName instanceof X509DnsName || genName instanceof X509IpAddress) { + extUsage.add(ExtendedKeyUsages.SERVER_AUTH); + extUsage.add(ExtendedKeyUsages.CLIENT_AUTH); + } + builder.addExtendedKeyUsage(false, new ExtendedKeyUsages(extUsage)); + } + } else { + params = new X509CertificateParameters(); + } + + + return certificateGeneratorFactory.getInstance( + CertifyingSigner.getInstance(true, issuer, signerFactory), + new X509CertificateGenerationParameters(validity, builder.build())) + .generate(new DistinguishedName(dn), publicKey, params); + } + + /** + * Create a self-signed certificate for a Root CA. + * + * @param keyPair the keypair to issue the certificate for and used for signing it. + * @param dn the distinguished name for the new the certificate. + * @param validity the validity of the certificate from now in days. + * @return a certified public key. + * @throws IOException in case on error while reading the public key. + * @throws GeneralSecurityException in case of error. + */ + public CertifiedKeyPair issueRootCACertificate(AsymmetricKeyPair keyPair, String dn, int validity) + throws IOException, GeneralSecurityException + { + return new CertifiedKeyPair( + keyPair.getPrivate(), + certificateGeneratorFactory.getInstance(signerFactory.getInstance(true, keyPair.getPrivate()), + new X509CertificateGenerationParameters( + validity, + extensionBuilder.get().addBasicConstraints(true) + .addKeyUsage(true, EnumSet.of(KeyUsage.keyCertSign, + KeyUsage.cRLSign)) + .build())) + .generate(new DistinguishedName(dn), keyPair.getPublic(), + new X509CertificateParameters()) + ); + } + + /** + * Create an intermediate CA certificate. + * + * @param issuer the certified keypair for issuing the certificate + * @param publicKey the public key to certify + * @param dn the distinguished name for the new the certificate. + * @param validity the validity of the certificate from now in days. + * @return a certified public key. + * @throws IOException in case on error while reading the public key. + * @throws GeneralSecurityException in case of error. + */ + public CertifiedPublicKey issueIntermediateCertificate(CertifiedKeyPair issuer, PublicKeyParameters publicKey, + String dn, int validity) + throws IOException, GeneralSecurityException + { + return certificateGeneratorFactory.getInstance( + CertifyingSigner.getInstance(true, issuer, signerFactory), + new X509CertificateGenerationParameters( + validity, + extensionBuilder.get().addBasicConstraints(0) + .addKeyUsage(EnumSet.of(KeyUsage.keyCertSign, + KeyUsage.cRLSign)) + .build())) + .generate(new DistinguishedName(dn), publicKey, + new X509CertificateParameters()); + } + + /** + * Check that an X509 certificate chain is complete and is valid on a given date. + * + * @param chain the ordered chain of certificate starting from root CA. + * @param date the date to check the validity for, or null to check for now. + * @return true if the chain is a X509 certificate chain complete and valid on the given date. + */ + public boolean checkX509CertificateChainValidity(Collection<CertifiedPublicKey> chain, Date date) + { + if (chain == null || chain.isEmpty()) { + return false; + } + + Date checkDate = (date != null) ? date : new Date(); + boolean rootExpected = true; + for (CertifiedPublicKey cert : chain) { + if (!(cert instanceof X509CertifiedPublicKey)) { + return false; + } + if (rootExpected) { + if (!((X509CertifiedPublicKey) cert).isRootCA()) { + return false; + } + rootExpected = false; + } + if (!((X509CertifiedPublicKey) cert).isValidOn(checkDate)) { + return false; + } + } + return true; + } +} diff --git a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/java/org/xwiki/crypto/script/RSACryptoScriptService.java b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/java/org/xwiki/crypto/script/RSACryptoScriptService.java index 879616f3005d99e45782984188ca666b49d4c5b5..a8a05b989fea08151c5d458d624f396f6db6c028 100644 --- a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/java/org/xwiki/crypto/script/RSACryptoScriptService.java +++ b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/java/org/xwiki/crypto/script/RSACryptoScriptService.java @@ -24,39 +24,27 @@ import java.security.GeneralSecurityException; import java.util.Collection; import java.util.Date; -import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; -import javax.inject.Provider; import javax.inject.Singleton; import org.xwiki.component.annotation.Component; import org.xwiki.crypto.KeyPairGenerator; +import org.xwiki.crypto.internal.RSACryptoHelper; import org.xwiki.crypto.params.cipher.asymmetric.AsymmetricKeyPair; import org.xwiki.crypto.params.cipher.asymmetric.PrivateKeyParameters; import org.xwiki.crypto.params.cipher.asymmetric.PublicKeyParameters; import org.xwiki.crypto.params.generator.asymmetric.RSAKeyGenerationParameters; import org.xwiki.crypto.pkix.CertificateChainBuilder; -import org.xwiki.crypto.pkix.CertificateGeneratorFactory; import org.xwiki.crypto.pkix.CertificateProvider; import org.xwiki.crypto.pkix.CertifyingSigner; -import org.xwiki.crypto.pkix.X509ExtensionBuilder; import org.xwiki.crypto.pkix.params.CertifiedKeyPair; import org.xwiki.crypto.pkix.params.CertifiedPublicKey; -import org.xwiki.crypto.pkix.params.x509certificate.DistinguishedName; -import org.xwiki.crypto.pkix.params.x509certificate.X509CertificateGenerationParameters; -import org.xwiki.crypto.pkix.params.x509certificate.X509CertificateParameters; -import org.xwiki.crypto.pkix.params.x509certificate.X509CertifiedPublicKey; -import org.xwiki.crypto.pkix.params.x509certificate.extension.ExtendedKeyUsages; -import org.xwiki.crypto.pkix.params.x509certificate.extension.KeyUsage; -import org.xwiki.crypto.pkix.params.x509certificate.extension.X509DnsName; import org.xwiki.crypto.pkix.params.x509certificate.extension.X509GeneralName; -import org.xwiki.crypto.pkix.params.x509certificate.extension.X509IpAddress; -import org.xwiki.crypto.pkix.params.x509certificate.extension.X509Rfc822Name; import org.xwiki.crypto.signer.CMSSignedDataGenerator; import org.xwiki.crypto.signer.CMSSignedDataVerifier; import org.xwiki.crypto.signer.SignerFactory; @@ -86,15 +74,14 @@ public class RSACryptoScriptService implements ScriptService private KeyPairGenerator keyPairGenerator; @Inject - @Named("SHA256withRSAEncryption") - private SignerFactory signerFactory; + private CMSSignedDataVerifier cmsSignedDataVerifier; @Inject - private Provider<X509ExtensionBuilder> extensionBuilder; + private RSACryptoHelper rsaCryptoHelper; @Inject - @Named("X509") - private CertificateGeneratorFactory certificateGeneratorFactory; + @Named("SHA256withRSAEncryption") + private SignerFactory signerFactory; @Inject private CMSSignedDataGenerator cmsSignedDataGenerator; @@ -103,9 +90,6 @@ public class RSACryptoScriptService implements ScriptService @Named("X509") private CertificateChainBuilder certificateChainBuilder; - @Inject - private CMSSignedDataVerifier cmsSignedDataVerifier; - /** * Generate a new RSA key pair. * @@ -179,18 +163,7 @@ public CertifiedKeyPair createCertifiedKeyPair(PrivateKeyParameters privateKey, public CertifiedKeyPair issueRootCACertificate(AsymmetricKeyPair keyPair, String dn, int validity) throws IOException, GeneralSecurityException { - return new CertifiedKeyPair( - keyPair.getPrivate(), - certificateGeneratorFactory.getInstance(signerFactory.getInstance(true, keyPair.getPrivate()), - new X509CertificateGenerationParameters( - validity, - extensionBuilder.get().addBasicConstraints(true) - .addKeyUsage(true, EnumSet.of(KeyUsage.keyCertSign, - KeyUsage.cRLSign)) - .build())) - .generate(new DistinguishedName(dn), keyPair.getPublic(), - new X509CertificateParameters()) - ); + return this.rsaCryptoHelper.issueRootCACertificate(keyPair, dn, validity); } /** @@ -248,16 +221,7 @@ public CertifiedPublicKey issueIntermediateCertificate(CertifiedKeyPair issuer, String dn, int validity) throws IOException, GeneralSecurityException { - return certificateGeneratorFactory.getInstance( - CertifyingSigner.getInstance(true, issuer, signerFactory), - new X509CertificateGenerationParameters( - validity, - extensionBuilder.get().addBasicConstraints(0) - .addKeyUsage(EnumSet.of(KeyUsage.keyCertSign, - KeyUsage.cRLSign)) - .build())) - .generate(new DistinguishedName(dn), publicKey, - new X509CertificateParameters()); + return this.rsaCryptoHelper.issueIntermediateCertificate(issuer, publicKey, dn, validity); } /** @@ -319,33 +283,7 @@ public CertifiedPublicKey issueCertificate(PrivateKeyParameters privateKey, Cert public CertifiedPublicKey issueCertificate(CertifiedKeyPair issuer, PublicKeyParameters publicKey, String dn, int validity, List<X509GeneralName> subjectAltName) throws IOException, GeneralSecurityException { - X509CertificateParameters params; - X509ExtensionBuilder builder = extensionBuilder.get().addKeyUsage(EnumSet.of(KeyUsage.digitalSignature, - KeyUsage.dataEncipherment)); - - if (subjectAltName != null) { - params = new X509CertificateParameters( - extensionBuilder.get().addSubjectAltName(false, subjectAltName.toArray(new X509GeneralName[]{})) - .build()); - Set<String> extUsage = new HashSet<>(); - for (X509GeneralName genName : subjectAltName) { - if (genName instanceof X509Rfc822Name) { - extUsage.add(ExtendedKeyUsages.EMAIL_PROTECTION); - } else if (genName instanceof X509DnsName || genName instanceof X509IpAddress) { - extUsage.add(ExtendedKeyUsages.SERVER_AUTH); - extUsage.add(ExtendedKeyUsages.CLIENT_AUTH); - } - builder.addExtendedKeyUsage(false, new ExtendedKeyUsages(extUsage)); - } - } else { - params = new X509CertificateParameters(); - } - - - return certificateGeneratorFactory.getInstance( - CertifyingSigner.getInstance(true, issuer, signerFactory), - new X509CertificateGenerationParameters(validity, builder.build())) - .generate(new DistinguishedName(dn), publicKey, params); + return this.rsaCryptoHelper.issueCertificate(issuer, publicKey, dn, validity, subjectAltName); } /** @@ -526,26 +464,6 @@ public boolean checkX509CertificateChainValidity(Collection<CertifiedPublicKey> */ public boolean checkX509CertificateChainValidity(Collection<CertifiedPublicKey> chain, Date date) { - if (chain == null || chain.isEmpty()) { - return false; - } - - Date checkDate = (date != null) ? date : new Date(); - boolean rootExpected = true; - for (CertifiedPublicKey cert : chain) { - if (!(cert instanceof X509CertifiedPublicKey)) { - return false; - } - if (rootExpected) { - if (!((X509CertifiedPublicKey) cert).isRootCA()) { - return false; - } - rootExpected = false; - } - if (!((X509CertifiedPublicKey) cert).isValidOn(checkDate)) { - return false; - } - } - return true; + return this.rsaCryptoHelper.checkX509CertificateChainValidity(chain, date); } } diff --git a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/resources/META-INF/components.txt b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/resources/META-INF/components.txt index ecf99fda17448db8cd7b84dc40d336c8c6920e27..3eb87ecea33aa44a0a3ca0f3d2f949d7663f4aae 100644 --- a/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/resources/META-INF/components.txt +++ b/xwiki-platform-core/xwiki-platform-crypto/xwiki-platform-crypto-script/src/main/resources/META-INF/components.txt @@ -1,3 +1,4 @@ +org.xwiki.crypto.internal.RSACryptoHelper org.xwiki.crypto.script.CryptoScriptService org.xwiki.crypto.script.RSACryptoScriptService org.xwiki.crypto.script.StoreScriptService