• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ebourg / jsign / #416

19 Jul 2026 11:19AM UTC coverage: 80.814% (-0.006%) from 80.82%
#416

push

ebourg
Fix 'missing provider' exception for KeyStoreTypes which do not have specific provider

e.g. using PKCS#12 keystore for signing

3 of 4 new or added lines in 1 file covered. (75.0%)

5265 of 6515 relevant lines covered (80.81%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.53
/jsign-crypto/src/main/java/net/jsign/jca/JsignJcaProvider.java
1
/*
2
 * Copyright 2023 Emmanuel Bourg
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package net.jsign.jca;
18

19
import java.io.InputStream;
20
import java.security.AccessController;
21
import java.security.InvalidKeyException;
22
import java.security.InvalidParameterException;
23
import java.security.Key;
24
import java.security.KeyStore;
25
import java.security.KeyStoreException;
26
import java.security.NoSuchAlgorithmException;
27
import java.security.PrivateKey;
28
import java.security.PrivilegedAction;
29
import java.security.Provider;
30
import java.security.Signature;
31
import java.security.SignatureException;
32
import java.security.UnrecoverableKeyException;
33
import java.security.cert.Certificate;
34
import java.util.Enumeration;
35

36
import net.jsign.DigestAlgorithm;
37
import net.jsign.KeyStoreBuilder;
38
import net.jsign.KeyStoreType;
39

40
/**
41
 * JCA provider using a Jsign keystore and compatible with jarsigner and apksigner.
42
 *
43
 * <p>The provider must be configured with the keystore parameter (the value depends on the keystore type).
44
 * The type of the keystore is one of the names from the {@link KeyStoreType} enum.</p>
45
 *
46
 * <p>Example:</p>
47
 * <pre>
48
 * Provider provider = new JsignJcaProvider();
49
 * provider.configure(vaultname)
50
 * KeyStore keystore = KeyStore.getInstance(AZUREKEYVAULT.name(), provider);
51
 * keystore.load(null, accessToken);
52
 *
53
 * PrivateKey key = (PrivateKey) keystore.getKey(alias, null);
54
 *
55
 * Signature signature = Signature.getInstance("SHA256withRSA", provider);
56
 * signature.initSign(key);
57
 * signature.update("Lorem ipsum dolor sit amet".getBytes());
58
 * signature.sign();
59
 * </pre>
60
 *
61
 * @since 6.0
62
 */
63
public class JsignJcaProvider extends Provider {
64

65
    private String keystore;
66

67
    public JsignJcaProvider() {
68
        super("Jsign", 1.0, "Jsign security provider");
1✔
69

70
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
1✔
71
            for (KeyStoreType type : KeyStoreType.values()) {
1✔
72
                putService(new ProviderService(this, "KeyStore", type.name(), JsignJcaKeyStore.class.getName(), () -> new JsignJcaKeyStore(type, keystore)));
1✔
73
            }
74
            for (String alg : new String[]{"RSA", "ECDSA"}) {
1✔
75
                for (DigestAlgorithm digest : DigestAlgorithm.values()) {
1✔
76
                    if (digest != DigestAlgorithm.MD5) {
1✔
77
                        String algorithm = digest.name() + "with" + alg;
1✔
78
                        putService(new ProviderService(this, "Signature", algorithm, JsignJcaSignature.class.getName(), () -> new JsignJcaSignature(algorithm)));
1✔
79
                    }
80
                }
81
            }
82
            return null;
1✔
83
        });
84
    }
1✔
85

86
    public JsignJcaProvider(String configArg) {
87
        this();
1✔
88
        configure(configArg);
1✔
89
    }
1✔
90

91
    public Provider configure(String configArg) throws InvalidParameterException {
92
        this.keystore = configArg;
1✔
93

94
        return this;
1✔
95
    }
96

