Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
O
Openfire
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Openfire
Commits
57b37b11
Commit
57b37b11
authored
May 28, 2015
by
Victor Hong
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added the ability to use user-defined classes to map identities from
certificates.
parent
bc19c579
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
245 additions
and
101 deletions
+245
-101
CertificateManager.java
src/java/org/jivesoftware/util/CertificateManager.java
+38
-101
CNCertificateIdentityMapping.java
.../jivesoftware/util/cert/CNCertificateIdentityMapping.java
+49
-0
CertificateIdentityMapping.java
...rg/jivesoftware/util/cert/CertificateIdentityMapping.java
+29
-0
SANCertificateIdentityMapping.java
...jivesoftware/util/cert/SANCertificateIdentityMapping.java
+129
-0
No files found.
src/java/org/jivesoftware/util/CertificateManager.java
View file @
57b37b11
...
...
@@ -49,7 +49,6 @@ import java.security.cert.CertificateFactory;
import
java.security.cert.CertificateParsingException
;
import
java.security.cert.CollectionCertStoreParameters
;
import
java.security.cert.PKIXBuilderParameters
;
import
java.security.cert.TrustAnchor
;
import
java.security.cert.X509CertSelector
;
import
java.security.cert.X509Certificate
;
import
java.util.ArrayList
;
...
...
@@ -58,11 +57,11 @@ import java.util.Collections;
import
java.util.Date
;
import
java.util.Enumeration
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.Hashtable
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.StringTokenizer
;
import
java.util.concurrent.CopyOnWriteArrayList
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
...
...
@@ -70,7 +69,6 @@ import java.util.regex.Pattern;
import
org.bouncycastle.asn1.ASN1Encodable
;
import
org.bouncycastle.asn1.ASN1InputStream
;
import
org.bouncycastle.asn1.ASN1TaggedObject
;
import
org.bouncycastle.asn1.ASN1Encodable
;
import
org.bouncycastle.asn1.DERSequence
;
import
org.bouncycastle.asn1.ASN1ObjectIdentifier
;
import
org.bouncycastle.asn1.DERObjectIdentifier
;
...
...
@@ -87,11 +85,13 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
import
org.bouncycastle.openssl.PEMParser
;
import
org.bouncycastle.openssl.PEMDecryptorProvider
;
import
org.bouncycastle.openssl.PEMEncryptedKeyPair
;
import
org.bouncycastle.openssl.PasswordFinder
;
import
org.bouncycastle.openssl.PEMKeyPair
;
import
org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
;
import
org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder
;
import
org.bouncycastle.x509.X509V3CertificateGenerator
;
import
org.jivesoftware.util.cert.CertificateIdentityMapping
;
import
org.jivesoftware.util.cert.CNCertificateIdentityMapping
;
import
org.jivesoftware.util.cert.SANCertificateIdentityMapping
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
...
...
@@ -105,9 +105,6 @@ public class CertificateManager {
private
static
final
Logger
Log
=
LoggerFactory
.
getLogger
(
CertificateManager
.
class
);
private
static
final
String
OTHERNAME_XMPP_OID
=
"1.3.6.1.5.5.7.8.5"
;
private
static
Pattern
cnPattern
=
Pattern
.
compile
(
"(?i)(cn=)([^,]*)"
);
private
static
Pattern
valuesPattern
=
Pattern
.
compile
(
"(?i)(=)([^,]*)"
);
private
static
Provider
provider
=
new
BouncyCastleProvider
();
...
...
@@ -119,9 +116,35 @@ public class CertificateManager {
private
static
List
<
CertificateEventListener
>
listeners
=
new
CopyOnWriteArrayList
<
CertificateEventListener
>();
private
static
List
<
CertificateIdentityMapping
>
certIdentityMapping
=
new
ArrayList
<
CertificateIdentityMapping
>();
static
{
// Add the BC provider to the list of security providers
Security
.
addProvider
(
provider
);
String
classList
=
JiveGlobals
.
getProperty
(
"provider.certIdentityMapping.classList"
);
if
(
classList
!=
null
)
{
StringTokenizer
st
=
new
StringTokenizer
(
classList
,
" ,\t\n\r\f"
);
while
(
st
.
hasMoreTokens
())
{
String
s_provider
=
st
.
nextToken
();
try
{
Class
c_provider
=
ClassUtils
.
forName
(
s_provider
);
CertificateIdentityMapping
provider
=
(
CertificateIdentityMapping
)(
c_provider
.
newInstance
());
Log
.
debug
(
"CertificateManager: Loaded "
+
s_provider
);
certIdentityMapping
.
add
(
provider
);
}
catch
(
Exception
e
)
{
Log
.
error
(
"CertificateManager: Error loading CertificateIdentityMapping: "
+
s_provider
+
"\n"
+
e
);
}
}
}
if
(
certIdentityMapping
.
isEmpty
())
{
Log
.
debug
(
"CertificateManager: No CertificateIdentityMapping's found. Loading default mappings"
);
certIdentityMapping
.
add
(
new
SANCertificateIdentityMapping
());
certIdentityMapping
.
add
(
new
CNCertificateIdentityMapping
());
}
}
/**
...
...
@@ -346,101 +369,15 @@ public class CertificateManager {
* @return the identities of the remote server as defined in the specified certificate.
*/
public
static
List
<
String
>
getPeerIdentities
(
X509Certificate
x509Certificate
)
{
// Look the identity in the subjectAltName extension if available
List
<
String
>
names
=
getSubjectAlternativeNames
(
x509Certificate
);
if
(
names
.
isEmpty
())
{
String
name
=
x509Certificate
.
getSubjectDN
().
getName
();
Matcher
matcher
=
cnPattern
.
matcher
(
name
);
// Create an array with the detected identities
names
=
new
ArrayList
<
String
>();
while
(
matcher
.
find
())
{
names
.
add
(
matcher
.
group
(
2
));
}
}
return
names
;
}
/**
* Returns the JID representation of an XMPP entity contained as a SubjectAltName extension
* in the certificate. If none was found then return <tt>null</tt>.
*
* @param certificate the certificate presented by the remote entity.
* @return the JID representation of an XMPP entity contained as a SubjectAltName extension
* in the certificate. If none was found then return <tt>null</tt>.
*/
private
static
List
<
String
>
getSubjectAlternativeNames
(
X509Certificate
certificate
)
{
List
<
String
>
identities
=
new
ArrayList
<
String
>();
try
{
Collection
<
List
<?>>
altNames
=
certificate
.
getSubjectAlternativeNames
();
// Check that the certificate includes the SubjectAltName extension
if
(
altNames
==
null
)
{
return
Collections
.
emptyList
();
}
// Use the type OtherName to search for the certified server name
for
(
List
<?>
item
:
altNames
)
{
Integer
type
=
(
Integer
)
item
.
get
(
0
);
if
(
type
==
0
)
{
// Type OtherName found so return the associated value
try
{
// Value is encoded using ASN.1 so decode it to get the server's identity
ASN1InputStream
decoder
=
new
ASN1InputStream
((
byte
[])
item
.
get
(
1
));
Object
object
=
decoder
.
readObject
();
ASN1Sequence
otherNameSeq
=
null
;
if
(
object
!=
null
&&
object
instanceof
ASN1Sequence
)
{
otherNameSeq
=
(
ASN1Sequence
)
object
;
}
else
{
continue
;
}
// Check the object identifier
ASN1ObjectIdentifier
objectId
=
(
ASN1ObjectIdentifier
)
otherNameSeq
.
getObjectAt
(
0
);
Log
.
debug
(
"Parsing otherName for subject alternative names: "
+
objectId
.
toString
()
);
if
(
!
OTHERNAME_XMPP_OID
.
equals
(
objectId
.
getId
()))
{
// Not a XMPP otherName
Log
.
debug
(
"Ignoring non-XMPP otherName, "
+
objectId
.
getId
());
continue
;
}
List
<
String
>
names
=
new
ArrayList
<
String
>();
for
(
CertificateIdentityMapping
mapping
:
certIdentityMapping
)
{
List
<
String
>
identities
=
mapping
.
mapIdentity
(
x509Certificate
);
Log
.
debug
(
"CertificateManager: "
+
mapping
.
name
()
+
" returned "
+
identities
.
toString
());
names
.
addAll
(
identities
);
}
// Get identity string
try
{
final
String
identity
;
ASN1Encodable
o
=
otherNameSeq
.
getObjectAt
(
1
);
if
(
o
instanceof
DERTaggedObject
)
{
ASN1TaggedObject
ato
=
DERTaggedObject
.
getInstance
(
o
);
Log
.
debug
(
"... processing DERTaggedObject: "
+
ato
.
toString
());
// TODO: there's bound to be a better way...
identity
=
ato
.
toString
().
substring
(
ato
.
toString
().
lastIndexOf
(
']'
)+
1
).
trim
();
}
else
{
DERUTF8String
derStr
=
DERUTF8String
.
getInstance
(
o
);
identity
=
derStr
.
getString
();
}
if
(
identity
!=
null
&&
identity
.
length
()
>
0
)
{
// Add the decoded server name to the list of identities
identities
.
add
(
identity
);
}
decoder
.
close
();
}
catch
(
IllegalArgumentException
ex
)
{
// OF-517: othername formats are extensible. If we don't recognize the format, skip it.
Log
.
debug
(
"Cannot parse altName, likely because of unknown record format."
,
ex
);
}
}
catch
(
UnsupportedEncodingException
e
)
{
// Ignore
}
catch
(
IOException
e
)
{
// Ignore
}
catch
(
Exception
e
)
{
Log
.
error
(
"Error decoding subjectAltName"
,
e
);
}
}
// Other types are not applicable for XMPP, so silently ignore them
}
}
catch
(
CertificateParsingException
e
)
{
Log
.
error
(
"Error parsing SubjectAltName in certificate: "
+
certificate
.
getSubjectDN
(),
e
);
}
return
identities
;
return
names
;
}
/**
...
...
src/java/org/jivesoftware/util/cert/CNCertificateIdentityMapping.java
0 → 100644
View file @
57b37b11
package
org
.
jivesoftware
.
util
.
cert
;
import
java.security.cert.X509Certificate
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* Certificate identity mapping that uses the CommonName as the
* identity credentials
*
* @author Victor Hong
*
*/
public
class
CNCertificateIdentityMapping
implements
CertificateIdentityMapping
{
private
static
Pattern
cnPattern
=
Pattern
.
compile
(
"(?i)(cn=)([^,]*)"
);
/**
* Maps certificate CommonName as identity credentials
*
* @param certificate
* @return
*/
@Override
public
List
<
String
>
mapIdentity
(
X509Certificate
certificate
)
{
String
name
=
certificate
.
getSubjectDN
().
getName
();
Matcher
matcher
=
cnPattern
.
matcher
(
name
);
// Create an array with the detected identities
List
<
String
>
names
=
new
ArrayList
<
String
>();
while
(
matcher
.
find
())
{
names
.
add
(
matcher
.
group
(
2
));
}
return
names
;
}
/**
* Returns the short name of mapping
*
* @return The short name of the mapping
*/
@Override
public
String
name
()
{
return
"Common Name Mapping"
;
}
}
src/java/org/jivesoftware/util/cert/CertificateIdentityMapping.java
0 → 100644
View file @
57b37b11
package
org
.
jivesoftware
.
util
.
cert
;
import
java.security.cert.X509Certificate
;
import
java.util.List
;
/**
* This is the interface used to map identity credentials from certificates.
* Users may implement this class to map authentication credentials (i.e. usernames)
* from certificate data (e.g. CommonName or SubjectAlternativeName)
*
* @author Victor Hong
*
*/
public
interface
CertificateIdentityMapping
{
/**
* Maps identities from X509Certificates
*
* @param certificate The certificate from which to map identities
* @return A list of identities mapped from the certificate
*/
List
<
String
>
mapIdentity
(
X509Certificate
certificate
);
/**
* Returns the short name of the mapping
*
* @return The short name of the mapping
*/
String
name
();
}
src/java/org/jivesoftware/util/cert/SANCertificateIdentityMapping.java
0 → 100644
View file @
57b37b11
package
org
.
jivesoftware
.
util
.
cert
;
import
java.io.IOException
;
import
java.io.UnsupportedEncodingException
;
import
java.security.cert.CertificateParsingException
;
import
java.security.cert.X509Certificate
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.List
;
import
org.bouncycastle.asn1.ASN1Encodable
;
import
org.bouncycastle.asn1.ASN1InputStream
;
import
org.bouncycastle.asn1.ASN1ObjectIdentifier
;
import
org.bouncycastle.asn1.ASN1Sequence
;
import
org.bouncycastle.asn1.ASN1TaggedObject
;
import
org.bouncycastle.asn1.DERTaggedObject
;
import
org.bouncycastle.asn1.DERUTF8String
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
/**
* Certificate identity mapping that uses XMPP-OtherName SubjectAlternativeName
* as the identity credentials
*
* @author Victor Hong
*
*/
public
class
SANCertificateIdentityMapping
implements
CertificateIdentityMapping
{
private
static
final
Logger
Log
=
LoggerFactory
.
getLogger
(
SANCertificateIdentityMapping
.
class
);
private
static
final
String
OTHERNAME_XMPP_OID
=
"1.3.6.1.5.5.7.8.5"
;
/**
* Returns the JID representation of an XMPP entity contained as a SubjectAltName extension
* in the certificate. If none was found then return an empty list.
*
* @param certificate the certificate presented by the remote entity.
* @return the JID representation of an XMPP entity contained as a SubjectAltName extension
* in the certificate. If none was found then return an empty list.
*/
@Override
public
List
<
String
>
mapIdentity
(
X509Certificate
certificate
)
{
List
<
String
>
identities
=
new
ArrayList
<
String
>();
try
{
Collection
<
List
<?>>
altNames
=
certificate
.
getSubjectAlternativeNames
();
// Check that the certificate includes the SubjectAltName extension
if
(
altNames
==
null
)
{
return
Collections
.
emptyList
();
}
// Use the type OtherName to search for the certified server name
for
(
List
<?>
item
:
altNames
)
{
Integer
type
=
(
Integer
)
item
.
get
(
0
);
if
(
type
==
0
)
{
// Type OtherName found so return the associated value
try
{
// Value is encoded using ASN.1 so decode it to get the server's identity
ASN1InputStream
decoder
=
new
ASN1InputStream
((
byte
[])
item
.
get
(
1
));
Object
object
=
decoder
.
readObject
();
ASN1Sequence
otherNameSeq
=
null
;
if
(
object
!=
null
&&
object
instanceof
ASN1Sequence
)
{
otherNameSeq
=
(
ASN1Sequence
)
object
;
}
else
{
continue
;
}
// Check the object identifier
ASN1ObjectIdentifier
objectId
=
(
ASN1ObjectIdentifier
)
otherNameSeq
.
getObjectAt
(
0
);
Log
.
debug
(
"Parsing otherName for subject alternative names: "
+
objectId
.
toString
()
);
if
(
!
OTHERNAME_XMPP_OID
.
equals
(
objectId
.
getId
()))
{
// Not a XMPP otherName
Log
.
debug
(
"Ignoring non-XMPP otherName, "
+
objectId
.
getId
());
continue
;
}
// Get identity string
try
{
final
String
identity
;
ASN1Encodable
o
=
otherNameSeq
.
getObjectAt
(
1
);
if
(
o
instanceof
DERTaggedObject
)
{
ASN1TaggedObject
ato
=
DERTaggedObject
.
getInstance
(
o
);
Log
.
debug
(
"... processing DERTaggedObject: "
+
ato
.
toString
());
// TODO: there's bound to be a better way...
identity
=
ato
.
toString
().
substring
(
ato
.
toString
().
lastIndexOf
(
']'
)+
1
).
trim
();
}
else
{
DERUTF8String
derStr
=
DERUTF8String
.
getInstance
(
o
);
identity
=
derStr
.
getString
();
}
if
(
identity
!=
null
&&
identity
.
length
()
>
0
)
{
// Add the decoded server name to the list of identities
identities
.
add
(
identity
);
}
decoder
.
close
();
}
catch
(
IllegalArgumentException
ex
)
{
// OF-517: othername formats are extensible. If we don't recognize the format, skip it.
Log
.
debug
(
"Cannot parse altName, likely because of unknown record format."
,
ex
);
}
}
catch
(
UnsupportedEncodingException
e
)
{
// Ignore
}
catch
(
IOException
e
)
{
// Ignore
}
catch
(
Exception
e
)
{
Log
.
error
(
"Error decoding subjectAltName"
,
e
);
}
}
// Other types are not applicable for XMPP, so silently ignore them
}
}
catch
(
CertificateParsingException
e
)
{
Log
.
error
(
"Error parsing SubjectAltName in certificate: "
+
certificate
.
getSubjectDN
(),
e
);
}
return
identities
;
}
/**
* Returns the short name of mapping
*
* @return The short name of the mapping
*/
@Override
public
String
name
()
{
return
"Subject Alternative Name Mapping"
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment