Skip to content

Commit efc934d

Browse files
authored
BAEL-7411: sign certificate using bouncy castle (#17845)
1 parent 076885d commit efc934d

File tree

8 files changed

+245
-0
lines changed

8 files changed

+245
-0
lines changed

libraries-security-2/.gitignore

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
HELP.md
2+
target/
3+
!.mvn/wrapper/maven-wrapper.jar
4+
!**/src/main/**/target/
5+
!**/src/test/**/target/
6+
7+
### STS ###
8+
.apt_generated
9+
.classpath
10+
.factorypath
11+
.project
12+
.settings
13+
.springBeans
14+
.sts4-cache
15+
16+
### IntelliJ IDEA ###
17+
.idea
18+
*.iws
19+
*.iml
20+
*.ipr
21+
22+
### NetBeans ###
23+
/nbproject/private/
24+
/nbbuild/
25+
/dist/
26+
/nbdist/
27+
/.nb-gradle/
28+
build/
29+
!**/src/main/**/build/
30+
!**/src/test/**/build/
31+
32+
### VS Code ###
33+
.vscode/

libraries-security-2/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Security
2+
3+
This module contains articles about security libraries.
4+
5+
### Relevant Articles:
6+
7+

libraries-security-2/pom.xml

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<artifactId>libraries-security-2</artifactId>
7+
<packaging>jar</packaging>
8+
<name>libraries-security-2</name>
9+
10+
<parent>
11+
<groupId>com.baeldung</groupId>
12+
<artifactId>parent-boot-3</artifactId>
13+
<version>0.0.1-SNAPSHOT</version>
14+
<relativePath>../parent-boot-3</relativePath>
15+
</parent>
16+
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.springframework.boot</groupId>
20+
<artifactId>spring-boot-starter-web</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework</groupId>
28+
<artifactId>spring-web</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.bouncycastle</groupId>
32+
<artifactId>bcpkix-jdk18on</artifactId>
33+
<version>${bouncycastle.version}</version>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.springframework.security</groupId>
37+
<artifactId>spring-security-oauth2-authorization-server</artifactId>
38+
<version>1.2.1</version>
39+
</dependency>
40+
</dependencies>
41+
42+
<build>
43+
<plugins>
44+
<plugin>
45+
<groupId>org.apache.maven.plugins</groupId>
46+
<artifactId>maven-surefire-plugin</artifactId>
47+
<version>${maven-surefire-plugin.version}</version>
48+
<configuration>
49+
<argLine>
50+
--add-opens java.base/java.lang=ALL-UNNAMED
51+
</argLine>
52+
</configuration>
53+
</plugin>
54+
<plugin>
55+
<groupId>org.apache.maven.plugins</groupId>
56+
<artifactId>maven-compiler-plugin</artifactId>
57+
<configuration>
58+
<source>11</source>
59+
<target>11</target>
60+
</configuration>
61+
</plugin>
62+
</plugins>
63+
</build>
64+
65+
<properties>
66+
<bouncycastle.version>1.76</bouncycastle.version>
67+
</properties>
68+
69+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.baeldung.bouncycastle;
2+
3+
import org.springframework.boot.SpringApplication;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
6+
@SpringBootApplication
7+
public class LibrariesSecurityApplication {
8+
public static void main(String[] args) {
9+
SpringApplication.run(LibrariesSecurityApplication.class, args);
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.baeldung.bouncycastle;
2+
3+
import org.bouncycastle.asn1.x500.X500Name;
4+
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
5+
import org.bouncycastle.asn1.x509.Certificate;
6+
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
7+
import org.bouncycastle.cert.X509CertificateHolder;
8+
import org.bouncycastle.cert.X509v3CertificateBuilder;
9+
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
10+
import org.bouncycastle.crypto.util.PrivateKeyFactory;
11+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
12+
import org.bouncycastle.operator.ContentSigner;
13+
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
14+
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
15+
import org.bouncycastle.operator.OperatorCreationException;
16+
import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder;
17+
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
18+
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
19+
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
20+
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
21+
22+
import javax.security.auth.x500.X500Principal;
23+
import java.io.ByteArrayInputStream;
24+
import java.io.IOException;
25+
import java.io.InputStream;
26+
import java.math.BigInteger;
27+
import java.security.*;
28+
import java.security.cert.CertificateException;
29+
import java.security.cert.CertificateFactory;
30+
import java.security.cert.X509Certificate;
31+
import java.util.Date;
32+
33+
public class SignCSRBouncyCastle {
34+
35+
static {
36+
Security.addProvider(new BouncyCastleProvider());
37+
}
38+
39+
public X509Certificate signCSR(PKCS10CertificationRequest inputCSR, PrivateKey caPrivate, KeyPair pair) throws OperatorCreationException, CertificateException, NoSuchProviderException, IOException {
40+
AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder()
41+
.find("SHA1withRSA");
42+
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder()
43+
.find(sigAlgId);
44+
AsymmetricKeyParameter foo = PrivateKeyFactory.createKey(caPrivate
45+
.getEncoded());
46+
SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pair
47+
.getPublic().getEncoded());
48+
49+
X509v3CertificateBuilder myCertificateGenerator = new X509v3CertificateBuilder(
50+
new X500Name("CN=issuer"), new BigInteger("1"), new Date(
51+
System.currentTimeMillis()), new Date(
52+
System.currentTimeMillis() + 30L * 365 * 24 * 60 * 60
53+
* 1000), inputCSR.getSubject(), keyInfo);
54+
55+
ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId)
56+
.build(foo);
57+
58+
X509CertificateHolder holder = myCertificateGenerator.build(sigGen);
59+
Certificate eeX509CertificateStructure = holder.toASN1Structure();
60+
CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
61+
62+
InputStream is1 = new ByteArrayInputStream(eeX509CertificateStructure.getEncoded());
63+
X509Certificate theCert = (X509Certificate) cf.generateCertificate(is1);
64+
is1.close();
65+
return theCert;
66+
}
67+
68+
public static KeyPair generateRSAKeyPair() throws NoSuchAlgorithmException {
69+
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
70+
keyPairGenerator.initialize(2048);
71+
return keyPairGenerator.generateKeyPair();
72+
}
73+
74+
public static PKCS10CertificationRequest generateCSR(KeyPair pair) throws OperatorCreationException {
75+
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
76+
new X500Principal("CN=Requested Test Certificate"), pair.getPublic());
77+
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
78+
ContentSigner signer = csBuilder.build(pair.getPrivate());
79+
return p10Builder.build(signer);
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
spring.application.name=libraries-security-2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
4+
<encoder>
5+
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6+
</pattern>
7+
</encoder>
8+
</appender>
9+
10+
<root level="INFO">
11+
<appender-ref ref="STDOUT" />
12+
</root>
13+
</configuration>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.baeldung.bouncycastle;
2+
3+
import org.bouncycastle.operator.OperatorCreationException;
4+
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.io.IOException;
8+
import java.security.*;
9+
import java.security.cert.CertificateException;
10+
import java.security.cert.X509Certificate;
11+
12+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
13+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
14+
15+
public class SignCSRBouncyCastleUnitTest {
16+
17+
@Test
18+
public void givenCSR_whenSignWithBC_thenSuccess() throws NoSuchAlgorithmException, OperatorCreationException, CertificateException, IOException, NoSuchProviderException, SignatureException, InvalidKeyException {
19+
SignCSRBouncyCastle signCSRBouncyCastle = new SignCSRBouncyCastle();
20+
KeyPair pair = SignCSRBouncyCastle.generateRSAKeyPair();
21+
PKCS10CertificationRequest csr = SignCSRBouncyCastle.generateCSR(pair);
22+
KeyPair caPair = SignCSRBouncyCastle.generateRSAKeyPair();
23+
X509Certificate signedCert = signCSRBouncyCastle.signCSR(csr, caPair.getPrivate(), pair);
24+
25+
assertThat(signedCert).isNotNull();
26+
assertThat(signedCert.getSubjectDN().getName()).isEqualTo("CN=Requested Test Certificate");
27+
assertDoesNotThrow(() -> signedCert.verify(caPair.getPublic()));
28+
}
29+
30+
}

0 commit comments

Comments
 (0)