97
    static class JsignJcaKeyStore extends AbstractKeyStoreSpi {
98

99
        private KeyStoreBuilder builder = new KeyStoreBuilder();
1✔
100
        private KeyStore keystore;
101

102
        public JsignJcaKeyStore(KeyStoreType type, String keystore) {
1✔
103
            builder.storetype(type);
1✔
104
            builder.keystore(keystore);
1✔
105
            builder.certfile("");
1✔
106
        }
1✔
107

108
        private KeyStore getKeyStore() throws KeyStoreException {
109
            if (keystore == null) {
1✔
110
                keystore = builder.build();
1✔
111
            }
112

113
            return keystore;
1✔
114
        }
115

116
        @Override
117
        public Key engineGetKey(String alias, char[] password) throws UnrecoverableKeyException {
118
            if (password != null) {
1✔
119
                builder.keypass(new String(password));
1✔
120
            }
121
            try {
122
                return new JsignJcaPrivateKey((PrivateKey) getKeyStore().getKey(alias, password), builder.provider());
1✔
123
            } catch (UnrecoverableKeyException e) {
×
124
                e.printStackTrace(); // because jarsigner swallows the root cause and hides what's going on
×
125
                throw e;
×
126
            } catch (KeyStoreException | NoSuchAlgorithmException e) {
×
127
                throw new RuntimeException(e);
×
128
            }
129
        }
130

131
        @Override
132
        public Certificate[] engineGetCertificateChain(String alias) {
133
            try {
134
                return getKeyStore().getCertificateChain(alias);
×
135
            } catch (KeyStoreException e) {
×
136
                return null;
×
137
            }
138
        }
139

140
        @Override
141
        public Enumeration<String> engineAliases() {
142
            try {
143
                return getKeyStore().aliases();
1✔
144
            } catch (KeyStoreException e) {
×
145
                throw new RuntimeException(e);
×
146
            }
147
        }
148

149
        @Override
150
        public void engineLoad(InputStream stream, char[] password) {
151
            if (password != null) {
1✔
152
                builder.storepass(new String(password));
1✔
153
            }
154
        }
1✔
155
    }
156

157
    static class JsignJcaPrivateKey implements PrivateKey {
158

159
        private final PrivateKey privateKey;
160
        private final Provider provider;
161

162
        public JsignJcaPrivateKey(PrivateKey key, Provider provider) {
1✔
163
            this.privateKey = key;
1✔
164
            this.provider = provider;
1✔
165
        }
1✔
166

167
        @Override
168
        public String getAlgorithm() {
169
            return privateKey.getAlgorithm();
×
170
        }
171

172
        @Override
173
        public String getFormat() {
174
            return privateKey.getFormat();
×
175
        }
176

177
        @Override
178
        public byte[] getEncoded() {
179
            return privateKey.getEncoded();
×
180
        }
181

182
        public PrivateKey getPrivateKey() {
183
            return privateKey;
1✔
184
        }
185

186
        public Provider getProvider() {
187
            return provider;
1✔
188
        }
189
    }
190

191
    static class JsignJcaSignature extends AbstractSignatureSpi {
192

193
        private Signature signature;
194

195
        public JsignJcaSignature(String signingAlgorithm) {
196
            super(signingAlgorithm);
1✔
197
        }
1✔
198

199
        @Override
200
        protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
201
            JsignJcaPrivateKey key = (JsignJcaPrivateKey) privateKey;
1✔
202

203
            try {
204
                Provider provider = key.getProvider();
1✔
205
                if (provider == null) {
1✔
NEW
206
                    signature = Signature.getInstance(signingAlgorithm);
×
207
                } else {
208
                    signature = Signature.getInstance(signingAlgorithm, provider);
1✔
209
                }
210
            } catch (NoSuchAlgorithmException e) {
×
211
                throw new RuntimeException(e);
×
212
            }
1✔
213
            signature.initSign(key.getPrivateKey());
1✔
214
        }
1✔
215

216
        @Override
217
        protected void engineUpdate(byte b) throws SignatureException {
218
            signature.update(b);
×
219
        }
×
220

221
        @Override
222
        protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
223
            signature.update(b, off, len);
1✔
224
        }
1✔
225

226
        @Override
227
        protected byte[] engineSign() throws SignatureException {
228
            return signature.sign();
1✔
229
        }
230
    }
231
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc