Skip to content

Commit ce66f8d

Browse files
committed
Added preliminary PGP support
1 parent c7cfdca commit ce66f8d

File tree

4 files changed

+237
-11
lines changed

4 files changed

+237
-11
lines changed

jpos/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ dependencies {
1717

1818
// compile 'org.apache.felix:org.apache.felix.framework:4.2.1'
1919
compile( "org.osgi:org.osgi.core:4.3.1" )
20+
compile( 'org.bouncycastle:bcprov-jdk15on:1.50')
21+
compile( 'org.bouncycastle:bcpg-jdk15on:1.50')
2022
testCompile "org.hamcrest:hamcrest-core:1.3"
2123
testCompile "org.hamcrest:hamcrest-library:1.3"
2224
testCompile("org.mockito:mockito-all:1.9.0") {

jpos/src/main/java/org/jpos/q2/Q2.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@
2727
import org.jdom.output.XMLOutputter;
2828
import org.jpos.iso.ISOException;
2929
import org.jpos.iso.ISOUtil;
30-
import org.jpos.util.Log;
31-
import org.jpos.util.LogEvent;
32-
import org.jpos.util.Logger;
33-
import org.jpos.util.SimpleLogListener;
30+
import org.jpos.util.*;
3431
import org.osgi.framework.Bundle;
3532
import org.osgi.framework.BundleContext;
3633
import org.osgi.framework.BundleException;
@@ -65,6 +62,7 @@ public class Q2 implements FileFilter, Runnable {
6562
public static final String ERROR_EXTENSION = "BAD";
6663
public static final String ENV_EXTENSION = "ENV";
6764
public static final String LICENSEE = "/LICENSEE.asc";
65+
public static final byte[] PUBKEYHASH = ISOUtil.hex2byte("C0C73A47A5A27992267AC825F3C8B0666DF3F8A544210851821BFCC1CFA9136C");
6866

6967
public static final String PROTECTED_QBEAN = "protected-qbean";
7068
public static final int SCAN_INTERVAL = 2500;
@@ -570,13 +568,15 @@ public UUID getInstanceId() {
570568
}
571569
public static String getVersionString() {
572570
String appVersionString = getAppVersionString();
571+
int l = PGPHelper.checkLicense();
572+
String sl = l > 0 ? " " + Integer.toString(l,16) : "";
573573
if (appVersionString != null) {
574-
return String.format ("jPOS %s %s/%s (%s)%n%s%s",
575-
getVersion(), getBranch(), getRevision(), getBuildTimestamp(), appVersionString, getLicensee()
574+
return String.format ("jPOS %s %s/%s%s (%s)%n%s%s",
575+
getVersion(), getBranch(), getRevision(), sl, getBuildTimestamp(), appVersionString, getLicensee()
576576
);
577577
}
578-
return String.format ("jPOS %s %s/%s (%s)%s",
579-
getVersion(), getBranch(), getRevision(), getBuildTimestamp(), getLicensee()
578+
return String.format ("jPOS %s %s/%s%s (%s) %s",
579+
getVersion(), getBranch(), getRevision(), sl, getBuildTimestamp(), getLicensee()
580580
);
581581
}
582582
public static String getLicensee() {
@@ -590,12 +590,12 @@ public static String getLicensee() {
590590
try {
591591
while (br.ready())
592592
p.println (br.readLine());
593-
} catch (Exception e) {
594-
e.printStackTrace(p);
595-
}
593+
} catch (Exception ignored) { }
596594
}
597595
return baos.toString();
598596
}
597+
598+
599599
private void parseCmdLine (String[] args) {
600600
CommandLineParser parser = new PosixParser ();
601601

@@ -927,5 +927,6 @@ public boolean isQPersist () {
927927
return obj instanceof QPersist;
928928
}
929929
}
930+
930931
}
931932

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
* jPOS Project [http://jpos.org]
3+
* Copyright (C) 2000-2014 Alejandro P. Revilla
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as
7+
* published by the Free Software Foundation, either version 3 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
19+
package org.jpos.util;
20+
21+
import java.io.*;
22+
import java.security.*;
23+
import java.util.*;
24+
import java.util.regex.Matcher;
25+
import java.util.regex.Pattern;
26+
27+
import org.bouncycastle.bcpg.ArmoredInputStream;
28+
import org.bouncycastle.jce.provider.BouncyCastleProvider;
29+
import org.bouncycastle.openpgp.*;
30+
import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
31+
import org.jpos.iso.ISOUtil;
32+
import org.jpos.q2.Q2;
33+
34+
import javax.crypto.Mac;
35+
import javax.crypto.spec.SecretKeySpec;
36+
37+
public class PGPHelper {
38+
static {
39+
Security.addProvider(new BouncyCastleProvider());
40+
PGPUtil.setDefaultProvider("BC");
41+
}
42+
43+
public static boolean verifySignature (InputStream in, PGPPublicKey pk) throws IOException, NoSuchProviderException, PGPException, SignatureException {
44+
boolean verify = false;
45+
boolean newl = false;
46+
int ch;
47+
ArmoredInputStream ain = new ArmoredInputStream(in, true);
48+
ByteArrayOutputStream out = new ByteArrayOutputStream();
49+
50+
while ((ch = ain.read()) >= 0 && ain.isClearText()) {
51+
if (newl) {
52+
out.write((byte) '\n');
53+
newl = false;
54+
}
55+
if (ch == '\n') {
56+
newl = true;
57+
continue;
58+
}
59+
out.write((byte) ch);
60+
}
61+
PGPObjectFactory pgpf = new PGPObjectFactory(ain);
62+
Object o = pgpf.nextObject();
63+
if (o instanceof PGPSignatureList) {
64+
PGPSignatureList list = (PGPSignatureList)o;
65+
if (list.size() > 0) {
66+
PGPSignature sig = list.get(0);
67+
sig.init (new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), pk);
68+
while ((ch = ain.read()) >= 0 && ain.isClearText()) {
69+
if (newl) {
70+
out.write((byte) '\n');
71+
newl = false;
72+
}
73+
if (ch == '\n') {
74+
newl = true;
75+
continue;
76+
}
77+
out.write((byte) ch);
78+
}
79+
sig.update(out.toByteArray());
80+
verify = sig.verify();
81+
}
82+
}
83+
return verify;
84+
}
85+
86+
public static PGPPublicKey readPublicKey(InputStream in, String id)
87+
throws IOException, PGPException
88+
{
89+
in = PGPUtil.getDecoderStream(in);
90+
id = id.toLowerCase();
91+
92+
PGPPublicKeyRingCollection pubRings = new PGPPublicKeyRingCollection(in);
93+
Iterator rIt = pubRings.getKeyRings();
94+
while (rIt.hasNext()) {
95+
PGPPublicKeyRing pgpPub = (PGPPublicKeyRing) rIt.next();
96+
try {
97+
pgpPub.getPublicKey();
98+
}
99+
catch (Exception ignored) {
100+
continue;
101+
}
102+
Iterator kIt = pgpPub.getPublicKeys();
103+
boolean isId = false;
104+
while (kIt.hasNext()) {
105+
PGPPublicKey pgpKey = (PGPPublicKey) kIt.next();
106+
107+
Iterator iter = pgpKey.getUserIDs();
108+
while (iter.hasNext()) {
109+
String uid = (String) iter.next();
110+
if (uid.toLowerCase().contains(id)) {
111+
isId = true;
112+
break;
113+
}
114+
}
115+
if (pgpKey.isEncryptionKey() && isId) {
116+
return pgpKey;
117+
}
118+
}
119+
}
120+
throw new IllegalArgumentException("Can't find encryption key in key ring.");
121+
}
122+
public static boolean checkSignature() {
123+
boolean ok = false;
124+
try {
125+
InputStream is = Q2.class.getResourceAsStream(Q2.LICENSEE);
126+
InputStream ks = Thread.currentThread().getContextClassLoader().getResourceAsStream("META-INF/.pgp/pubring.asc");
127+
PGPPublicKey pk = PGPHelper.readPublicKey(ks, "license@jpos.org");
128+
ok = verifySignature(is, pk);
129+
} catch (Exception ignored) { }
130+
return ok;
131+
}
132+
133+
public static int checkLicense() {
134+
int rc = 0x80000;
135+
boolean newl = false;
136+
int ch;
137+
138+
try {
139+
InputStream in = Q2.class.getResourceAsStream(Q2.LICENSEE);
140+
InputStream ks = Thread.currentThread().getContextClassLoader().getResourceAsStream("META-INF/.pgp/pubring.asc");
141+
PGPPublicKey pk = readPublicKey(ks, "license@jpos.org");
142+
ArmoredInputStream ain = new ArmoredInputStream(in, true);
143+
ByteArrayOutputStream out = new ByteArrayOutputStream();
144+
Mac mac = Mac.getInstance("HmacSHA256");
145+
mac.init(new SecretKeySpec(pk.getFingerprint(), "HmacSHA256"));
146+
147+
while ((ch = ain.read()) >= 0 && ain.isClearText()) {
148+
if (newl) {
149+
out.write((byte) '\n');
150+
newl = false;
151+
}
152+
if (ch == '\n') {
153+
newl = true;
154+
continue;
155+
}
156+
out.write((byte) ch);
157+
}
158+
PGPObjectFactory pgpf = new PGPObjectFactory(ain);
159+
Object o = pgpf.nextObject();
160+
if (o instanceof PGPSignatureList) {
161+
PGPSignatureList list = (PGPSignatureList) o;
162+
if (list.size() > 0) {
163+
PGPSignature sig = list.get(0);
164+
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), pk);
165+
while ((ch = ain.read()) >= 0 && ain.isClearText()) {
166+
if (newl) {
167+
out.write((byte) '\n');
168+
newl = false;
169+
}
170+
if (ch == '\n') {
171+
newl = true;
172+
continue;
173+
}
174+
out.write((byte) ch);
175+
}
176+
sig.update(out.toByteArray());
177+
if (sig.verify()) {
178+
rc &= 0x7FFFF;
179+
ByteArrayInputStream bais = new ByteArrayInputStream(out.toByteArray());
180+
BufferedReader reader = new BufferedReader(new InputStreamReader(bais));
181+
String s;
182+
Pattern p1 = Pattern.compile("\\s(valid through:)\\s(\\d\\d\\d\\d-\\d\\d-\\d\\d)?", Pattern.CASE_INSENSITIVE);
183+
Pattern p2 = Pattern.compile("\\s(instances:)\\s([\\d]{0,4})?", Pattern.CASE_INSENSITIVE);
184+
while ((s = reader.readLine()) != null) {
185+
Matcher matcher = p1.matcher(s);
186+
if (matcher.find() && matcher.groupCount() == 2) {
187+
String lDate = matcher.group(2);
188+
if (lDate.compareTo(Q2.getBuildTimestamp().substring(0, 10)) < 0) {
189+
rc |= 0x40000;
190+
}
191+
}
192+
matcher = p2.matcher(s);
193+
if (matcher.find() && matcher.groupCount() == 2) {
194+
rc |= Integer.parseInt(matcher.group(2));
195+
}
196+
}
197+
}
198+
}
199+
if (!Arrays.equals(Q2.PUBKEYHASH, mac.doFinal(pk.getEncoded())))
200+
rc |= 0x20000;
201+
}
202+
} catch (Exception ignored) { }
203+
return rc;
204+
}
205+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-----BEGIN PGP PUBLIC KEY BLOCK-----
2+
Version: SKS 1.1.4
3+
Comment: Hostname: pgp.mit.edu
4+
5+
mQENBEyiKxYBCAC/IMoiIBPLWpXrsiILBGNRJq6bhxMK1pQKWlYC6m3xFtHlBHPJpzIG3DQg
6+
pvtE+rVADomfVzvp2D1PA8qGJpbgrkqeQm6EeTsxZk6xP6a2ItcV42hk/CwY4wUBinwq8knY
7+
7slazSE9zjJFQIar9C9Vk97th9Lx0Ii9E6Rs13csKF+d+arV9fguIoDlTDulB/M9QEyVw0/7
8+
viLDi+f029gRSeUiJa486RmlB4CqKdB6/2k+lR7HuTyfsa/3qbLywCg3dTpJ1recam6yg6FC
9+
W7s4SKbgd7XF2Z++covHOpcQm95NrlMwQAO6Q0wWGyx2BFt1mA+Fl8J325jNwBvyKmNHABEB
10+
AAG0H2pQT1MgTGljZW5zZSA8bGljZW5zZUBqcG9zLm9yZz6JATYEEwECACAFAkyiKxYCGwMG
11+
CwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRDkMnju9Z2LRQg9B/oCSPkxq90HfDDjarW/+Znr
12+
WOD2h8d4eqQq0AMJPkS5+9Uz0ErhaAVKCZOROLvMhxY33jV1ADbWiBjQ+hudab6Sj22Cdyz7
13+
lJELIKFHCdiJxKHvkybDQJHkGGUXb9tcFf5fX5EyYn5VpsQgoSMU6KB7qUsvGpLfBAKExDGW
14+
3uENb+8fWELLYYBQRS9FVAOihVSo5NGJ/nwZaDgoDOQxphdEIXUbIdHTkviy6jMYyIOa2jEg
15+
LFGfr0B3hXA45PRCiZo+0xd95S5dWhKyatIYIYvJNgStOqfIpxSIGN0T2dWvB4xZUrigsa+K
16+
p2xzce8oU1H5oX3B2E7KcbDrdMe2SepK
17+
=XKpH
18+
-----END PGP PUBLIC KEY BLOCK-----

0 commit comments

Comments
 (0)